Service調用其餘Service的private方法, @Transactional會生效嗎(下)

前情提要:java

在Service調用其餘Service的private方法, @Transactional會生效嗎(上)中證實了動態代理不會代理private方法的, 並經過閱讀源碼證明了.git

可是咱們能夠本身實現一個動態代理功能替代Spring Boot中原有的, 達到動態代理private方法的目的.github

主要流程爲:spring

  1. 從新實現一個ProxyGenerator.generateClassFile()方法, 輸出帶有private方法的代理類字節碼數據
  2. 把字節碼數據加載到JVM中, 生成Class
  3. 替代Spring Boot中默認的動態代理功能, 換成咱們本身的動態代理.

前置代碼

首先, 要實現代理目標類的private方法的目標, 必需要能拿到被代理類的實例, 因此先改裝切面InvocationHandler, 把要被代理的類保存下來. .app

@Getter
public abstract class PrivateProxyInvocationHandler implements InvocationHandler {

    private final Object subject;

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

前文的切面TransactionalAopSpring BootJdkDynamicAopProxy中掃描被@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裏.

加載到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);
    }
}

PrivateProxyTests_IMG

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

以上功能咱們經過實現PrivateProxyGeneratorPrivateProxy兩個類, 實現了JDK的動態代理功能, 而且還能代理private方法. 接下來就要考慮如何把Spring Boot裏的動態代理功能替換成咱們本身的.

替代Spring Boot默認動態代理

上面經過模仿JDK的動態代理, 本身實現了一個能代理private方法的動態代理功能.
如今爲了讓@Transactional註解能對private方法生效, 就要把自定義的動態代理方法嵌入到Spring Boot的代理流程中

AopProxy

Spring Boot中自帶的兩種動態代理方式爲JDK和Cglib, 對應的實現類是JdkDynamicAopProxyObjenesisCglibAopProxy, 這兩個類都是實現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().

AbstractAdvisorAutoProxyCreator

實現了PrivateAopProxy類, 再考慮如何把他替換掉Spring Boot中的JdkDynamicAopProxyObjenesisCglibAopProxy.
這兩種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;
    }
}

既然AopProxyFactoryProxyFactory的一個變量, 那麼如今看一下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是一個抽象類而且繼承的類和實現的接口比較多, 因此這邊我先查看了一下其整個的類結構圖(只顯示了重要的接口).

AbstractAutoProxyCreator類圖_IMG

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

其實現了org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor.
SmartInstantiationAwareBeanPostProcessor繼承InstantiationAwareBeanPostProcessor,而InstantiationAwareBeanPostProcessor繼承BeanPostProcessor.

這三個接口是Spring用於建立Bean時的加強功能, 是Spring的IOC和AOP實現的核心思想, 建議你們都去了解一下, 這裏就不詳細講解了,
只要知道AbstractAutoProxyCreator實現了SmartInstantiationAwareBeanPostProcessor的接口, 因此能在建立Bean的時候對其進行代理.

接着, 看一下其子類.

其直接子類有AbstractAdvisorAutoProxyCreatorBeanNameAutoProxyCreator.
這兩個類的主要區別爲切點的不一樣, 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.aopProxyFactoryPrimaryAopProxyFactory的代碼: proxyFactory.setAopProxyFactory(new PrimaryAopProxyFactory());就完成了PrivateProxyAdvisorAutoProxyCreator.

引入到Bean中

接下來就是把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();
    }
}

如今, 再跑一下最開頭的單元測試!

結果圖_IMG

從單元測試的結果看到, 切面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

原文地址:Service調用其餘Service的private方法, @Transactional會生效嗎(下)

相關文章
相關標籤/搜索