diff --git a/neutrino-core/pom.xml b/neutrino-core/pom.xml index d903271c..52f916d6 100644 --- a/neutrino-core/pom.xml +++ b/neutrino-core/pom.xml @@ -33,6 +33,11 @@ 8.0.29 test + + org.apache.velocity + velocity-engine-core + 2.2 + diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/interceptor/InterceptorFactory.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/interceptor/InterceptorFactory.java index c537a56f..abd14493 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/interceptor/InterceptorFactory.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/interceptor/InterceptorFactory.java @@ -60,11 +60,13 @@ public class InterceptorFactory { clazz, () -> { try { - if (BeanManager.getBean(clazz) != null) { - cache.set(clazz, BeanManager.getBean(clazz)); - } else { - cache.set(clazz, clazz.newInstance()); - } + // TODO + cache.set(clazz, clazz.newInstance()); +// if (BeanManager.getBean(clazz) != null) { +// cache.set(clazz, BeanManager.getBean(clazz)); +// } else { +// cache.set(clazz, clazz.newInstance()); +// } } catch (InstantiationException|IllegalAccessException e) { throw new RuntimeException(e); } diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/AsgcProxyFactory.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/AsgcProxyFactory.java index 74b14dbf..d2651ba2 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/AsgcProxyFactory.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/AsgcProxyFactory.java @@ -21,18 +21,10 @@ */ package fun.asgc.neutrino.core.aop.proxy; -import fun.asgc.neutrino.core.aop.Invocation; import fun.asgc.neutrino.core.util.*; import lombok.extern.slf4j.Slf4j; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -74,7 +66,7 @@ public class AsgcProxyFactory implements ProxyFactory { private T doGet(Class clazz) throws ReflectiveOperationException { ProxyClass proxyClass = new ProxyClass(clazz); proxyClass.setName(generateClassName(clazz)); - String sourceCode = generateProxyClassSourceCode(clazz, proxyClass.getName()); + String sourceCode = AsgcProxyGenerator.getInstance().generator(proxyClass.getName(), clazz); // generateProxyClassSourceCode(clazz, proxyClass.getName()); proxyClass.setSourceCode(sourceCode); log.debug("类:{} 的代理类源码:\n{}", clazz.getName(), sourceCode); @@ -87,161 +79,4 @@ public class AsgcProxyFactory implements ProxyFactory { private String generateClassName(Class clazz) { return String.format(classNameTemplate, clazz.getSimpleName(), proxyClassCounter.incrementAndGet()); } - - /** - * 生成代理类源代码 - 继承方式 - * 1、类不能有final修饰符 - * 2、被代理方法不能有final修饰符 - * @param clazz - * @return - */ - private String generateProxyClassSourceCode(Class clazz, String proxyClassName) { - if (ClassUtil.isInterface(clazz)) { - return generateProxyClassSourceCodeForInterface(clazz, proxyClassName); - } - Set> importClasses = new HashSet<>(); - StringBuilder header = new StringBuilder(); - header.append("package " + clazz.getPackage().getName() + ";").append("\n"); - appendImport(header, Invocation.class, importClasses); - appendImport(header, ProxyCache.class, importClasses); - - StringBuilder body = new StringBuilder(); - body.append("public class ").append(proxyClassName).append(" extends ").append(clazz.getSimpleName()).append("{\n"); - Method[] methods = clazz.getMethods(); - if (ArrayUtil.notEmpty(methods)) { - for (Method method : methods) { - if (Modifier.isFinal(method.getModifiers()) || Modifier.isStatic(method.getModifiers())) { - continue; - } - Long methodId = ProxyCache.setMethod(method); - Class returnType = method.getReturnType(); - Set> exceptionTypes = ReflectUtil.getExceptionTypes(method); - boolean isVoid = returnType == void.class; - boolean isThrow = CollectionUtil.notEmpty(exceptionTypes); - String parametersString = buildParametersString(method.getParameters()); - String parameterNamesString = buildParameterNamesString(method.getParameters()); - String throwString = isThrow ? "throws " + buildTypeNameString(exceptionTypes) : ""; - if (isThrow) { - for (Class c : exceptionTypes) { - appendImport(header, c, importClasses); - } - } - - body.append("\t").append("public").append(" ").append(returnType.getName()).append(" ").append(method.getName()).append("(").append(parametersString).append(") ").append(throwString).append(" {").append("\n") - .append("\t\tInvocation inv = new Invocation(").append(methodId + "L,").append("this,").append("() -> {").append("\n") - .append("\t\t\t").append(isVoid ? "" : "return ").append("super.").append(method.getName()).append("(").append(parameterNamesString).append(");").append("\n") - .append(isVoid ? "\t\t\treturn null;\n" : "") - .append("\t\t").append("}").append(StringUtil.isEmpty(parameterNamesString) ? "" : "," + parameterNamesString).append(");").append("\n") - .append("\t\t").append("try {\n") - .append("\t\t\t").append("inv.invoke();").append("\n") - .append("\t\t").append("} catch (Exception e) {\n"); - - if (isThrow) { - body.append("\t\t\t").append("if (ProxyCache.checkMethodThrow(" + methodId + "L,e)) {").append("\n") - .append("\t\t\t\t").append("throw e;").append("\n") - .append("\t\t\t").append("} else {").append("\n") - .append("\t\t\t\t").append("e.printStackTrace();").append("\n") - .append("\t\t\t").append("}").append("\n"); - } else { - body.append("\t\t\t").append("e.printStackTrace();").append("\n"); - } - body.append("\t\t").append("}\n") - .append(isVoid ? "" : "\t\treturn inv.getReturnValue();\n") - .append("\t").append("}").append("\n"); - } - } - body.append("}").append("\n"); - return header.toString().concat(body.toString()); - } - - private String generateProxyClassSourceCodeForInterface(Class clazz, String proxyClassName) { - Set> importClasses = new HashSet<>(); - StringBuilder header = new StringBuilder(); - header.append("package " + clazz.getPackage().getName() + ";").append("\n"); - appendImport(header, Invocation.class, importClasses); - appendImport(header, ProxyCache.class, importClasses); - - StringBuilder body = new StringBuilder(); - body.append("public class ").append(proxyClassName).append(" implements ").append(clazz.getSimpleName()).append("{\n"); - Method[] methods = clazz.getMethods(); - if (ArrayUtil.notEmpty(methods)) { - for (Method method : methods) { - if (Modifier.isFinal(method.getModifiers())) { - continue; - } - Long methodId = ProxyCache.setMethod(method); - Class returnType = method.getReturnType(); - Set> exceptionTypes = ReflectUtil.getExceptionTypes(method); - boolean isVoid = returnType == void.class; - boolean isThrow = CollectionUtil.notEmpty(exceptionTypes); - String parametersString = buildParametersString(method.getParameters()); - String parameterNamesString = buildParameterNamesString(method.getParameters()); - String throwString = isThrow ? "throws " + buildTypeNameString(exceptionTypes) : ""; - if (isThrow) { - for (Class c : exceptionTypes) { - appendImport(header, c, importClasses); - } - } - - body.append("\t").append("public").append(" ").append(returnType.getName()).append(" ").append(method.getName()).append("(").append(parametersString).append(") ").append(throwString).append(" {").append("\n") - .append("\t\tInvocation inv = new Invocation(").append(methodId + "L,").append("this,").append("() -> {").append("\n") - .append("\t\t\treturn null;\n") - .append("\t\t").append("}").append(StringUtil.isEmpty(parameterNamesString) ? "" : "," + parameterNamesString).append(");").append("\n") - .append("\t\t").append("try {\n") - .append("\t\t\t").append("inv.invoke();").append("\n") - .append("\t\t").append("} catch (Exception e) {\n"); - - if (isThrow) { - body.append("\t\t\t").append("if (ProxyCache.checkMethodThrow(" + methodId + "L,e)) {").append("\n") - .append("\t\t\t\t").append("throw e;").append("\n") - .append("\t\t\t").append("} else {").append("\n") - .append("\t\t\t\t").append("e.printStackTrace();").append("\n") - .append("\t\t\t").append("}").append("\n"); - } else { - body.append("\t\t\t").append("e.printStackTrace();").append("\n"); - } - body.append("\t\t").append("}\n") - .append(isVoid ? "" : "\t\treturn inv.getReturnValue();\n") - .append("\t").append("}").append("\n"); - - } - } - body.append("}").append("\n"); - return header.toString().concat(body.toString()); - } - - /** - * 追加import语句,java.lang包下不需要import - * @param sb - * @param clazz - * @param importClasses - */ - private synchronized void appendImport(StringBuilder sb, Class clazz, Set> importClasses) { - if (importClasses.contains(clazz) || clazz.getName().startsWith("java.lang.")) { - return; - } - importClasses.add(clazz); - sb.append("import ").append(clazz.getName()).append(";\n"); - } - - private String buildParametersString(Parameter[] parameters) { - if (ArrayUtil.isEmpty(parameters)) { - return ""; - } - return Stream.of(parameters).map(item -> item.getType().getName() + " " + item.getName()).collect(Collectors.joining(",")); - } - - private String buildParameterNamesString(Parameter[] parameters) { - if (ArrayUtil.isEmpty(parameters)) { - return ""; - } - return Stream.of(parameters).map(Parameter::getName).collect(Collectors.joining(",")); - } - - private String buildTypeNameString(Set> classes) { - if (CollectionUtil.isEmpty(classes)) { - return ""; - } - return classes.stream().map(Class::getName).collect(Collectors.joining(",")); - } } diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/AsgcProxyGenerator.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/AsgcProxyGenerator.java new file mode 100644 index 00000000..d2cc4c37 --- /dev/null +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/proxy/AsgcProxyGenerator.java @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package fun.asgc.neutrino.core.aop.proxy; + +import fun.asgc.neutrino.core.aop.Invocation; +import fun.asgc.neutrino.core.util.*; +import lombok.Data; +import lombok.experimental.Accessors; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import sun.management.MethodInfo; + +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * + * @author: aoshiguchen + * @date: 2022/7/6 + */ +@SuppressWarnings("all") +public class AsgcProxyGenerator { + private static final AsgcProxyGenerator instance = new AsgcProxyGenerator(); + private VelocityEngine engine; + private Template template; + + private AsgcProxyGenerator() { + engine = new VelocityEngine(); + engine.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + engine.init(); + template = engine.getTemplate("tpl/AsgcProxy.tpl"); + } + + private String render(Map map) { + VelocityContext context = new VelocityContext(map); + StringWriter stringWriter = new StringWriter(); + template.merge(context, stringWriter); + return stringWriter.toString(); + } + + public String generator(String proxyClassName, Class targetType) { + Map map = new HashMap<>(); + map.put("package", targetType.getPackage().getName()); + List> importList = new ArrayList<>(); + map.put("importList", importList); + appendImport(importList, Invocation.class); + appendImport(importList, ProxyCache.class); + map.put("proxyClassName", proxyClassName); + map.put("targetType", targetType); + map.put("targetIsInterface", ClassUtil.isInterface(targetType)); + + List methodInfoList = new ArrayList<>(); + map.put("methodInfoList", methodInfoList); + + Method[] methods = targetType.getMethods(); + if (ArrayUtil.notEmpty(methods)) { + for (Method method : methods) { + if (Modifier.isFinal(method.getModifiers()) || Modifier.isStatic(method.getModifiers())) { + continue; + } + Long methodId = ProxyCache.setMethod(method); + Class returnType = method.getReturnType(); + boolean isVoid = returnType == void.class; + appendImport(importList, returnType); + if (ArrayUtil.notEmpty(method.getParameters())) { + Stream.of(method.getParameters()).forEach(parameter -> appendImport(importList, parameter.getType())); + } + Set> exceptionTypes = ReflectUtil.getExceptionTypes(method); + boolean isThrow = CollectionUtil.notEmpty(exceptionTypes); + String throwsString = isThrow ? " throws " + buildTypeNameString(exceptionTypes) + " ": " "; + if (CollectionUtil.notEmpty(exceptionTypes)) { + exceptionTypes.forEach(exceptionType -> appendImport(importList, exceptionType)); + } + + String parameterNamesString = buildParameterNamesString(method.getParameters()); + methodInfoList.add(new MethodInfo() + .setMethodId(methodId) + .setReturnType(returnType) + .setVoid(isVoid) + .setMethodName(method.getName()) + .setParametersString(buildParametersString(method.getParameters())) + .setThrowsString(throwsString) + .setThrow(isThrow) + .setParameterNamesString(parameterNamesString) + ); + } + } + return render(map); + } + + public static AsgcProxyGenerator getInstance() { + return instance; + } + + private String buildParametersString(Parameter[] parameters) { + if (ArrayUtil.isEmpty(parameters)) { + return ""; + } + return Stream.of(parameters).map(item -> item.getType().getSimpleName() + " " + item.getName()).collect(Collectors.joining(", ")); + } + + private String buildTypeNameString(Set> classes) { + if (CollectionUtil.isEmpty(classes)) { + return ""; + } + return classes.stream().map(Class::getSimpleName).collect(Collectors.joining(",")); + } + + private String buildParameterNamesString(Parameter[] parameters) { + if (ArrayUtil.isEmpty(parameters)) { + return ""; + } + return Stream.of(parameters).map(Parameter::getName).collect(Collectors.joining(", ")); + } + + /** + * 追加import语句,java.lang包下不需要import + * @param importClasses + * @param clazz + */ + private synchronized void appendImport(List> importClasses, Class clazz) { + if (importClasses.contains(clazz) || clazz.getName().startsWith("java.lang.") || TypeUtil.isNormalBasicType(clazz) || clazz == void.class) { + return; + } + importClasses.add(clazz); + } + + @Accessors(chain = true) + @Data + public static class MethodInfo { + private Long methodId; + private Class returnType; + private boolean isVoid; + private String methodName; + private String parametersString; + private String throwsString; + private boolean isThrow; + private String parameterNamesString; + } +} diff --git a/neutrino-core/src/main/resources/tpl/AsgcProxy.tpl b/neutrino-core/src/main/resources/tpl/AsgcProxy.tpl new file mode 100644 index 00000000..a6811b5c --- /dev/null +++ b/neutrino-core/src/main/resources/tpl/AsgcProxy.tpl @@ -0,0 +1,48 @@ +package ${package}; + +#foreach($item in ${importList}) +import ${item.getName()}; +#end + +#if(${targetIsInterface}) +public class ${proxyClassName} implements ${targetType.getSimpleName()} { +#else +public class ${proxyClassName} extends ${targetType.getSimpleName()} { +#end + +#foreach($methodInfo in ${methodInfoList}) + public ${methodInfo.returnType.getSimpleName()} ${methodInfo.methodName} (${methodInfo.parametersString})${methodInfo.throwsString}{ + Invocation inv = new Invocation(${methodInfo.methodId}L, this, () -> { +#if(!${targetIsInterface}) +#if(${methodInfo.void}) + super.${methodInfo.methodName}(${methodInfo.parameterNamesString}); + return null; +#else + return super.${methodInfo.methodName}(${methodInfo.parameterNamesString}); +#end +#else + return null; +#end + }); + try { + inv.invoke(); + } catch(RuntimeException e) { + throw e; + } catch(Exception e) { +#if(${methodInfo.throw}) + if (ProxyCache.checkMethodThrow(${methodInfo.methodId}L, e)) { + throw e; + } else { + e.printStackTrace(); + } +#else + e.printStackTrace(); +#end + } +#if(!${methodInfo.void}) + return inv.getReturnValue(); +#end + } + +#end +} diff --git a/neutrino-core/src/test/java/fun/asgc/neutrino/core/aop/AsgcProxyGeneratorTest.java b/neutrino-core/src/test/java/fun/asgc/neutrino/core/aop/AsgcProxyGeneratorTest.java new file mode 100644 index 00000000..cebf5f0c --- /dev/null +++ b/neutrino-core/src/test/java/fun/asgc/neutrino/core/aop/AsgcProxyGeneratorTest.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package fun.asgc.neutrino.core.aop; + +import fun.asgc.neutrino.core.aop.proxy.AsgcProxyGenerator; +import org.junit.Test; + +/** + * + * @author: aoshiguchen + * @date: 2022/7/6 + */ +public class AsgcProxyGeneratorTest { + + @Test + public void test1() { + String result = AsgcProxyGenerator.getInstance().generator("A", Panda.class); + System.out.println(result); + } + +} diff --git a/neutrino-core/src/test/java/fun/asgc/neutrino/core/aop/Test3.java b/neutrino-core/src/test/java/fun/asgc/neutrino/core/aop/Test3.java index 9f88b2d5..a036c698 100644 --- a/neutrino-core/src/test/java/fun/asgc/neutrino/core/aop/Test3.java +++ b/neutrino-core/src/test/java/fun/asgc/neutrino/core/aop/Test3.java @@ -21,6 +21,7 @@ */ package fun.asgc.neutrino.core.aop; +import fun.asgc.neutrino.core.aop.proxy.Proxy; import fun.asgc.neutrino.core.util.ReflectUtil; import org.junit.Test; @@ -36,15 +37,16 @@ public class Test3 { @Test public void test1() throws Exception { - Animal animal = Aop.get(Animal.class); -// Animal animal = Proxy.getProxyFactory(ProxyStrategy.ASGC_PROXY).get(Animal.class); +// Animal animal = Aop.get(Animal.class); + Animal animal = Proxy.getProxyFactory(ProxyStrategy.ASGC_PROXY).get(Animal.class); System.out.println(animal); System.out.println(animal.say("aa")); } @Test public void test2() throws Exception { - Mammal mammal = Aop.get(Mammal.class); +// Mammal mammal = Aop.get(Mammal.class); + Mammal mammal = Proxy.getProxyFactory(ProxyStrategy.ASGC_PROXY).get(Mammal.class); mammal.crawl(); System.out.println(ReflectUtil.getInterfaceAll(Mammal.class));