赞
踩
作者:zzzzbw
来源:SegmentFault 思否社区
省流大师:
@Slf4j@Aspect@Componentpublic class TransactionalAop { @Around("@within(org.springframework.transaction.annotation.Transactional)") public Object recordLog(ProceedingJoinPoint p) throws Throwable { log.info("Transaction start!"); Object result; try { result = p.proceed(); } catch (Exception e) { log.info("Transaction rollback!"); throw new Throwable(e); } log.info("Transaction commit!"); return result; }}
然后写测试的类和Test方法, Test方法中通过反射调用HelloServiceImpl的private方法primaryHello().
public interface HelloService { void hello(String name);}@Slf4j@Transactional@Servicepublic class HelloServiceImpl implements HelloService { @Override public void hello(String name) { log.info("hello {}!", name); } private long privateHello(Integer time) { log.info("private hello! time: {}", time); return System.currentTimeMillis(); }}@Slf4j@SpringBootTestpublic class HelloTests { @Autowired private HelloService helloService; @Test public void helloService() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { helloService.hello("hello"); Method privateHello = helloService.getClass().getDeclaredMethod("privateHello", Integer.class); privateHello.setAccessible(true); Object invoke = privateHello.invoke(helloService, 10); log.info("privateHello result: {}", invoke); }}
从结果看到, public方法hello()成功被代理了, 但是private方法不仅没有被代理到, 甚至也无法通过反射调用.
这其实也不难理解, 从抛出的异常信息中也可以看到:
java.lang.NoSuchMethodException: cn.zzzzbw.primary.proxy.service.impl.HelloServiceImpl
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } // 被代理类有接口, 使用JDK代理 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } // 被代理类没有实现接口, 使用Cglib代理 return new ObjenesisCglibAopProxy(config); } else { // 默认JDK代理 return new JdkDynamicAopProxy(config); } }}
这段代码就是Spring Boot经典的两种动态代理方式选择过程, 如果目标类有实现接口(targetClass.isInterface() || Proxy.isProxyClass(targetClass)),
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable { @Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } Class>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }}
可以看到JdkDynamicAopProxy实现了InvocationHandler接口, 然后在getProxy方法中先是做了一系列操作(AOP的execution表达式解析、代理链式调用等, 里面逻辑复杂且和我们代理主流程关系不大, 就不研究了),最后返回的是由JDK提供的生成代理类的方法Proxy.newProxyInstance的结果.
public class Proxy implements java.io.Serializable { public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException { /* * 1. 各种校验 */ Objects.requireNonNull(h); final Class>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * 2. 获取生成的代理类Class */ Class> cl = getProxyClass0(loader, intfs); /* * 3. 反射获取构造方法生成代理对象实例 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { cons.setAccessible(true);return null; } }); }return cons.newInstance(new Object[]{h}); } catch ... }}
Proxy.newProxyInstance()方法实际上做了3件事, 在上面流程代码注释了. 最重要的就是步骤2, 生成代理类的Class, Class> cl = getProxyClass0(loader, intfs);, 这就是生成动态代理类的核心方法.
那就再看一下getProxyClass0()方法
private static Class> getProxyClass0(ClassLoader loader, Class>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } /* * 如果代理类已经生成则直接返回, 否则通过ProxyClassFactory创建新的代理类 */ return proxyClassCache.get(loader, interfaces);}
getProxyClass0()方法从缓存proxyClassCache中获取对应的代理类. proxyClassCache是一个WeakCache对象, 他是一个类似于Map形式的缓存, 里面逻辑比较复杂就不细看了.
public Class> apply(ClassLoader loader, Class>[] interfaces) { ... // 上面是很多校验, 这里先不看 /* * 为新生成的代理类起名:proxyPkg(包名) + proxyClassNamePrefix(固定字符串"$Proxy") + num(当前代理类生成量) */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * 生成定义的代理类的字节码 byte数据 */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { /* * 把生成的字节码数据加载到JVM中, 返回对应的Class */ return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch ...}
ProxyClassFactory.apply()方法中主要就是做两件事:1. 调用ProxyGenerator.generateProxyClass()方法生成代理类的字节码数据 2. 把数据加载到JVM中生成Class.
public static byte[] generateProxyClass(final String name, Class[] interfaces){ ProxyGenerator gen = new ProxyGenerator(name, interfaces); // 实际生成字节码 final byte[] classFile = gen.generateClassFile(); // 访问权限操作, 这里省略 ... return classFile;}private byte[] generateClassFile() { /* ============================================================ * 步骤一: 添加所有需要代理的方法 */ // 添加equal、hashcode、toString方法 addProxyMethod(hashCodeMethod, Object.class); addProxyMethod(equalsMethod, Object.class); addProxyMethod(toStringMethod, Object.class); // 添加目标代理类的所有接口中的所有方法 for (int i = 0; i Method[] methods = interfaces[i].getMethods(); for (int j = 0; j addProxyMethod(methods[j], interfaces[i]); } } // 校验是否有重复的方法 for (List sigmethods : proxyMethods.values()) { checkReturnTypes(sigmethods); } /* ============================================================ * 步骤二:组装需要生成的代理类字段信息(FieldInfo)和方法信息(MethodInfo) */ try { // 添加构造方法 methods.add(generateConstructor());for (List sigmethods : proxyMethods.values()) {for (ProxyMethod pm : sigmethods) { // 由于代理类内部会用反射调用目标类实例的方法, 必须有反射依赖, 所以这里固定引入Method方法 fields.add(new FieldInfo(pm.methodFieldName,"Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC)); // 添加代理方法的信息 methods.add(pm.generateMethod()); } } methods.add(generateStaticInitializer()); } catch (IOException e) { throw new InternalError("unexpected I/O Exception"); }if (methods.size() > 65535) { throw new IllegalArgumentException("method limit exceeded"); }if (fields.size() > 65535) { throw new IllegalArgumentException("field limit exceeded"); } /* ============================================================ * 步骤三: 输出最终要生成的class文件 */ // 这部分就是根据上面组装的信息编写字节码 ...return bout.toByteArray();}
这个sun.misc.ProxyGenerator.generateClassFile()方法就是真正的实现生成代理类字节码数据的地方, 主要为三个步骤:
添加所有需要代理的方法, 把需要代理的方法(equal、hashcode、toString方法和接口中声明的方法)的一些相关信息记录下来.
组装需要生成的代理类的字段信息和方法信息. 这里会根据步骤一添加的方法, 生成实际的代理类的方法的实现. 比如:
如果目标代理类实现了一个HelloService接口, 且实现其中的方法hello, 那么生成的代理类就会生成如下形式方法:
public Object hello(Object... args){ try{ return (InvocationHandler)h.invoke(this, this.getMethod("hello"), args); } catch ... }
把上面添加和组装的信息通过流拼接出最终的java class字节码数据
前情提要: 在Service调用其他Service的private方法, @Transactional会生效吗(上)中证明了动态代理不会代理private方法的, 并通过阅读源码证实了. 但是我们可以自己实现一个动态代理功能替代Spring Boot中原有的, 达到动态代理private方法的目的. 主要流程为:
- 重新实现一个ProxyGenerator.generateClassFile()方法, 输出带有private方法的代理类字节码数据
- 把字节码数据加载到JVM中, 生成Class
- 替代Spring Boot中默认的动态代理功能, 换成我们自己的动态代理.
@Getterpublic abstract class PrivateProxyInvocationHandler implements InvocationHandler { private final Object subject; public PrivateProxyInvocationHandler(Object subject) { this.subject = subject; }}
前文的切面TransactionalAop是Spring Boot在JdkDynamicAopProxy中扫描被@Aspect注解的类, 然后解析类里面的方法以及切点等.
@Slf4jpublic class TransactionalHandler extends PrivateProxyInvocationHandler { public TransactionalHandler(Object subject) { super(subject); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log.info("Transaction start!"); Object result; try { result = method.invoke(getSubject(), args); } catch (Exception e) { log.info("Transaction rollback!"); throw new Throwable(e); } log.info("Transaction commit!"); return result; }}
public class PrivateProxyGenerator { ... private String generateClassSrc() { // 1. 添加equal、hashcode、toString方法 // 这里省略 // 2. 添加interface中的方法 for (Class> interfaceClz : interfaces) { // TODO 这里就不考虑多个interfaces含有相同method的情况了 Method[] methods = interfaceClz.getMethods(); this.proxyMethods.put(interfaceClz, Arrays.asList(methods)); } // 3. 添加代理类中的私有方法 // TODO 这是新增的 Object subject = h.getSubject(); Method[] declaredMethods = subject.getClass().getDeclaredMethods(); List privateMethods = Arrays.stream(declaredMethods) .filter(method -> method.getModifiers() == Modifier.PRIVATE) .collect(Collectors.toList()); this.privateMethods.addAll(privateMethods); // 4. 校验方法的签名等@see sun.misc.ProxyGenerator.checkReturnTypes // 这里省略 // 5. 添加类里的字段信息和方法数据 // 如静态方法、构造方法、字段等 // TODO 这里省略, 在编写java字符串(步骤7)时直接写入 // 6. 校验一下方法长度、字段长度等 // 这里省略 // 7. 把刚才添加的数据真正写到class文件里 // TODO 这里我们根据逻辑写成java字符串return writeJavaSrc(); } ...}
这部分代码和JDK的ProxyGenerator.generateProxyClass()方法流程类似, 主要就是保存一下被代理类及其方法的一些信息, 真正编写代码数据的功能在writeJavaSrc()方法里完成.
private String writeJavaSrc() { StringBuffer sb = new StringBuffer(); int packageIndex = this.className.lastIndexOf("."); String packageName = this.className.substring(0, packageIndex); String clzName = this.className.substring(packageIndex + 1); // package信息 sb.append("package").append(SPACE).append(packageName).append(SEMICOLON).append(WRAP); // class 信息, interface接口 sb.append(PUBLIC).append(SPACE).append("class").append(SPACE).append(clzName).append(SPACE); sb.append("implements").append(SPACE); String interfaceNameList = Arrays.stream(this.interfaces).map(Class::getTypeName).collect(Collectors.joining(",")); sb.append(interfaceNameList); sb.append(SPACE).append("{").append(WRAP); // 必须要的属性和构造函数 /** * private PrivateProxyInvocationHandler h; */ sb.append(PRIVATE).append(SPACE).append(PrivateProxyInvocationHandler.class.getName()).append(SPACE).append("h;").append(WRAP); /** * public $Proxy0(PrivateProxyInvocationHandler h) { * this.h = h; * } */ sb.append(PUBLIC).append(SPACE).append(clzName).append("(") .append(PrivateProxyInvocationHandler.class.getName()).append(SPACE).append("h").append("){").append(WRAP) .append("this.h = h;").append(WRAP) .append("}"); // 代理public方法 this.proxyMethods.forEach((interfaceClz, methods) -> { for (Method proxyMethod : methods) { writeProxyMethod(sb, interfaceClz, proxyMethod, PUBLIC); } }); // 代理private方法 for (Method proxyMethod : this.privateMethods) { writeProxyMethod(sb, null, proxyMethod, PRIVATE); } sb.append("}"); return sb.toString();}/** * 编写代理方法数据 */private void writeProxyMethod(StringBuffer sb, Class> interfaceClz, Method proxyMethod, String accessFlag) { // 1. 编写方法的声明, 例: // public void hello(java.lang.String var0) sb.append(accessFlag) .append(SPACE) // 返回类 .append(proxyMethod.getReturnType().getTypeName()).append(SPACE) .append(proxyMethod.getName()).append("("); // 参数类 Class>[] parameterTypes = proxyMethod.getParameterTypes(); // 参数类名 List argClassNames = new ArrayList<>(); // 参数名 List args = new ArrayList<>();for (int i = 0; i Class> parameterType = parameterTypes[i]; argClassNames.add(parameterType.getTypeName()); args.add("var" + i); } // 写入参数的声明for (int i = 0; i sb.append(argClassNames.get(i)).append(SPACE).append(args.get(i)).append(","); }if (parameterTypes.length > 0) { //去掉最后一个逗号 sb.replace(sb.length() - 1, sb.length(), ""); } sb.append(")").append("{").append(WRAP); // 如果是public方法, 则编写的代理方法逻辑大致如下 /** * try { * Method m = HelloService.class.getMethod("hello", String.class, Integer.class); * return this.h.invoke(this, proxyMethod, new Object[]{var0, var1...}); * } catch (Throwable e) { * throw new RuntimeException(e); * } */ // 如果是private方法, 则编写的代理方法逻辑大致如下 /** * try { * Method m = h.getSubject().getClass().getDeclaredMethod("hello", String.class, Integer.class); * m.setAccessible(true); * return this.h.invoke(this, proxyMethod, new Object[]{var0, var1...}); * } catch (Throwable e) { * throw new RuntimeException(e); * } */ // 2. try sb.append("try{").append(WRAP); // 3. 编写获取目标代理方法的功能 sb.append(Method.class.getTypeName()).append(SPACE).append("m = ");if (PUBLIC.equals(accessFlag)) { // 3.1 public方法的代理, 通过接口获取实例方法. 例: // java.lang.reflect.Method m = HelloService.class.getMethod("hello", String.class, Integer.class); sb.append(interfaceClz.getTypeName()).append(".class") .append(".getMethod(").append("\"").append(proxyMethod.getName()).append("\"").append(",").append(SPACE); } else { // 3.2 private方法的代理, 通过目标代理类实例获取方法. 例: // java.lang.reflect.Method m = h.getSubject().getClass().getDeclaredMethod("hello", String.class, Integer.class); sb.append("h.getSubject().getClass().getDeclaredMethod(").append("\"").append(proxyMethod.getName()).append("\"").append(",").append(SPACE); } argClassNames.forEach(name -> sb.append(name).append(".class").append(","));if (parameterTypes.length > 0) { //去掉最后一个逗号 sb.replace(sb.length() - 1, sb.length(), ""); } sb.append(");").append(WRAP);if (!PUBLIC.equals(accessFlag)) { // 3.3 不是public方法, 设置访问权限 sb.append("m.setAccessible(true);").append(WRAP); } // 4. InvocationHandler中调用代理方法逻辑, 例: // return this.h.invoke(this, m, new Object[]{var0});if (!proxyMethod.getReturnType().equals(Void.class) && !proxyMethod.getReturnType().equals(void.class)) { // 有返回值则返回且强转 sb.append("return").append(SPACE).append("(").append(proxyMethod.getReturnType().getName()).append(")"); } String argsList = String.join(",", args); sb.append("this.h.invoke(this, m, new Object[]{").append(argsList).append("});"); // 5. catch sb.append("} catch (Throwable e) {").append(WRAP); sb.append("throw new RuntimeException(e);").append(WRAP); sb.append("}"); sb.append("}").append(WRAP);}
writeJavaSrc()大体上分为两部分, 第一部分是编写类的一些固定信息代码数据, 如包名、类声明、构造函数等, 生成大致类似于下面的代码:
package cn.zzzzbw.primary.proxy.reflect;public class $Proxy0 implements cn.zzzzbw.primary.proxy.service.HelloService { private cn.zzzzbw.primary.proxy.reflect.PrivateProxyInvocationHandler h; public $Proxy0(cn.zzzzbw.primary.proxy.reflect.PrivateProxyInvocationHandler h) { this.h = h; }}
第二部分就是writeProxyMethod()方法, 编写代理后的方法的代码数据, 生成大致类似于下面的代码:
// 代理的public方法public void hello(java.lang.String var0) { try { java.lang.reflect.Method m = cn.zzzzbw.primary.proxy.service.HelloService.class.getMethod("hello", java.lang.String.class); this.h.invoke(this, m, new Object[]{var0}); } catch (Throwable e) { throw new RuntimeException(e); }}// 代理的private方法private long primaryHello(java.lang.Integer var0) { try { java.lang.reflect.Method m = h.getSubject().getClass().getDeclaredMethod("privateHello", java.lang.Integer.class); m.setAccessible(true); return (long) this.h.invoke(this, m, new Object[]{var0}); } catch (Throwable e) { throw new RuntimeException(e); }}
以上就是我们自己实现的支持private方法动态代理的"字节码"生成功能. 现在写个单元测试看一下效果
@Slf4jpublic class PrivateProxyGeneratorTests { public static void main(String[] args) throws IOException { // 1 生成java源碼 String packageName = "cn.zzzzbw.primary.proxy.reflect"; String clazzName = "$Proxy0"; String proxyName = packageName + "." + clazzName; Class>[] interfaces = HelloServiceImpl.class.getInterfaces(); PrivateProxyInvocationHandler h = new TransactionalHandler(new HelloServiceImpl()); String src = PrivateProxyGenerator.generateProxyClass(proxyName, interfaces, h); // 2 保存成java文件 String filePath = PrivateProxy.class.getResource("/").getPath(); String clzFilePath = filePath + packageName.replace(".", "/") + "/" + clazzName + ".java"; log.info("clzFilePath: {}", clzFilePath); File f = new File(clzFilePath); if (!f.getParentFile().exists()) { f.getParentFile().mkdirs(); } try (FileWriter fw = new FileWriter(f)) { fw.write(src); fw.flush(); } }}
运行之后生成了一个$Proxy0.java文件, 看一下文件内容(代码格式化了一下):
package cn.zzzzbw.primary.proxy.reflect;public class $Proxy0 implements cn.zzzzbw.primary.proxy.service.HelloService { private cn.zzzzbw.primary.proxy.reflect.PrivateProxyInvocationHandler h; public $Proxy0(cn.zzzzbw.primary.proxy.reflect.PrivateProxyInvocationHandler h) { this.h = h; } public void hello(java.lang.String var0) { try { java.lang.reflect.Method m = cn.zzzzbw.primary.proxy.service.HelloService.class.getMethod("hello", java.lang.String.class); this.h.invoke(this, m, new Object[]{var0}); } catch (Throwable e) { throw new RuntimeException(e); } } private long privateHello(java.lang.Integer var0) { try { java.lang.reflect.Method m = h.getSubject().getClass().getDeclaredMethod("privateHello", java.lang.Integer.class); m.setAccessible(true); return (long) this.h.invoke(this, m, new Object[]{var0}); } catch (Throwable e) { throw new RuntimeException(e); } }}
生成的$Proxy0就是被代理类HelloServiceImpl的代理类, 他实现了HelloServiceImpl的所有interface, 有个成员变量PrivateProxyInvocationHandler h,
public class PrivateProxy { private static final String proxyClassNamePrefix = "$Proxy"; private static final AtomicLong nextUniqueNumber = new AtomicLong(); public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, PrivateProxyInvocationHandler h) { try { // 1 生成java源码 String packageName = PrivateProxy.class.getPackage().getName(); long number = nextUniqueNumber.getAndAdd(1); String clazzName = proxyClassNamePrefix + number; String proxyName = packageName + "." + clazzName; String src = PrivateProxyGenerator.generateProxyClass(proxyName, interfaces, h); // 2 讲源码输出到java文件中 String filePath = PrivateProxy.class.getResource("/").getPath(); String clzFilePath = filePath + packageName.replace(".", "/") + "/" + clazzName + ".java"; File f = new File(clzFilePath); if (!f.getParentFile().exists()) { f.getParentFile().mkdirs(); } try (FileWriter fw = new FileWriter(f)) { fw.write(src); fw.flush(); } //3、将java文件编译成class文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null); Iterable extends JavaFileObject> iterable = manage.getJavaFileObjects(f); JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable); task.call(); manage.close(); f.delete(); // 4、将class加载进jvm Class> proxyClass = loader.loadClass(proxyName); // 通过构造方法生成代理对象 Constructor> constructor = proxyClass.getConstructor(PrivateProxyInvocationHandler.class); return constructor.newInstance(h); } catch (Exception e) { e.printStackTrace(); } return null; }}
PrivateProxy通过调用PrivateProxyGenerator.generateProxyClass()获取到代理类的.java文件的字符串, 然后输出到java文件中, 再编译成.class文件.@Slf4jpublic class PrivateProxyTests { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { PrivateProxyInvocationHandler handler = new PrivateProxyInvocationHandler(new HelloServiceImpl()) { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log.info("PrivateProxyInvocationHandler!"); return method.invoke(getSubject(), args); } }; Object o = PrivateProxy.newProxyInstance(ClassLoader.getSystemClassLoader(), HelloServiceImpl.class.getInterfaces(), handler); log.info("{}", o); HelloService helloService = (HelloService) o; helloService.hello("hello"); Method primaryHello = helloService.getClass().getDeclaredMethod("privateHello", Integer.class); primaryHello.setAccessible(true); Object invoke = primaryHello.invoke(helloService, 10); log.info("privateHello result: {}", invoke); }}
从单元测试结果看到PrivateProxy.newProxyInstance()方法成功生成了HelloServiceImpl的代理类cn.zzzzbw.primary.proxy.reflect.$Proxy0, 并且把public和private方法都代理了.
以上功能我们通过实现PrivateProxyGenerator和 PrivateProxy两个类, 实现了JDK的动态代理功能, 并且还能代理private方法. 接下来就要考虑如何把Spring Boot里的动态代理功能替换成我们自己的.
public class PrivateAopProxy implements AopProxy { private final AdvisedSupport advised; /** * 构造方法 *
* 直接复制JdkDynamicAopProxy构造方法逻辑
*/
public PrivateAopProxy(AdvisedSupport config) throws AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
throw new AopConfigException("No advisors and no TargetSource specified");
}
this.advised = config;
}
@Override
public Object getProxy() {return getProxy(ClassUtils.getDefaultClassLoader());
}
@Override
public Object getProxy(ClassLoader classLoader) {
// 获取目标类接口
Class>[] interfaces = this.advised.getTargetClass().getInterfaces();
TransactionalHandler handler;
try {
// 生成切面, 这里写死为TransactionalHandler
handler = new TransactionalHandler(this.advised.getTargetSource().getTarget());
} catch (Exception e) {
throw new RuntimeException(e);
}
// 返回代理类对象return PrivateProxy.newProxyInstance(classLoader, interfaces, handler);
}
}
PrivateAopProxy.getProxy()方法先通过advised获取到目标代理类的接口, 并通过实例生成切面TransactionalHandler, 然后返回刚才实现的PrivateProxy.newProxyInstance()方法生成的代理类.
JdkDynamicAopProxy的切面是通过自身实现InvocationHandler接口的invoke()方法, 实现了一个切面的链式调用的功能, 逻辑较复杂就不去模仿了.
class PrimaryAopProxyFactory implements AopProxyFactory { @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { return new PrivateAopProxy(config); }}
实现了的PrimaryAopProxyFactory, 现在要考虑怎么替换掉Spring Boot中的DefaultAopProxyFactory
(是不是有点像套娃, 但是没办法, 就只能这样一步一步替换过去. 我个人觉得Spring Boot这部分设计的就不够优雅了, 使用了Factory工厂模式, 但是想要替换AopProxy的时候却要把Factory也替换了.可能是开发者认为AOP这部分没必要开放给使用者修改吧, 或者是我个人没找到更好的方式修改)
想要替换掉DefaultAopProxyFactory, 就要找出哪里生成AopProxyFactory, 那么就可以通过打断点的方式把断点打在createAopProxy()上, 然后再看一下调用链.
观察到org.springframework.aop.framework.ProxyFactory.getProxy()方法负责生成和控制AopProxyFactory.createAopProxy()的逻辑. ProxyFactory继承了ProxyCreatorSupport类,
public class ProxyFactory extends ProxyCreatorSupport { public Object getProxy() { return createAopProxy().getProxy(); }}public class ProxyCreatorSupport extends AdvisedSupport { private AopProxyFactory aopProxyFactory; /** * Create a new ProxyCreatorSupport instance. */ public ProxyCreatorSupport() { this.aopProxyFactory = new DefaultAopProxyFactory(); } protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); } return getAopProxyFactory().createAopProxy(this); } public AopProxyFactory getAopProxyFactory() { return this.aopProxyFactory; }}
既然AopProxyFactory是ProxyFactory的一个变量, 那么现在看一下ProxyFactory是由谁控制的, 怎么样才能修改为PrimaryAopProxyFactory.
继续通过断点的方式, 发现在org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy()方法中会new一个ProxyFactory并且赋值一些属性, 然后调用ProxyFactory.getProxy()方法返回生成的代理对象. 看一下源码
protected Object createProxy(Class> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } // 实例化ProxyFactory ProxyFactory proxyFactory = new ProxyFactory(); // 下面都是为proxyFactory赋值 proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } // 调用Factory工厂方法返回代理类对象 return proxyFactory.getProxy(getProxyClassLoader());}
AbstractAutoProxyCreator.createProxy()做的事情就是new一个ProxyFactory, 然后为其赋值, 最后调用ProxyFactory.getProxy()返回代理对象.
由于ProxyFactory是直接new出来的, 是一个局部变量, 所以没办法全局的修改ProxyFactory.aopProxyFactory.
public class PrivateProxyAdvisorAutoProxyCreator extends AnnotationAwareAspectJAutoProxyCreator { @Override protected Object createProxy(Class> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { // 由于AutoProxyUtils.exposeTargetClass不是public方法, 且与本文功能无关, 这里就不作改造, 直接注释掉 /* if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } */ ProxyFactory proxyFactory = new ProxyFactory(); // 设置aopProxyFactory为PrimaryAopProxyFactory proxyFactory.setAopProxyFactory(new PrimaryAopProxyFactory()); proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(isFrozen()); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } return proxyFactory.getProxy(getProxyClassLoader()); }}
直接把AbstractAutoProxyCreator.createProxy()方法里的代码拷贝过来, 然后把一些调用private变量的地方改成调用其public的getter方法,
public abstract class AopConfigUtils { public static final String AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator"; // AbstractAdvisorAutoProxyCreator实现类列表 private static final List> APC_PRIORITY_LIST = new ArrayList<>(3); static { // 添加AbstractAdvisorAutoProxyCreator实现类, 优先级有小到大, 也就是说默认为最后添加的AnnotationAwareAspectJAutoProxyCreator APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); } // 引入AspectJAwareAdvisorAutoProxyCreator @Nullable public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {return registerAspectJAutoProxyCreatorIfNecessary(registry, null); } @Nullable public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) {return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source); } /** * 此方法引入AbstractAdvisorAutoProxyCreator实现类到Spring Boot中 */ @Nullable private static BeanDefinition registerOrEscalateApcAsRequired( Class> cls, BeanDefinitionRegistry registry, @Nullable Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null");if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { // 如果Spring Boot中已经有被引入的AbstractAdvisorAutoProxyCreator实现类, 则比对优先级 BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls);if (currentPriority apcDefinition.setBeanClassName(cls.getName()); } }return null; } // 引入对应的cls到Spring Boot的Bean管理中, 且命名为AUTO_PROXY_CREATOR_BEAN_NAME变量值 RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);return beanDefinition; }}
AopConfigUtils工具类引入AbstractAdvisorAutoProxyCreator的实现类的时候指定了Bean名,
@Component(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME)public class PrivateProxyAdvisorAutoProxyCreator extends AnnotationAwareAspectJAutoProxyCreator { ...}
但是这样还不够, 如果直接这样启动项目, 会爆出Class name [cn.zzzzbw.primary.proxy.spring.PrivateProxyAdvisorAutoProxyCreator] is not a known auto-proxy creator class的错误.
private static int findPriorityForClass(@Nullable String className) { for (int i = 0; i Class> clazz = APC_PRIORITY_LIST.get(i); if (clazz.getName().equals(className)) { return i; } } throw new IllegalArgumentException( "Class name [" + className + "] is not a known auto-proxy creator class");}
这下就比较麻烦了, APC_PRIORITY_LIST是private属性, 且也没有开放public方法去修改, 大概Spring官方也不想别人去修改这部分功能吧. 所以我只能通过反射的方式去修改了(如果是单元测试则写在单元测试里, 如果是启动项目则写在启动类里), 代码如下:
static { try { Field apc_priority_list = AopConfigUtils.class.getDeclaredField("APC_PRIORITY_LIST"); apc_priority_list.setAccessible(true); List> o = (List>) apc_priority_list.get(AopConfigUtils.class); o.add(PrivateProxyAdvisorAutoProxyCreator.class); } catch (Exception e) { e.printStackTrace(); }}
现在, 再跑一下最开头的单元测试!
从单元测试的结果看到, 切面TransactionalHandler不仅代理了HelloServiceImpl的public方法hello(), 也成功代理了private方法privateHello(), 并且是由Spring Boot来控制的!
经过一大长串的花里胡哨的操作, 终于实现了在private方法上使@Transactional生效的效果了. 当然, 目前这只是理论上的生效,
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。