@Transactional标签用于Spring中的事务标记。 
先简单说下几个概念: 
1)Spring的AOP是应用于Spring管理的bean上并基于代理实现的,代理的方法有java动态代理和CGLib动态代理方式,在不进行任何设置时,如果一个被代理的类实现了接口,就优先使用java动态代理。 
2)Spring在bean的创建过程中将代理应用于bean上,代理的创建有这么一个重要的类AbstractAutoProxyCreator,这个类用于自动代理的创建,而这个类实现的接口中有BeanPostProcessor这个接口。但是这个类是一个抽象类,因为系统会根据不同的配置方式创建不同的子类: 
对于aop:config的标签,创建的是AspectJAwareAdvisorAutoProxyCreator类,对于aspectj-autoproxy标签,创建的是AnnotationAwareAspectJAutoProxyCreator类。其继承结构如下: 

3)BeanPostProcessor的几个方法都是钩子函数,Spring容器管理的bean在创建过程中会调用实现了BeanPostProcessor的类的几个方法,用于修改创建的bean的实例。 
4)Spring中aop采用了aopalliance的约定,Advice是一个标记接口,其子接口为Interceptor,事务的实现类就是一个Interceptor。Advisor接口用来包含Advice和其他一些信息,分为IntroductionAdvisor和PointcutAdvisor两个分支,前者只能应用于类层面。事务管理中的Advisor类是BeanFactoryTransactionAttributeSourceAdvisor,是PointcutAdvisor的一个分支。 
5)事务的Interceptor为TransactionInterceptor

那么我们从AbstractAutoProxyCreator作为入口进行源代码的分析: 
首先看实现的BeanPostProcessor的钩子函数

    /*** Create a proxy with the configured interceptors if the bean is* identified as one to proxy by the subclass.* @see #getAdvicesAndAdvisorsForBean*/public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (!this.earlyProxyReferences.containsKey(cacheKey)) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}

这个方法的注释说明了这个方法的作用是:创建代理并应用所配置的interceptor于其上。 
沿着这个方法继续查看wrapIfNecessary方法的代码片断:

            // Create proxy if we have advice.// 查找这个bean的adive和advisorObject[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {// 加入缓存this.advisedBeans.put(cacheKey, Boolean.TRUE);// 创建代理Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}

可以发现,系统将查找应用于这个bean上的advice和advisor,然后根据此创建代理。getAdvicesAndAdvisorsForBean这个方式是一个模板方法,具体的实现由子类AbstractAdvisorAutoProxyCreator实现。 
子类中getAdvicesAndAdvisorsForBean调用了findEligibleAdvisors函数:

    //找到所有advisorList<Advisor> candidateAdvisors = findCandidateAdvisors(); //过滤适用于这个bean的advisorList<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);

findCandidateAdvisors会查找系统中所有实现了Advisor接口的bean。 
然后findAdvisorsThatCanApply在所有Advisor中过滤适用于当前正在创建的bean的Advisor。 
我们追踪findAdvisorsThatCanApply的函数调用,会发现此功能委托给了AopUtils.findAdvisorsThatCanApply函数,查找PointcutAdvisor类型的过滤,定位到AopUtils类的如下函数:

    public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {Assert.notNull(pc, "Pointcut must not be null");if (!pc.getClassFilter().matches(targetClass)) {return false;}MethodMatcher methodMatcher = pc.getMethodMatcher();IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;if (methodMatcher instanceof IntroductionAwareMethodMatcher) {introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;}Set<Class> classes = new LinkedHashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));classes.add(targetClass);//扫描targetClass(即创建过程的bean所在的类)实现的所有接口和其自身类的每个方法for (Class<?> clazz : classes) {Method[] methods = clazz.getMethods();for (Method method : methods) {if ((introductionAwareMethodMatcher != null &&introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||methodMatcher.matches(method, targetClass)) {return true;}}}return false;}

上述代码中:Pointcut pc参数是PointcutAdvisor保存的切点的信息,对于BeanFactoryTransactionAttributeSourceAdvisor,它是TransactionAttributeSourcePointcut。MethodMatcher是切点的属性,用于判断一个方法是否适用于advice,TransactionAttributeSourcePointcut在继承体系上实现了MethodMatcher接口,将返回this。上述代码中,methodMatcher.matches(method, targetClass)即是TransactionAttributeSourcePointcut的match方法的调用,代码如下:

    public boolean matches(Method method, Class targetClass) {TransactionAttributeSource tas = getTransactionAttributeSource();return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);}

此时会从目标类和方法中获取TransactionAttribute,它包含了事务的定义,对于以标签形式定义的事务,它是AnnotationTransactionAttributeSource。查看AnnotationTransactionAttributeSource的getTransactionAttribute方法,它的定义位于父类:

    public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {// First, see if we have a cached value.Object cacheKey = getCacheKey(method, targetClass);Object cached = this.attributeCache.get(cacheKey);if (cached != null) {// Value will either be canonical value indicating there is no transaction attribute,// or an actual transaction attribute.if (cached == NULL_TRANSACTION_ATTRIBUTE) {return null;}else {return (TransactionAttribute) cached;}}else {// We need to work it out.TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);// Put it in the cache.if (txAtt == null) {this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);}else {if (logger.isDebugEnabled()) {Class<?> classToLog = (targetClass != null ? targetClass : method.getDeclaringClass());logger.debug("Adding transactional method '" + classToLog.getSimpleName() + "." +method.getName() + "' with attribute: " + txAtt);}this.attributeCache.put(cacheKey, txAtt);}return txAtt;}}

系统将先从Map的缓存查找,若没有,则调用 
computeTransactionAttribute,这个方法定义也位于父类:

    private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {// Don't allow no-public methods as required.if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}// Ignore CGLIB subclasses - introspect the actual user class.Class<?> userClass = ClassUtils.getUserClass(targetClass);// The method may be on an interface, but we need attributes from the target class.// If the target class is null, the method will be unchanged.Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);// If we are dealing with method with generic parameters, find the original method.specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);// First try is the method in the target class.TransactionAttribute txAtt = findTransactionAttribute(specificMethod);if (txAtt != null) {return txAtt;}// Second try is the transaction attribute on the target class.txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());if (txAtt != null) {return txAtt;}if (specificMethod != method) {// Fallback is to look at the original method.txAtt = findTransactionAttribute(method);if (txAtt != null) {return txAtt;}// Last fallback is the class of the original method.return findTransactionAttribute(method.getDeclaringClass());}return null;}

这个函数很好的说明了@Transactional标签的查找顺序。 
我们查看方法上的@Transactional标签的查找,调用归为自身的如下方法:

    protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {for (TransactionAnnotationParser annotationParser : this.annotationParsers) {TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);if (attr != null) {return attr;}}return null;}

可见,@Transactional标签的解析是由TransactionAnnotationParser接口列表解析完成的,这个接口列表默认只有SpringTransactionAnnotationParser。SpringTransactionAnnotationParser的parseTransactionAnnotation方法将解析Transactional标签并将其属性转换到TransactionAttribute的实现类RuleBasedTransactionAttribute。 
到此为止,对于具有TransactionAttribute的解析完成并加入缓存。 
对于当前bean适用的advisor也判断完成,回到AbstractAutoProxyCreator的wrapIfNecessary方法,如果存在advisor的话,将调用createProxy方法,创建代理的过程最终会归结为CglibAopProxy和JdkDynamicAopProxy两个类上,分别对应两种类型的代理的创建。 
代理创建好后,执行代理上的一个方法将会执行特定的代理函数,对于CglibAopProxy创建的代理,这个方法是CglibAopProxy内部的DynamicAdvisedInterceptor类的intercept方法,对于JdkDynamicAopProxy为invoke方法。 
这两个方法中都会获取interception链:

                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

然后追踪函数,发现系统先从缓存中获取,若缓存不存在则调用DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice方法,贴一段这个方法的代码片段:

            if (advisor instanceof PointcutAdvisor) {// Add it conditionally.PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {MethodInterceptor[] interceptors = registry.getInterceptors(advisor);MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {if (mm.isRuntime()) {// Creating a new object instance in the getInterceptors() method// isn't a problem as we normally cache created chains.for (MethodInterceptor interceptor : interceptors) {interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));}}else {interceptorList.addAll(Arrays.asList(interceptors));}}}}

这里获取的MethodInterceptor[] interceptors是TransactionInterceptor。 
而且又看到了MethodMatcher!只是将比较放在了MethodMatchers.matches中了而已:

    public static boolean matches(MethodMatcher mm, Method method, Class<?> targetClass, boolean hasIntroductions) {Assert.notNull(mm, "MethodMatcher must not be null");return ((mm instanceof IntroductionAwareMethodMatcher &&((IntroductionAwareMethodMatcher) mm).matches(method, targetClass, hasIntroductions)) ||mm.matches(method, targetClass));}

上述方法如果判定匹配的话,则将interceptors返回。 
TransactionInterceptor的invoke方法将在代理对象的方法被调用前调用:

    public Object invoke(final MethodInvocation invocation) throws Throwable {// Work out the target class: may be {@code null}.// The TransactionAttributeSource should be passed the target class// as well as the method, which may be from an interface.Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// Adapt to TransactionAspectSupport's invokeWithinTransaction...return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {public Object proceedWithInvocation() throws Throwable {return invocation.proceed();}});}

看到这里,对于在代理对象上调用一个方法时事务是否作用以及如何作用相信已经清楚了

关于proxy模式下,@Transactional标签在创建代理对象时的应用相关推荐

  1. 使用cglib创建代理对象

    在上一篇文章中,我讨论了基于标准Java的代理对象. 当您要在实现接口的对象上具有方法调用处理程序时,可以使用这些方法. Java反射代理的创建要求您具有一个实现接口的对象. 我们要代理的对象已经失控 ...

  2. 【设计模式】代理模式 ( 动态代理使用流程 | 创建目标对象 | 创建被代理对象 | 创建调用处理程序 | 动态创建代理对象 | 动态代理调用 )

    文章目录 前言 一.静态代理的弊端 二.动态代理的优势 三.动态代理使用流程 1.目标对象接口 2.被代理对象 3.调用处理程序 4.客户端 四.动态生成 代理对象 类 的 字节码 文件数据 前言 代 ...

  3. 【Android】高德地图在Debug模式下运行正常但是打Release包时则闪退解决办法

    [Android]高德地图在Debug模式下运行正常但是打Release包时则闪退解决办法 来源: https://blog.csdn.net/weixin_39370093/article/deta ...

  4. Spring AOP源码(2)—AspectJAwareAdvisorAutoProxyCreator创建代理对象【两万字】

      基于最新Spring 5.x,介绍了Spring AOP中的AspectJAwareAdvisorAutoProxyCreator自动代理创建者的工作流程,对于创建代理对象的源码进行了深度分析! ...

  5. java动态创建代理对象

    代理模式在开发过程中有着众多好处,现在我先给大家首先介绍什么是代理模式,以及java的中代理模式的使用,最后再看代理模式的概念作用等自然就会明白了 1.动态创建代理对象 >>>> ...

  6. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  7. 使用djcproxy创建代理对象

    在过去的几周中,我展示了如何使用Java Reflection API和cglib创建代理对象. 在本文中,我将向您展示如何使用djcproxy做到这一点. 哦,不是,另一个代理实现! 除了我创建此代 ...

  8. cglib创建代理对象(1)

    cglib创建代理对象 还是从一个的小demo开始 例子 被代理的类 public class Bean{public String sayHello(String name) {return &qu ...

  9. MyBatis运行原理(三)接口式编程及创建代理对象原理分析

    一.面向接口开发步骤 定义代理接口,将操作数据库的方法定义在代理接口中. 在SQL 映射文件中编写SQL 语句. 将SQL 映射文件注册在MyBatis 的全局配置文件中. 编写测试代码. 二.环境准 ...

最新文章

  1. 主机通过sftp传输文件到某台服务器ubuntu虚拟机出现:open for write: permission denied
  2. Android测试框架-uiautomator
  3. int (*a)[10] 和 int *a[10] 的区别
  4. OpenCV油画效果
  5. 然之协同系统3.5(OA+CRM+CASH+TEAM)
  6. 【计算机网络复习 数据链路层】3.4.2 停止-等待协议
  7. oracle within的用法,Oracle的 listagg() WITHIN GROUP ()函数使用
  8. JavaScript:异步执行机制
  9. 博文荐书:阿里运维、Java微服务、Scala编程
  10. YOLOv5目标检测算法——通俗易懂的解析
  11. c语言函数实验总结小说,大一上期C语言实验报告7函数
  12. 【LaTex-错误和异常】\verb ended by end of line.原因是因为闭合边界符没有在\verb命令所属行中出现;\verb命令的正确和错误用法、verbatim环境的用法
  13. pandas 获取Dataframe元素值的几种方法
  14. SLC NAND FLASH的物理结构
  15. 电磁场与电磁波-2-恒定电场
  16. greenplum使用总结之常用SQL及函数
  17. 163电子邮箱能免费注册吗?163电子邮件注册移动办公解决方案
  18. java date 日期格式_如何将JAVA DATE类型的日期 转换成指定格式类型的 (如:YYYY-MM-DD) 的 DATE类型数据?...
  19. auto盘病毒清除器.bat
  20. 尾巴大叔回归“大叔”

热门文章

  1. 雷电交加,雨水倾泻--北京
  2. Redis-过期Key删除/淘汰Kry策略
  3. 如何简单快速的了解区块链技术(结尾有惊喜)
  4. 执行计划级别mysql 2ef,Mysql 层级、执行顺序、执行计划分析
  5. jvm面试2 jvm如何加载java代码? JVM知识重点:内存模型和GC
  6. 干货 一文看尽 mysql
  7. ES6函数相关包含箭头函数
  8. 小细节决定大人生 或 对于细节的在意程度决定你人生到达的高度 或 对于细节的把控决定你是否比水平大致相同的人优秀与否 + 做事要带点脑子
  9. android真机单元测试,Android 单元测试入门
  10. Linux日志收集logrotate原理介绍