深入理解Spring系列之十二:@Transactional是如何工作的
首先从tx:annotation-driven/说起。配置了tx:annotation-driven/,就必定有对应的标签解析器类,查看NamespaceHandler接口的实现类,可以看到一个TxNamespaceHandler,它注册了AnnotationDrivenBeanDefinitionParser对annotation-driven元素进行解析。
public class TxNamespaceHandler extends NamespaceHandlerSupport {
static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";
static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
public TxNamespaceHandler() {
}
static String getTransactionManagerName(Element element) {
return element.hasAttribute("transaction-manager") ? element.getAttribute("transaction-manager") : "transactionManager";
}
public void init() {
this.registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
// <tx:annotation-driven/> 标签解析器
this.registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
this.registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
}
}
进入AnnotationDrivenBeanDefinitionParser类,重点看parse方法。
public BeanDefinition parse(Element element, ParserContext parserContext) {
this.registerTransactionalEventListenerFactory(parserContext);
String mode = element.getAttribute("mode");
if ("aspectj".equals(mode)) {
this.registerTransactionAspect(element, parserContext);
} else {
AnnotationDrivenBeanDefinitionParser.AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
}
return null;
}
从代码中可以看出,如果<tx:annotation-driven/>中没有配置mode参数,则默认使用代理模式进行后续处理;如果配置了mode=aspectj,则使用aspectj代码织入模式进行后续处理。
本篇分析使用代理模式的代码,进入AopAutoProxyConfigurer.configureAutoProxyCreator方法。
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
// 重点代码
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
String txAdvisorBeanName = "org.springframework.transaction.config.internalTransactionAdvisor";
if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
Object eleSource = parserContext.extractSource(element);
RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
sourceDef.setSource(eleSource);
sourceDef.setRole(2);
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(2);
AnnotationDrivenBeanDefinitionParser.registerTransactionManager(element, interceptorDef);
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
advisorDef.setSource(eleSource);
advisorDef.setRole(2);
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
if (element.hasAttribute("order")) {
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
}
parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));
parserContext.registerComponent(compositeDef);
}
}
上面的代码中标出了一行核心代码,容易被忽略。进入 AopNamespaceUtils.registerAutoProxyCreatorIfNecessary 方法。
public static void registerAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
// 重点代码
BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
重点关注上面标出的代码,进入AopConfigUtils.registerAutoProxyCreatorIfNecessary方法。
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
上面中的代码向Spring容器中注册了一个InfrastructureAdvisorAutoProxyCreator类。可能会疑问为什么要注册这个类,有什么作用?
查看InfrastructureAdvisorAutoProxyCreator类继承关系。

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
// 重点代码: 是创建代理对象的核心方法
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
其中 wrapIfNecessary 方法 是创建代理对象的核心方法。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
//重点代码:获取事务属性切面
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 重点代码: 创建一个AOP代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
getAdvicesAndAdvisorsForBean方法会遍历容器中所有的切面,查找与当前实例化bean匹配的切面,这里就是获取事务属性切面,查找@Transactional注解及其属性值,具体实现比较复杂,这里暂不深入分析,最终会得到 BeanFactoryTransactionAttributeSourceAdvisor 实例,然后根据得到的切面进入createProxy方法,创建一个AOP代理。
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
ProxyFactory proxyFactory = new 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);
}
//重点代码
return proxyFactory.getProxy(getProxyClassLoader());
}
进入ProxyFactory.getProxy方法。
public Object getProxy(ClassLoader classLoader) {
// 重点代码:进入 createAopProxy() 方法
return createAopProxy().getProxy(classLoader);
}
createAopProxy方法决定使用JDK还是Cglib创建代理。createAopProxy方法代码如下:
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
// 重点代码: 进入 createAopProxy 方法
return getAopProxyFactory().createAopProxy(this);
}
@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.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
可以看出默认是使用JDK动态代理创建代理,如果目标类是接口,则使用JDK动态代理,否则使用Cglib。这里分析使用JDK动态代理的方式,进入JdkDynamicAopProxy.getProxy方法。
@Override
public Object getProxy() {
return getProxy(ClassUtils.getDefaultClassLoader());
}
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//重点代码
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
可以看到很熟悉的创建代理的代码Proxy.newProxyInstance。这里要注意的是,newProxyInstance方法的最后一个参数是JdkDynamicAopProxy类本身,也就是说在对目标类进行调用的时候,会进入JdkDynamicAopProxy的invoke方法。这里只关注JdkDynamicAopProxy的invoke方法的重点代码。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// Get the interception chain for this method.
// 重点代码
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
// 重点代码
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
// 重点代码
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
深入理解Spring系列之十二:@Transactional是如何工作的的更多相关文章
- 深入理解Spring系列之十:DispatcherServlet请求分发源码分析
转载 https://mp.weixin.qq.com/s/-kEjAeQFBYIGb0zRpST4UQ DispatcherServlet是SpringMVC的核心分发器,它实现了请求分发,是处理请 ...
- 深入理解Spring系列之二:BeanDefinition解析
转载 https://mp.weixin.qq.com/s?__biz=MzI0NjUxNTY5Nw==&mid=2247483814&idx=1&sn=ddf49931d55 ...
- webpack4 系列教程(十二):处理第三方JavaScript库
教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步<webpack4 系列教程(十二):处理第三方 JavaScript 库>原文地址.或者来我的小站看更多内容:godbm ...
- Java 设计模式系列(十二)策略模式(Strategy)
Java 设计模式系列(十二)策略模式(Strategy) 策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换.策略模式使得算法可以 ...
- CRL快速开发框架系列教程十二(MongoDB支持)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- spring boot / cloud (十二) 异常统一处理进阶
spring boot / cloud (十二) 异常统一处理进阶 前言 在spring boot / cloud (二) 规范响应格式以及统一异常处理这篇博客中已经提到了使用@ExceptionHa ...
- Spring Cloud(十二):分布式链路跟踪 Sleuth 与 Zipkin【Finchley 版】
Spring Cloud(十二):分布式链路跟踪 Sleuth 与 Zipkin[Finchley 版] 发表于 2018-04-24 | 随着业务发展,系统拆分导致系统调用链路愈发复杂一个前端请 ...
- OSGi 系列(十二)之 Http Service
OSGi 系列(十二)之 Http Service 1. 原始的 HttpService (1) 新建 web-osgi 工程,目录结构如下: (2) HomeServlet package com. ...
- 深入理解Spring系列之七:web应用自动装配Spring配置
转载 https://mp.weixin.qq.com/s/Lf4akWFmcyn9ZVGUYNi0Lw 在<深入理解Spring系列之一:开篇>的示例代码中使用如下方式去加载Spring ...
随机推荐
- 小程序 setData() 方法
setData() 参数格式 字段 类型 必填 描述 最低版本 data Object 是 这次要改变的数据 callback Function 否 回调函数 1.5.0 callback 是一个 ...
- Web模块:spring-boot-starter-web
spring-boot-autoconfigure-1.5.1.RELEASE.jar!/org/springframework/boot/autoconfigure/web 上述jar的web包下, ...
- 秒杀多线程第八篇 经典线程同步 信号量Semaphore (续)
java semaphore实现: Semaphore当前在多线程环境下被扩放使用,操作系统的信号量是个很重要的概念,在进程控制方面都有应用.Java 并发库 的Semaphore 可以很轻松完成信号 ...
- 题解 P1781 【宇宙总统】
小金羊发现用的方法和python大佬们的方法还是不一样... (大概是我太弱了qAq) emmm... (Mode:Python 3)Code: a=int(input()) #几个数 L=list( ...
- 《Linux Shell 脚本攻略》读书笔记
本书主要讲解一些linux shell命令的用法,讲解一些shell的奇技淫巧. 第一章 小试牛刀 主要介绍一些基本shell指令 终端打印:echo.printf 别名:alias 终端处理工具:t ...
- 802.1p 优先级与内部优先级的映射关系
缺省情况下,所有华为 S 系列交换机的 802.1P 优先级 与内部优先级的映射关系是 一 样的,如表 10-3 所示.从中可以看出,这些交换机中 802.1p 优先级与内部优先级的缺省映射关系是按等 ...
- SAM I AM UVA - 11419(最小顶点覆盖+输出一组解)
就是棋盘问题输出一组解 https://blog.csdn.net/llx523113241/article/details/47759745 http://www.matrix67.com/blog ...
- 【BZOJ3555】企鹅QQ(字符串哈希)
[BZOJ3555]企鹅QQ(字符串哈希) 题面 BZOJ 题解 把前缀哈希一下,后缀哈希一下 枚举哪个位置不选,然后检查一下相同就行了.. 为什么我的\(Hash\)老是\(WA\), 为什么\(Z ...
- NAT网络地址转换模拟过程
原理图,如图1 图1 以下为配置NAT网络地址转换的实验: eNSP模拟图,如图2 图2 Step1.给路由器的每个接口赋予一个地址,如图3,图4 图3 图4 AR1和AR2中添加路由表项,如图5,图 ...
- linux 递归删除目录文件
比如删.svn文件 >find . -name ".svn" | xargs -exec rm -rf