Aop子类代理生成代理类源码改为使用模板引擎生成

This commit is contained in:
aoshiguchen
2022-07-06 15:46:37 +08:00
parent 143bc79d0e
commit 72ce626a27
7 changed files with 271 additions and 174 deletions
+5
View File
@@ -33,6 +33,11 @@
<version>8.0.29</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
</project>
@@ -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);
}
@@ -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> T doGet(Class<T> 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<Class<?>> 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<Class<?>> 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<Class<?>> 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<Class<?>> 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<Class<?>> 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<Class<?>> classes) {
if (CollectionUtil.isEmpty(classes)) {
return "";
}
return classes.stream().map(Class::getName).collect(Collectors.joining(","));
}
}
@@ -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<String, Object> 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<String, Object> map = new HashMap<>();
map.put("package", targetType.getPackage().getName());
List<Class<?>> 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<MethodInfo> 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<Class<?>> 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<Class<?>> 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<Class<?>> 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;
}
}
@@ -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
}
@@ -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);
}
}
@@ -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));