前情提要:java
在Service調用其餘Service的private方法, @Transactional會生效嗎(上)中證實了動態代理不會代理private方法的, 並經過閱讀源碼證明了. git
可是咱們能夠本身實現一個動態代理功能替代
Spring Boot
中原有的, 達到動態代理private方法的目的. github主要流程爲:spring
- 從新實現一個
ProxyGenerator.generateClassFile()
方法, 輸出帶有private方法的代理類字節碼數據- 把字節碼數據加載到JVM中, 生成Class
- 替代
Spring Boot
中默認的動態代理功能, 換成咱們本身的動態代理.
首先, 要實現代理目標類的private方法的目標, 必需要能拿到被代理類的實例, 因此先改裝切面InvocationHandler
, 把要被代理的類保存下來. .app
@Getter public abstract class PrivateProxyInvocationHandler implements InvocationHandler { private final Object subject; public PrivateProxyInvocationHandler(Object subject) { this.subject = subject; } }
前文的切面TransactionalAop
是Spring Boot
在JdkDynamicAopProxy
中掃描被@Aspect
註解的類, 而後解析類裏面的方法以及切點等.
爲了簡便實現, 就不實現掃描解析的功能了, 這裏直接模仿前文的TransactionalAop
的功能實現切面TransactionalHandler
.jvm
@Slf4j public 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; } }
根據閱讀ProxyGenerator.generateProxyClass()
方法生成字節碼的代碼能夠知道, 動態代理的功能實際上就是動態的生成類的字節碼, 經過新生成的字節碼的類替代原有的類. ide
那咱們只要模仿generateProxyClass()
方法的功能, 實現本身的動態生成代碼的功能, 而且在生成的時候把被代理類的private方法也一併生成了, 就能夠實現private方法的動態代理功能. 函數
另外ProxyGenerator.generateProxyClass()
方法是直接編寫字節碼數據的(即.class
文件), 爲了方便咱們編寫和查看生成的數據, 咱們就實現動態編寫java數據, 而後再編譯成字節碼文件.工具
PrivateProxyGenerator是此次功能實現的核心代碼, 迫於文章篇幅這裏只放出重點部分, 如需完整代碼可直接查看源碼
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<Method> 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()
方法裏完成.post
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<String> argClassNames = new ArrayList<>(); // 參數名 List<String> args = new ArrayList<>(); for (int i = 0; i < parameterTypes.length; i++) { Class<?> parameterType = parameterTypes[i]; argClassNames.add(parameterType.getTypeName()); args.add("var" + i); } // 寫入參數的聲明 for (int i = 0; i < args.size(); 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方法動態代理的"字節碼"生成功能. 如今寫個單元測試看一下效果
@Slf4j public 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和private方法都被$Proxy0
從新實現了一遍, 經過PrivateProxyInvocationHandler.invoke()
來調用代理後的方法邏輯.
看來咱們本身實現的代理類字節碼動態生成的功能挺成功的, 接下來就要考慮代理類生成的邏輯, 以及如何把.java文件加載到JVM裏.
如今就模仿java.lang.reflect.Proxy.newProxyInstance()
方法, 編寫本身的編譯加載生成動態代理類對象的方法.
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(); } //三、將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(); // 四、將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文件.
接着經過ClassLoader
加載到JVM中
接着寫個單元測試看看效果:
@Slf4j public 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
裏的動態代理功能替換成咱們本身的.
Spring Boot
默認動態代理上面經過模仿JDK的動態代理, 本身實現了一個能代理private方法的動態代理功能.
如今爲了讓@Transactional
註解能對private方法生效, 就要把自定義的動態代理方法嵌入到Spring Boot
的代理流程中
Spring Boot
中自帶的兩種動態代理方式爲JDK和Cglib, 對應的實現類是JdkDynamicAopProxy
和ObjenesisCglibAopProxy
, 這兩個類都是實現AopProxy
接口, 實現其中的getProxy()
方法返回代理後的對象.
上文也分析了JdkDynamicAopProxy.getProxy()
方法是如何返回代理對象的, 這裏咱們就模仿來實現一個本身的AopProxy
.
public class PrivateAopProxy implements AopProxy { private final AdvisedSupport advised; /** * 構造方法 * <p> * 直接複製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()方法, 實現了一個切面的鏈式調用的功能, 邏輯較複雜就不去模仿了.
本文的目的主要是代理私有方法, 不怎麼關注切面, 因此就直接固定用new TransactionalHandler().
實現了PrivateAopProxy
類, 再考慮如何把他替換掉Spring Boot
中的JdkDynamicAopProxy
和ObjenesisCglibAopProxy
.
這兩種AopProxy
是經過DefaultAopProxyFactory.createAopProxy()
根據條件生成的, 那麼如今就要替換掉DefaultAopProxyFactory
, 經過實現本身的AopProxyFactory
來生成PrivateAopProxy
.
由於不須要DefaultAopProxyFactory
裏的那種判斷動態代理方式, 自定義的AopProxyFactory
就直接new一個PrivateAopProxy
返回就好了.
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
類,
其getProxy()
方法會調用ProxyCreatorSupport
中的aopProxyFactory
變量, 而aopProxyFactory
默認就是DefaultAopProxyFactory
, 相關源碼以下:
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
.
因此就考慮實現一個類繼承AbstractAutoProxyCreator
而後重寫createProxy()
方法, 在本身的createProxy()
方法中修改ProxyFactory.aopProxyFactory
的值.
AbstractAutoProxyCreator
是一個抽象類而且繼承的類和實現的接口比較多, 因此這邊我先查看了一下其整個的類結構圖(只顯示了重要的接口).
首先, 看一下其父類和父接口.
其實現了org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor
. SmartInstantiationAwareBeanPostProcessor
繼承InstantiationAwareBeanPostProcessor
,而InstantiationAwareBeanPostProcessor
繼承BeanPostProcessor
.
這三個接口是Spring
用於建立Bean時的加強功能, 是Spring
的IOC和AOP實現的核心思想, 建議你們都去了解一下, 這裏就不詳細講解了,
只要知道AbstractAutoProxyCreator
實現了SmartInstantiationAwareBeanPostProcessor
的接口, 因此能在建立Bean的時候對其進行代理.
接着, 看一下其子類.
其直接子類有AbstractAdvisorAutoProxyCreator
和BeanNameAutoProxyCreator
.
這兩個類的主要區別爲切點的不一樣, BeanNameAutoProxyCreator
是經過Bean名稱等配置指定切點, AbstractAdvisorAutoProxyCreator
是基於Advisor
匹配機制來決定切點.
AbstractAdvisorAutoProxyCreator
又有三個子類, 分別爲AnnotationAwareAspectJAutoProxyCreator(AspectJAwareAdvisorAutoProxyCreator)
, InfrastructureAdvisorAutoProxyCreator
, DefaultAdvisorAutoProxyCreator
.
一般使用的就是AnnotationAwareAspectJAutoProxyCreator
, 從名字上看就能夠知道, 它會經過註解和Aspect
表達式來決定切面,
如上文實現的TransactionalAop
切面裏的@Around("@within(org.springframework.transaction.annotation.Transactional)")
形式就是由AnnotationAwareAspectJAutoProxyCreator
處理的.
那麼如今直接繼承抽象類AbstractAutoProxyCreator
的子類AnnotationAwareAspectJAutoProxyCreator
, 而後重寫createProxy()
方法.
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方法,
再加上設置ProxyFactory.aopProxyFactory
爲PrimaryAopProxyFactory
的代碼: proxyFactory.setAopProxyFactory(new PrimaryAopProxyFactory());
就完成了PrivateProxyAdvisorAutoProxyCreator
.
接下來就是把PrivateProxyAdvisorAutoProxyCreator
引入到Spring Boot
組件中, 由於其實現了SmartInstantiationAwareBeanPostProcessor
接口, 因此我想着直接在類上加@Component
註解就行了.
可是加上以後卻沒有生效, 就去看一下AnnotationAwareAspectJAutoProxyCreator
, 這個類上是沒有加@Component
註解的, 那麼它是怎麼引入到Spring Boot
的?.
爲了查明緣由, 我就查一下哪裏調用了AnnotationAwareAspectJAutoProxyCreator
類, 找到了一個AopConfigUtils
這麼一個工具類, 上文提到的幾種AbstractAdvisorAutoProxyCreator
的實現類就是這裏引入的,
且設置Bean名爲"org.springframework.aop.config.internalAutoProxyCreator"
, 看一下相關代碼:
public abstract class AopConfigUtils { public static final String AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator"; // AbstractAdvisorAutoProxyCreator實現類列表 private static final List<Class<?>> 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 < requiredPriority) { 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名,
因此咱們要給PrivateProxyAdvisorAutoProxyCreator
的Bean名也指定爲AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME
才能覆蓋:
@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
的錯誤.
這是因爲AopConfigUtils
在查找AbstractAdvisorAutoProxyCreator
實現類的優先級的時候要求必須是在AopConfigUtils.APC_PRIORITY_LIST
有的才行.
private static int findPriorityForClass(@Nullable String className) { for (int i = 0; i < APC_PRIORITY_LIST.size(); 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<Class<?>> o = (List<Class<?>>) 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
生效的效果了. 固然, 目前這只是理論上的生效,
由於中間在模仿JdkDynamicAopProxy
實現PrivateAopProxy
的時候, 因爲JdkDynamicAopProxy
的切面實現邏輯很是複雜, 咱們直接把切面寫死成了TransactionalHandler
.
可是本文的主要目的就是可以在Spring Boot
代理private方法, 只要可以代理, 說明@Transactional
事務生效也是徹底能作到的.
"Service調用其餘Service的private方法, @Transactional會生效嗎"
若是僅僅回答問題自己是很簡單的, 只要瞭解Spring Boot
的AOP原理便可. 可是也能夠深刻其中, 順着這個問題繼續研究,
從前文Service調用其餘Service的private方法, @Transactional會生效嗎(上)閱讀Spring Boot
動態代理的功能源碼實現, 到本文親手實現"特殊功能"的動態代理,
不只精通了Spring Boot
動態代理的代碼實現流程, 還掌握了JDK的動態代理功能, 收益很是大!
文中相關源碼: private-proxy-source-code