Service 调用其他 Service 的 private 方法,@Transactional 会生效吗(下)
Spring Boot
2020-10-26
3505
3
前情提要:
在 Service 调用其他 Service 的 private 方法,@Transactional 会生效吗(上) 中证明了动态代理不会代理 private 方法的,并通过阅读源码证实了。
但是我们可以自己实现一个动态代理功能替代
Spring Boot
中原有的,达到动态代理 private 方法的目的。主要流程为:
- 重新实现一个
ProxyGenerator.generateClassFile()
方法,输出带有 private 方法的代理类字节码数据- 把字节码数据加载到 JVM 中,生成 Class
- 替代
Spring Boot
中默认的动态代理功能,换成我们自己的动态代理。
前置代码
首先,要实现代理目标类的 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
注解的类,然后解析类里面的方法以及切点等。 为了简便实现,就不实现扫描解析的功能了,这里直接模仿前文的TransactionalAop
的功能实现切面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;
}
}
生成字节码数据
根据阅读ProxyGenerator.generateProxyClass()
方法生成字节码的代码可以知道,动态代理的功能实际上就是动态的生成类的字节码,通过新生成的字节码的类替代原有的类。 那我们只要模仿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()
方法里完成。
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();
}
//3、将 java 文件编译成 class 文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> iterable = manage.getJavaFileObjects(f);
JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);
task.call();
manage.close();
f.delete();
// 4、将 class 加载进 jvm
Class<?> proxyClass = loader.loadClass(proxyName);
// 通过构造方法生成代理对象
Constructor<?> constructor = proxyClass.getConstructor(PrivateProxyInvocationHandler.class);
return constructor.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
PrivateProxy
通过调用PrivateProxyGenerator.generateProxyClass()
获取到代理类的。java 文件的字符串,然后输出到 java 文件中,再编译成。class 文件。 接着通过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
的代理流程中
AopProxy
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().
AbstractAdvisorAutoProxyCreator
实现了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
.
引入到 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();
}
}
现在,再跑一下最开头的单元测试!
从单元测试的结果看到,切面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 会生效吗(下) zzbw.cn/post/23)