先给一段代码:
publicintsave(Stringname, intage) throwsException{
insert(name, age);
return1;
}
@Transactional
publicvoidinsert(Stringname, intage){
jdbcTemplate.update("insert into user(id,name,age)values(1,'"+name+"',"+age+")");
jdbcTemplate.update("insert into user(id,name,age)values(2,'"+name+"',"+age+")");
jdbcTemplate.update("insert into user(id,name,age)values(1,'"+name+"',"+age+")");
}
我们知道,这样使用事务是不会生效的,那么其原因是什么呢?
解析
先说结论,在测试层面上:
• 如果一个bean的方法或者类上有@Transactional注解的话,那么这个Bean会被CGLib生成动态代理,在注入这个bean并调用它的方法的时候,会被DynamicAdvisedInterceptor类拦截,如果被调用的方法有@Transactional注解,就会被事务拦截器拦截。如下代码,会走到AOP拦截中生效
@RestController
public class ThirdApp {
public String test(){
return "test";
}
@Transactional
public void tx(){
}
}
@RestController
public class SecondApp {
@Autowired
ThirdApp thirdApp;
@RequestMapping("/third")
@ResponseBody
public String third(@RequestParam("name") String name,
@RequestParam("age") Integer age,
@RequestParam("id") Integer id){
thirdApp.test();
return "tx";
}
}
• 如果一个bean方法上没有@Transactional注解,那么这个Bean就是普通的bean,不会被AOP拦截,不被拦截。下面的代码与上面相比就是在ThirdApp内部没加@Transactionnal注解
@RestController
public class ThirdApp {
public String test(){
return "test";
}
public void tx(){
}
}
@RestController
public class SecondApp {
@Autowired
ThirdApp thirdApp;
@RequestMapping("/third")
@ResponseBody
public String third(@RequestParam("name") String name,
@RequestParam("age") Integer age,
@RequestParam("id") Integer id){
thirdApp.test();
return "tx";
}
}
• 如果在当前的Bean中,未加上事务注解的方法调用加上@Transactional注解的事务,不会生效,因为AOP拦截在反射层面上,直接方法调用是不会被拦截的。如题目。
源码层面
1. 在Spring项目启动的时候,如果注解使用了@EnableTransactionManagement,那么生成三个Bean。
TransactionAttributeSource->AnnotationTransactionAttributeSource->SpringTransactionAnnotationParser
2. 在启动创建Bean的时候,顺序如下图,其中AbstractAutoProxyCreator.getAdvicesAndAdvisorsForBean 用来判断当前类是否需要进行代理。
3 有配置需要创建动态代理的地方,比如加上了@Transactional注解的,在Spring的容器内存储的是动态代理。
4. 在调用方法的时候,我们会从Spring的容器内获取对应的bean,如果在bean的内部直接进行方法调用而不是通过反射则不走AOP。AOP拦截的是反射调用,而配置了需要代理的Bean,用xxx.xxxmethod时走的是反射。原因继续往下看
5. 使用CGLib创建的代理对象ObjenesisCglibAopProxy是CglibAopProxy的子类。
// 创建动态代理对象的时候初始化工作
publicCglibAopProxy(AdvisedSupport config) throwsAopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
if(config.getAdvisors().length== 0&& config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
thrownewAopConfigException("No advisors and no TargetSource specified");
}
this.advised= config;
this.advisedDispatcher= newAdvisedDispatcher(this.advised);
}
6. 在创建AopProxy后进行getProxy, 这部分代码用到的CGlib的Enhancer,关于CGlib的详细用法后续再说,这段代码会使用CGlib创建动态代理,并指定拦截器,在执行方法的时候被拦截器进行拦截。
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
if(ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for(Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass, classLoader);
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if(classLoader != null) {
enhancer.setClassLoader(classLoader);
if(classLoader instanceofSmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(newClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = newClass<?>[callbacks.length];
for(intx = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(newProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
returncreateProxyClassAndInstance(enhancer, callbacks);
7. 默认的使用DynamicAdvisedInterceptor拦截器进行拦截,其内部获取方法是否有对应的Advice(增强)进行处理, 如果有增强的话,则使用增强进行处理
8. 在处理的时候会用TransactionInterceptor进行处理。而TransactionInterceptor的具体处理,我们之前有提到过,参考Spring事务原理分析
9. 到这一步就走到事务拦截器内部,交给事务进行处理了。其中有一步关键的方法是判断当前的方法是否有Advice增强的。代码比较关键,处理方法是否有Advice的
DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass)
最后
结合之前提到的SpringBean生命周期,事务原理分析,可以知道为什么上面的代码事务不生效了。
参考
• 探索Spring
注:
本文独家发布自金蝶云社区