<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Service調(diào)用其他Service的private方法, @Transactional會生效嗎

          共 29972字,需瀏覽 60分鐘

           ·

          2020-10-30 03:55

          作者:zzzzbw

          來源:SegmentFault 思否社區(qū)




          省流大師:


          1. 一個Service調(diào)用其他Service的private方法, @Transactional會生效嗎
          2. 正常流程不能生效
          3. 經(jīng)過一番操作, 達到理論上可以

          本文基于Spring Boot 2.3.3.RELEASE、JDK1.8 版本, 使用Lombok插件




          疑問


          有一天, 我的小伙伴問我,"一個Service調(diào)用其他Service的private方法, @Transactional的事務(wù)會生效嗎?"

          我當場直接就回答: "這還用想, 那肯定不能生效啊!". 于是他問, "為什么不能生效?"

          "這不是很明顯的事情, 你怎么在一個Service調(diào)用另一個Service的私有方法?". 他接著說到: "可以用反射啊".

          "就算用反射, @Transactional的原理是基于AOP的動態(tài)代理實現(xiàn)的, 動態(tài)代理不會代理private方法的!".

          他接著問道: "真的不會代理private方法嗎?".

          "額...應(yīng)該不會吧..."

          這下我回答的比較遲疑了. 因為平時只是大概知道動態(tài)代理會在字節(jié)碼的層面生成java類, 但是里面具體怎么實現(xiàn), 會不會處理private方法, 還真的不確定



          驗證


          雖然心里知道了結(jié)果, 但還是要實踐一下, Service調(diào)用其他Service的private方法,?@Transactional的事務(wù)到底能不能生效, 看看會不會被打臉.

          由于@Transactional的事務(wù)效果測試的時候不方便直白的看到, 不過其事務(wù)是通過AOP的切面實現(xiàn)的, 所以這里自定義一個切面來表示事務(wù)效果, 方便測試, 只要這個切面生效, 那事務(wù)生效肯定也不是事.

          @Slf4j
          @Aspect
          @Component
          public?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方法中通過反射調(diào)用HelloServiceImpl的private方法primaryHello().

          public?interface?HelloService?{
          ????void?hello(String?name);
          }

          @Slf4j
          @Transactional
          @Service
          public?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
          @SpringBootTest
          public?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);
          ????}
          }


          從結(jié)果看到, public方法hello()成功被代理了, 但是private方法不僅沒有被代理到, 甚至也無法通過反射調(diào)用.

          這其實也不難理解, 從拋出的異常信息中也可以看到:

          java.lang.NoSuchMethodException: cn.zzzzbw.primary.proxy.service.impl.HelloServiceImpl$$EnhancerBySpringCGLIB$$679d418b.privateHello(java.lang.Integer)

          helloService注入的不是實現(xiàn)類HelloServiceImpl, 而是代理類生成的HelloServiceImpl$$EnhancerBySpringCGLIB$$6f6c17b4. 假如生成代理類的時候沒有把private方法也寫上, 那么自然是沒法調(diào)用的.

          一個Service調(diào)用其他Service的private方法, @Transactional的事務(wù)是不會生效的

          從上面的驗證結(jié)果可以得到這個結(jié)果. 但是這只是現(xiàn)象, 還需要最終看具體的代碼來確定一下, 是不是真的在代理的時候把private方法丟掉了, 是怎么丟掉的.



          Spring Boot代理生成流程


          Spring Boot生成代理類的大致流程如下:

          [生成Bean實例] -> [Bean后置處理器(如BeanPostProcessor)] -> [調(diào)用ProxyFactory.getProxy方法(如果需要被代理)] -> [調(diào)用DefaultAopProxyFactory.createAopProxy.getProxy方法獲取代理后的對象]

          其中重點關(guān)注一下DefaultAopProxyFactory.createAopProxy方法.

          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);
          ????????????}
          ????????????//?被代理類沒有實現(xiàn)接口,?使用Cglib代理
          ????????????return?new?ObjenesisCglibAopProxy(config);
          ????????}
          ????????else?{
          ????????????//?默認JDK代理
          ????????????return?new?JdkDynamicAopProxy(config);
          ????????}
          ????}
          }

          這段代碼就是Spring Boot經(jīng)典的兩種動態(tài)代理方式選擇過程, 如果目標類有實現(xiàn)接口(targetClass.isInterface() || Proxy.isProxyClass(targetClass)),
          則用JDK代理(JdkDynamicAopProxy), 否則用CGlib代理(ObjenesisCglibAopProxy).

          不過在Spring Boot 2.x版本以后, 默認會用CGlib代理模式, 但實際上Spring 5.x中AOP默認代理模式還是JDK, 是Spring Boot特意修改的, 具體原因這里不詳細講解了, 感興趣的可以去看一下issue #5423

          假如想要強制使用JDK代理模式, 可以設(shè)置配置spring.aop.proxy-target-class=false

          上面的HelloServiceImpl實現(xiàn)了HelloService接口, 用的就是JdkDynamicAopProxy(為了防止Spring Boot2.x修改的影響, 這里設(shè)置配置強制開啟JDK代理). 于是看一下JdkDynamicAopProxy.getProxy方法

          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實現(xiàn)了InvocationHandler接口, 然后在getProxy方法中先是做了一系列操作(AOP的execution表達式解析、代理鏈式調(diào)用等, 里面邏輯復(fù)雜且和我們代理主流程關(guān)系不大, 就不研究了),最后返回的是由JDK提供的生成代理類的方法Proxy.newProxyInstance的結(jié)果.



          JDK代理類生成流程


          既然Spring把代理的流程托付給JDK了, 那我們也跟著流程看看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.?反射獲取構(gòu)造方法生成代理對象實例
          ?????????*/
          ????????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);, 這就是生成動態(tài)代理類的核心方法.

          那就再看一下getProxyClass0()方法

          private?static?Class?getProxyClass0(ClassLoader?loader,
          ???????????????????????????????????????Class...?interfaces)?{
          ????if?(interfaces.length?>?65535)?{
          ????????throw?new?IllegalArgumentException("interface?limit?exceeded");
          ????}
          ????/*
          ?????*?如果代理類已經(jīng)生成則直接返回,?否則通過ProxyClassFactory創(chuàng)建新的代理類
          ?????*/
          ????return?proxyClassCache.get(loader,?interfaces);
          }

          getProxyClass0()方法從緩存proxyClassCache中獲取對應(yīng)的代理類.?proxyClassCache是一個WeakCache對象, 他是一個類似于Map形式的緩存, 里面邏輯比較復(fù)雜就不細看了.

          不過我們只要知道, 這個緩存在get時如果存在值, 則返回這個值, 如果不存在, 則調(diào)用ProxyClassFactory的apply()方法.

          所以現(xiàn)在看一下ProxyClassFactory.apply()方法

          public?Class?apply(ClassLoader?loader,?Class[]?interfaces)?{
          ????...
          ????//?上面是很多校驗,?這里先不看

          ????/*
          ?????*?為新生成的代理類起名:proxyPkg(包名)?+ proxyClassNamePrefix(固定字符串"$Proxy")?+?num(當前代理類生成量)
          ?????*/
          ????long?num?=?nextUniqueNumber.getAndIncrement();
          ????String?proxyName?=?proxyPkg?+?proxyClassNamePrefix?+?num;

          ????/*
          ?????*?生成定義的代理類的字節(jié)碼?byte數(shù)據(jù)
          ?????*/
          ????byte[]?proxyClassFile?=?ProxyGenerator.generateProxyClass(
          ????????proxyName,?interfaces,?accessFlags);
          ????try?{
          ????????/*
          ?????????*?把生成的字節(jié)碼數(shù)據(jù)加載到JVM中,?返回對應(yīng)的Class
          ?????????*/
          ????????return?defineClass0(loader,?proxyName,
          ????????????????????????????proxyClassFile,?0,?proxyClassFile.length);
          ????}?catch?...
          }

          ProxyClassFactory.apply()方法中主要就是做兩件事:1. 調(diào)用ProxyGenerator.generateProxyClass()方法生成代理類的字節(jié)碼數(shù)據(jù) 2. 把數(shù)據(jù)加載到JVM中生成Class.



          代理類字節(jié)碼生成流程


          經(jīng)過一連串的源碼查看, 終于到最關(guān)鍵的生成字節(jié)碼環(huán)節(jié)了. 現(xiàn)在一起來看代理類字節(jié)碼是到底怎么生成的, 對待private方法是怎么處理的.

          public?static?byte[]?generateProxyClass(final?String?name,
          ???????????????????????????????????????Class[]?interfaces)
          {
          ???ProxyGenerator?gen?=?new?ProxyGenerator(name,?interfaces);
          ???//?實際生成字節(jié)碼
          ???final?byte[]?classFile?=?gen.generateClassFile();
          ????
          ????//?訪問權(quán)限操作,?這里省略
          ????...

          ???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]);
          ???????}
          ???}

          ???//?校驗是否有重復(fù)的方法
          ???for?(List?sigmethods?:?proxyMethods.values())?{
          ???????checkReturnTypes(sigmethods);
          ???}

          ???/*?============================================================
          ????*?步驟二:組裝需要生成的代理類字段信息(FieldInfo)和方法信息(MethodInfo)
          ????*/
          ???try?{
          ???????//?添加構(gòu)造方法
          ???????methods.add(generateConstructor());

          ???????for?(List?sigmethods?:?proxyMethods.values())?{
          ???????????for?(ProxyMethod?pm?:?sigmethods)?{

          ???????????????//?由于代理類內(nèi)部會用反射調(diào)用目標類實例的方法,?必須有反射依賴,?所以這里固定引入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文件
          ????*/

          ????//?這部分就是根據(jù)上面組裝的信息編寫字節(jié)碼
          ????...

          ???return?bout.toByteArray();
          }

          這個sun.misc.ProxyGenerator.generateClassFile()方法就是真正的實現(xiàn)生成代理類字節(jié)碼數(shù)據(jù)的地方, 主要為三個步驟:

          添加所有需要代理的方法, 把需要代理的方法(equal、hashcode、toString方法和接口中聲明的方法)的一些相關(guān)信息記錄下來.
          1. 組裝需要生成的代理類的字段信息和方法信息. 這里會根據(jù)步驟一添加的方法, 生成實際的代理類的方法的實現(xiàn). 比如:

          2. 如果目標代理類實現(xiàn)了一個HelloService接口, 且實現(xiàn)其中的方法hello, 那么生成的代理類就會生成如下形式方法:




            public?Object?hello(Object...?args){
            ????try{
            ????????return?(InvocationHandler)h.invoke(this,?this.getMethod("hello"),?args);
            ????}?catch?...??
            }


          3. 把上面添加和組裝的信息通過流拼接出最終的java class字節(jié)碼數(shù)據(jù)


          **看了這段代碼, 現(xiàn)在我們可以真正確定代理類是不會代理private方法了. 在步驟一中知道代理類只會代理equal、hashcode、toString方法和接口中聲明的方法, 所以目標類的private方法是不會被代理到的.

          不過想一下也知道, 私有方法在正常情況下外部也無法調(diào)用, 即使代理了也沒法使用, 所以也沒必要去代理.**



          結(jié)論


          上文通過閱讀Spring Boot動態(tài)代理流程以及JDK動態(tài)代理功能實現(xiàn)的源碼, 得出結(jié)論動態(tài)代理不會代理private方法, 所以@Transactional注解的事務(wù)也不會對其生效.

          但是看完成整個代理流程之后感覺動態(tài)代理也不過如此嘛, JDK提供的動態(tài)代理功能太菜了, 我們完全可以自己來實現(xiàn)動態(tài)代理的功能, 讓@Transactional注解的private方法也能生效, 我上我也行!

          根據(jù)上面看源碼流程, 如果要實現(xiàn)代理private方法并使@Transactional注解生效的效果, 那么只要倒敘剛才看源碼的流程, 如下:

          1. 重新實現(xiàn)一個ProxyGenerator.generateClassFile()方法, 輸出帶有private方法的代理類字節(jié)碼數(shù)據(jù)
          2. 把字節(jié)碼數(shù)據(jù)加載到JVM中, 生成Class
          3. 替代Spring Boot中默認的動態(tài)代理功能, 換成我們自己的動態(tài)代理.

          這部分內(nèi)容在Service調(diào)用其他Service的private方法, @Transactional會生效嗎(下), 歡迎閱讀

          前情提要:
          在Service調(diào)用其他Service的private方法, @Transactional會生效嗎(上)中證明了動態(tài)代理不會代理private方法的, 并通過閱讀源碼證實了.
          但是我們可以自己實現(xiàn)一個動態(tài)代理功能替代Spring Boot中原有的, 達到動態(tài)代理private方法的目的.
          主要流程為:
          1. 重新實現(xiàn)一個ProxyGenerator.generateClassFile()方法, 輸出帶有private方法的代理類字節(jié)碼數(shù)據(jù)
          2. 把字節(jié)碼數(shù)據(jù)加載到JVM中, 生成Class
          3. 替代Spring Boot中默認的動態(tài)代理功能, 換成我們自己的動態(tài)代理.



          前置代碼


          首先, 要實現(xiàn)代理目標類的private方法的目標, 必須要能拿到被代理類的實例, 所以先改裝切面InvocationHandler, 把要被代理的類保存下來.?

          @Getter
          public?abstract?class?PrivateProxyInvocationHandler?implements?InvocationHandler?{

          ????private?final?Object?subject;

          ????public?PrivateProxyInvocationHandler(Object?subject)?{
          ????????this.subject?=?subject;
          ????}
          }

          前文的切面TransactionalAop是Spring Boot在JdkDynamicAopProxy中掃描被@Aspect注解的類, 然后解析類里面的方法以及切點等.

          為了簡便實現(xiàn), 就不實現(xiàn)掃描解析的功能了, 這里直接模仿前文的TransactionalAop的功能實現(xiàn)切面TransactionalHandler.

          @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;
          ????}
          }



          生成字節(jié)碼數(shù)據(jù)


          根據(jù)閱讀ProxyGenerator.generateProxyClass()方法生成字節(jié)碼的代碼可以知道, 動態(tài)代理的功能實際上就是動態(tài)的生成類的字節(jié)碼, 通過新生成的字節(jié)碼的類替代原有的類.

          那我們只要模仿generateProxyClass()方法的功能, 實現(xiàn)自己的動態(tài)生成代碼的功能, 并且在生成的時候把被代理類的private方法也一并生成了, 就可以實現(xiàn)private方法的動態(tài)代理功能.

          另外ProxyGenerator.generateProxyClass()方法是直接編寫字節(jié)碼數(shù)據(jù)的(即.class文件), 為了方便我們編寫和查看生成的數(shù)據(jù), 我們就實現(xiàn)動態(tài)編寫java數(shù)據(jù), 然后再編譯成字節(jié)碼文件.

          PrivateProxyGenerator是這次功能實現(xiàn)的核心代碼, 迫于文章篇幅這里只放出重點部分, 如需完整代碼可直接查看源碼

          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.?添加類里的字段信息和方法數(shù)據(jù)
          ????????//?如靜態(tài)方法、構(gòu)造方法、字段等
          ????????//?TODO?這里省略,?在編寫java字符串(步驟7)時直接寫入

          ????????//?6.?校驗一下方法長度、字段長度等
          ????????//?這里省略

          ????????//?7.?把剛才添加的數(shù)據(jù)真正寫到class文件里
          ????????//?TODO?這里我們根據(jù)邏輯寫成java字符串
          ????????return?writeJavaSrc();
          ????}

          ????...
          }

          這部分代碼和JDK的ProxyGenerator.generateProxyClass()方法流程類似, 主要就是保存一下被代理類及其方法的一些信息, 真正編寫代碼數(shù)據(jù)的功能在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);


          ????//?必須要的屬性和構(gòu)造函數(shù)
          ????/**
          ?????*?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();
          }

          /**
          ?*?編寫代理方法數(shù)據(jù)
          ?*/
          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("(");

          ????//?參數(shù)類
          ????Class[]?parameterTypes?=?proxyMethod.getParameterTypes();
          ????//?參數(shù)類名
          ????List?argClassNames?=?new?ArrayList<>();
          ????//?參數(shù)名
          ????List?args?=?new?ArrayList<>();
          ????for?(int?i?=?0;?i?????????Class?parameterType?=?parameterTypes[i];
          ????????argClassNames.add(parameterType.getTypeName());
          ????????args.add("var"?+?i);
          ????}
          ????//?寫入?yún)?shù)的聲明
          ????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方法,?設(shè)置訪問權(quán)限
          ????????sb.append("m.setAccessible(true);").append(WRAP);
          ????}

          ????//?4.?InvocationHandler中調(diào)用代理方法邏輯,?例:
          ????//?return?this.h.invoke(this,?m,?new?Object[]{var0});
          ????if?(!proxyMethod.getReturnType().equals(Void.class)?&&?!proxyMethod.getReturnType().equals(void.class))?{
          ????????//?有返回值則返回且強轉(zhuǎn)
          ????????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()大體上分為兩部分, 第一部分是編寫類的一些固定信息代碼數(shù)據(jù), 如包名、類聲明、構(gòu)造函數(shù)等, 生成大致類似于下面的代碼:

          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()方法, 編寫代理后的方法的代碼數(shù)據(jù), 生成大致類似于下面的代碼:

          //?代理的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);
          ????}
          }

          以上就是我們自己實現(xiàn)的支持private方法動態(tài)代理的"字節(jié)碼"生成功能. 現(xiàn)在寫個單元測試看一下效果

          @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文件, 看一下文件內(nèi)容(代碼格式化了一下):

          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的代理類, 他實現(xiàn)了HelloServiceImpl的所有interface, 有個成員變量PrivateProxyInvocationHandler h,

          其所有public和private方法都被$Proxy0重新實現(xiàn)了一遍, 通過PrivateProxyInvocationHandler.invoke()來調(diào)用代理后的方法邏輯.

          看來我們自己實現(xiàn)的代理類字節(jié)碼動態(tài)生成的功能挺成功的, 接下來就要考慮代理類生成的邏輯, 以及如何把.java文件加載到JVM里.



          加載到JVM, 生成動態(tài)代理類


          現(xiàn)在就模仿java.lang.reflect.Proxy.newProxyInstance()方法, 編寫自己的編譯加載生成動態(tài)代理類對象的方法.

          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?講源碼輸出到j(luò)ava文件中
          ????????????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?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);

          ????????????//?通過構(gòu)造方法生成代理對象
          ????????????Constructor?constructor?=?proxyClass.getConstructor(PrivateProxyInvocationHandler.class);
          ????????????return?constructor.newInstance(h);
          ????????}?catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????????return?null;
          ????}
          }

          PrivateProxy通過調(diào)用PrivateProxyGenerator.generateProxyClass()獲取到代理類的.java文件的字符串, 然后輸出到j(luò)ava文件中, 再編譯成.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);
          ????}
          }


          從單元測試結(jié)果看到PrivateProxy.newProxyInstance()方法成功生成了HelloServiceImpl的代理類cn.zzzzbw.primary.proxy.reflect.$Proxy0, 并且把public和private方法都代理了.

          以上功能我們通過實現(xiàn)PrivateProxyGenerator和?PrivateProxy兩個類, 實現(xiàn)了JDK的動態(tài)代理功能, 并且還能代理private方法. 接下來就要考慮如何把Spring Boot里的動態(tài)代理功能替換成我們自己的.



          替代Spring Boot默認動態(tài)代理


          上面通過模仿JDK的動態(tài)代理, 自己實現(xiàn)了一個能代理private方法的動態(tài)代理功能.

          現(xiàn)在為了讓@Transactional注解能對private方法生效, 就要把自定義的動態(tài)代理方法嵌入到Spring Boot的代理流程中

          AopProxy


          Spring Boot中自帶的兩種動態(tài)代理方式為JDK和Cglib, 對應(yīng)的實現(xiàn)類是JdkDynamicAopProxy和ObjenesisCglibAopProxy, 這兩個類都是實現(xiàn)AopProxy接口, 實現(xiàn)其中的getProxy()方法返回代理后的對象.

          上文也分析了JdkDynamicAopProxy.getProxy()方法是如何返回代理對象的, 這里我們就模仿來實現(xiàn)一個自己的AopProxy.

          public?class?PrivateAopProxy?implements?AopProxy?{
          ????private?final?AdvisedSupport?advised;
          ????
          ????/**
          ?????*?構(gòu)造方法
          ?????*?


          ?????*?直接復(fù)制JdkDynamicAopProxy構(gòu)造方法邏輯
          ?????*/
          ????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, 然后返回剛才實現(xiàn)的PrivateProxy.newProxyInstance()方法生成的代理類.

          JdkDynamicAopProxy的切面是通過自身實現(xiàn)InvocationHandler接口的invoke()方法, 實現(xiàn)了一個切面的鏈式調(diào)用的功能, 邏輯較復(fù)雜就不去模仿了.

          本文的目的主要是代理私有方法, 不怎么關(guān)注切面, 所以就直接固定用new TransactionalHandler().

          AbstractAdvisorAutoProxyCreator


          實現(xiàn)了PrivateAopProxy類, 再考慮如何把他替換掉Spring Boot中的JdkDynamicAopProxy和ObjenesisCglibAopProxy.

          這兩種AopProxy是通過DefaultAopProxyFactory.createAopProxy()根據(jù)條件生成的, 那么現(xiàn)在就要替換掉DefaultAopProxyFactory, 通過實現(xiàn)自己的AopProxyFactory來生成PrivateAopProxy.

          因為不需要DefaultAopProxyFactory里的那種判斷動態(tài)代理方式, 自定義的AopProxyFactory就直接new一個PrivateAopProxy返回就行了.

          class?PrimaryAopProxyFactory?implements?AopProxyFactory?{
          ????@Override
          ????public?AopProxy?createAopProxy(AdvisedSupport?config)?throws?AopConfigException?{
          ????????return?new?PrivateAopProxy(config);
          ????}
          }

          實現(xiàn)了的PrimaryAopProxyFactory, 現(xiàn)在要考慮怎么替換掉Spring Boot中的DefaultAopProxyFactory

          (是不是有點像套娃, 但是沒辦法, 就只能這樣一步一步替換過去. 我個人覺得Spring Boot這部分設(shè)計的就不夠優(yōu)雅了, 使用了Factory工廠模式, 但是想要替換AopProxy的時候卻要把Factory也替換了.可能是開發(fā)者認為AOP這部分沒必要開放給使用者修改吧, 或者是我個人沒找到更好的方式修改)

          想要替換掉DefaultAopProxyFactory, 就要找出哪里生成AopProxyFactory, 那么就可以通過打斷點的方式把斷點打在createAopProxy()上, 然后再看一下調(diào)用鏈.

          觀察到org.springframework.aop.framework.ProxyFactory.getProxy()方法負責生成和控制AopProxyFactory.createAopProxy()的邏輯.?ProxyFactory繼承了ProxyCreatorSupport類,

          其getProxy()方法會調(diào)用ProxyCreatorSupport中的aopProxyFactory變量, 而aopProxyFactory默認就是DefaultAopProxyFactory, 相關(guān)源碼如下:

          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的一個變量, 那么現(xiàn)在看一下ProxyFactory是由誰控制的, 怎么樣才能修改為PrimaryAopProxyFactory.

          繼續(xù)通過斷點的方式, 發(fā)現(xiàn)在org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy()方法中會new一個ProxyFactory并且賦值一些屬性, 然后調(diào)用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);
          ????}
          ????
          ????//?調(diào)用Factory工廠方法返回代理類對象
          ????return?proxyFactory.getProxy(getProxyClassLoader());
          }

          AbstractAutoProxyCreator.createProxy()做的事情就是new一個ProxyFactory, 然后為其賦值, 最后調(diào)用ProxyFactory.getProxy()返回代理對象.

          由于ProxyFactory是直接new出來的, 是一個局部變量, 所以沒辦法全局的修改ProxyFactory.aopProxyFactory.

          所以就考慮實現(xiàn)一個類繼承AbstractAutoProxyCreator然后重寫createProxy()方法, 在自己的createProxy()方法中修改ProxyFactory.aopProxyFactory的值.

          AbstractAutoProxyCreator是一個抽象類并且繼承的類和實現(xiàn)的接口比較多, 所以這邊我先查看了一下其整個的類結(jié)構(gòu)圖(只顯示了重要的接口).


          首先, 看一下其父類和父接口.

          其實現(xiàn)了org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor.

          SmartInstantiationAwareBeanPostProcessor繼承InstantiationAwareBeanPostProcessor,而InstantiationAwareBeanPostProcessor繼承BeanPostProcessor.
          這三個接口是Spring用于創(chuàng)建Bean時的增強功能, 是Spring的IOC和AOP實現(xiàn)的核心思想, 建議大家都去了解一下, 這里就不詳細講解了,只要知道AbstractAutoProxyCreator實現(xiàn)了SmartInstantiationAwareBeanPostProcessor的接口, 所以能在創(chuàng)建Bean的時候?qū)ζ溥M行代理.

          接著, 看一下其子類.

          其直接子類有AbstractAdvisorAutoProxyCreator和BeanNameAutoProxyCreator.

          這兩個類的主要區(qū)別為切點的不同,?BeanNameAutoProxyCreator是通過Bean名稱等配置指定切點,?AbstractAdvisorAutoProxyCreator是基于Advisor匹配機制來決定切點.

          AbstractAdvisorAutoProxyCreator又有三個子類, 分別為AnnotationAwareAspectJAutoProxyCreator(AspectJAwareAdvisorAutoProxyCreator),?InfrastructureAdvisorAutoProxyCreator,?DefaultAdvisorAutoProxyCreator.

          通常使用的就是AnnotationAwareAspectJAutoProxyCreator, 從名字上看就可以知道, 它會通過注解和Aspect表達式來決定切面,

          如上文實現(xiàn)的TransactionalAop切面里的@Around("@within(org.springframework.transaction.annotation.Transactional)")形式就是由AnnotationAwareAspectJAutoProxyCreator處理的.

          那么現(xiàn)在直接繼承抽象類AbstractAutoProxyCreator的子類AnnotationAwareAspectJAutoProxyCreator, 然后重寫createProxy()方法.

          public?class?PrivateProxyAdvisorAutoProxyCreator?extends?AnnotationAwareAspectJAutoProxyCreator?{

          ????@Override
          ????protected?Object?createProxy(Class?beanClass,?String?beanName,?Object[]?specificInterceptors,?TargetSource?targetSource)?{
          ????????
          ????????//?由于AutoProxyUtils.exposeTargetClass不是public方法,?且與本文功能無關(guān),?這里就不作改造,?直接注釋掉
          ????????/*
          ????????if?(this.beanFactory?instanceof?ConfigurableListableBeanFactory)?{
          ????????????AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)?this.beanFactory,?beanName,?beanClass);
          ????????}
          ????????*/

          ????????ProxyFactory?proxyFactory?=?new?ProxyFactory();
          ????????//?設(shè)置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()方法里的代碼拷貝過來, 然后把一些調(diào)用private變量的地方改成調(diào)用其public的getter方法,

          再加上設(shè)置ProxyFactory.aopProxyFactory為PrimaryAopProxyFactory的代碼:?proxyFactory.setAopProxyFactory(new PrimaryAopProxyFactory());就完成了PrivateProxyAdvisorAutoProxyCreator.



          引入到Bean中


          接下來就是把PrivateProxyAdvisorAutoProxyCreator引入到Spring Boot組件中, 因為其實現(xiàn)了SmartInstantiationAwareBeanPostProcessor接口, 所以我想著直接在類上加@Component注解就好了.

          但是加上之后卻沒有生效, 就去看一下AnnotationAwareAspectJAutoProxyCreator, 這個類上是沒有加@Component注解的, 那么它是怎么引入到Spring Boot的?

          為了查明原因, 我就查一下哪里調(diào)用了AnnotationAwareAspectJAutoProxyCreator類, 找到了一個AopConfigUtils這么一個工具類, 上文提到的幾種AbstractAdvisorAutoProxyCreator的實現(xiàn)類就是這里引入的,

          且設(shè)置Bean名為"org.springframework.aop.config.internalAutoProxyCreator", 看一下相關(guān)代碼:

          public?abstract?class?AopConfigUtils?{

          ????public?static?final?String?AUTO_PROXY_CREATOR_BEAN_NAME?=
          ????????????"org.springframework.aop.config.internalAutoProxyCreator";
          ????
          ????//?AbstractAdvisorAutoProxyCreator實現(xiàn)類列表
          ????private?static?final?List>?APC_PRIORITY_LIST?=?new?ArrayList<>(3);

          ????static?{
          ????????//?添加AbstractAdvisorAutoProxyCreator實現(xiàn)類,?優(yōu)先級有小到大,?也就是說默認為最后添加的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實現(xiàn)類到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中已經(jīng)有被引入的AbstractAdvisorAutoProxyCreator實現(xiàn)類,?則比對優(yōu)先級
          ????????????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;
          ????????}
          ????????//?引入對應(yīng)的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的實現(xiàn)類的時候指定了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實現(xiàn)類的優(yōu)先級的時候要求必須是在AopConfigUtils.APC_PRIORITY_LIST有的才行.

          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();
          ????}
          }

          現(xiàn)在, 再跑一下最開頭的單元測試!


          從單元測試的結(jié)果看到, 切面TransactionalHandler不僅代理了HelloServiceImpl的public方法hello(), 也成功代理了private方法privateHello(), 并且是由Spring Boot來控制的!

          經(jīng)過一大長串的花里胡哨的操作, 終于實現(xiàn)了在private方法上使@Transactional生效的效果了. 當然, 目前這只是理論上的生效,

          因為中間在模仿JdkDynamicAopProxy實現(xiàn)PrivateAopProxy的時候, 由于JdkDynamicAopProxy的切面實現(xiàn)邏輯非常復(fù)雜, 我們直接把切面寫死成了TransactionalHandler.

          但是本文的主要目的就是能夠在Spring Boot代理private方法, 只要能夠代理, 說明@Transactional事務(wù)生效也是完全能做到的.



          感悟


          "Service調(diào)用其他Service的private方法, @Transactional會生效嗎"
          如果僅僅回答問題本身是很簡單的, 只要了解Spring Boot的AOP原理即可. 但是也可以深入其中, 順著這個問題繼續(xù)研究,

          從前文Service調(diào)用其他Service的private方法, @Transactional會生效嗎(上)閱讀Spring Boot動態(tài)代理的功能源碼實現(xiàn), 到本文親手實現(xiàn)"特殊功能"的動態(tài)代理,

          不僅精通了Spring Boot動態(tài)代理的代碼實現(xiàn)流程, 還掌握了JDK的動態(tài)代理功能, 收益非常大!

          文中相關(guān)源碼:?private-proxy-source-code
          (https://github.com/zzzzbw/private-proxy-source-code)




          點擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開更多互動和交流。

          -?END -

          瀏覽 51
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大鸡吧网站在现看 | 狠狠操狠狠爽 | 爱爱爱爱爱视频 | 熟女作爱一区二区三区免费 | 日韩一区二区在线视频 |