fescar源码解析系列(一)之启动详解

  fescar是gts刚开源的版本,对gts关注已久,比较熟悉其原理,而半年前本身又开发了一个可用版本meepo(详情),因此对fescar的源码也是必看。经过比较,能够看meepo设计上的不足,以及一些编码细节上的困惑。javascript

  项目架构及环境搭建很少叙述,github官网已经够详细了,直接开始撸源码。java

    demo的使用上,和meepo大致一致,在须要分布式事务的方法上,加上事务注解便可。git

1、@GlobalTransactional注解的使用

很普通的dubbo程序,再看下purchase方法github

  和meepo惟一不一样的是,fescar使用了自定义注解@GlobalTransactional,meepo则是spring事务注解@Transactional。spring

  自定义注解表明了fescar弃用了JTA规范,本身承担TM的角色,好处是加入分布式事务后,不会影响到原来的事务实现。meepo默认会修改@Transactional标注的原单机事务实现,虽然不影响使用,但不算合理,能够手动指定transmanager解决,如@Transactional("transactionManager1")。坏处则是放弃了spring 已经实现了的成熟稳定JTA规范。数组

  如今问题来了,一样实现事务,一样用的注解,二者实现机制同样吗?先说答案,是同样的。本篇文章在讲解@GlobalTransactional注解的同时,也过了一遍spring的@Transactional。缓存

  先跟踪代码,在第一张图的的purchase处,打了一个断点,debug到时,发现service是cglib生成的一个代理。第一个问题来了,为何是cglib?spring的常识是,aop有接口的bean默认jdk动态代理,没接口的cglib代理,或者xml配置 proxy-target-class="true" 强制使用cglib。可是在本例中,bussinesService有接口,xml未配置,却为什么使用cglib代理?架构

  ok,这个问题一会再找找,咱们得先解决另一个重要的问题,代理类是何时生成的?框架

2、BeanPostProcessor接口

  这个问题好解答,就是BeanPostProcessor接口。fescar、dubbo不少框架包括spring自己,都有使用BeanPostProcessor来为xml中配置的bean生成代理,我写的meepo也有,这是spring很常见的一种扩展使用方式。分布式

  看一下spirng的生命周期。 

 

  在xml里配置的每个bean,在初始化的先后,会先找到缓存的BeanPostProcessor list,自己做为参数,传入BeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization方法,为bean作一些处理如生成代理类,而后返回。

  fercas的GlobalTransactionScanner承担了这个角色,GlobalTransactionScanner继承了spring aop包下的AbstractAutoProxyCreator类,这个类实现了InstantiationBeanPostProcessor接口,是spring实现aop最关键的一个抽象类,相似JTA事务里的AbstractPlatformTransactionManager。

3、spring aop的核心类AbstractAutoProxyCreator

  默认状况下,代理是经过AbstractAutoProxyCreator中的postProcessAfterInitialization()建立的。

上述方法的执行逻辑为:
a.获取缓存的bean名字,getCacheKey()方法其实是便于子类扩展。默认返回className_beanName
b.若earlyProxyReferences缓存中不包含当前cacheKey,则调用wrapIfNecessary()建立代理对象。
earlyProxyReferences这个缓存是干吗的呢?
从上下文中能够找到getEarlyBeanReference()方法中会将相应的cacheKey保存到earlyProxyReferences中,同时也会调用wrapIfNecessary()方法,所以能够看出该缓存用于保存已经建立过代理对象的cachekey,避免重复建立。代码以下:

  那么getEarlyBeanReference()方法又是什么时候被调用的呢?
  经过分析能够看到,getEarlyBeanReference()方法在SmartInstantiantionAwareBeanPostProcessor中声明,咱们知道,spring为了解决单例bean之间的循环依赖问题,提早将代理对象暴露出去。
  接下来是wrapIfNecessary方法,该方法是真正生成代理类的地方。GlobalTransactionScanner重写了该方法,咱们来看一下。

  主要逻辑有:

  a. 查看当前bean是否有@GlobalTransactional注解,若是没有,本身返回bean,再也不继续生成代理。

  b. new了 一个实现了MethodInterceptor接口的方法拦截器GlobalTransactionalInterceptor,相似jdk动态代理的InvocationHandler,关键逻辑所在

  !AopUtils.isAopProxy(bean) 判断是否已经是代理类,避免重复代理,若未被代理,则调用父类的wrapIfNecessary方法。

  AbstractAutoProxyCreator是全部使用spring aop的关键类,和GlobalTransactionScanner同样,spring自身的一些aop机制也使用了AbstractAutoProxyCreator。咱们看下@Transactional的使用,配置xml后在须要事务的方法加上注解便可,以下

<!-- 事务管理器配置,单数据源事务 -->
<bean id="pkgouTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="pkGouDataSource" />
</bean>
<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="pkgouTransactionManager" />

  tx标签初始化的时候,由对应的解析器AnnotationDrivenBeanDefinitionParser来解析,在解析方法parse中,会注册一个InfrastructureAdvisorAutoProxyCreator类,这个类继承了AbstractAutoProxyCreator。使用普通的aop标签,解析时也会注册一个继承AbstractAutoProxyCreator的AspectJAwareAdvisorAutoProxyCreator类。类结构图以下:

 

  

AbstractAutoProxyCreator这个抽象类,有一个惟一抽象方法getAdvicesAndAdvisorsForBean。该方法做用是为bean获取全部适用的Advice和Advisor。

4、AOP基础概念

Advice和Advisor以及PointCut都是aop都基础概念,简单理解下:

Advice 即通知,具体的处理逻辑,好比方法先后加日志,具体实现能够是MethodInterceptor的invoke方法;

PointCut 是要拦截的范围,好比哪一个包下的controller;

Advisor是对Advice和PointCut的一个封装,一个服务可能初始化了几个Advisor,那么bean初始化时,遍历这些Advisor,先判断本身是否在Advisor的PointCut范围内,若是是,则生成aop代理。

  AbstractAutoProxyCreator的类结构图能够看到,它有一个子类AbstractAdvisorAutoProxyCreator,同时是几个标签解析生成的Creator的父类,AbstractAdvisorAutoProxyCreator的做用是自动获取Advisor集合,咱们来看下它的getAdvicesAndAdvisorsForBean方法。

  findEligibleAdvisors处理两件事

  • 找到Spring中Advisor的实现类(findCandidateAdvisors)
  • 将全部拥有@Aspect注解的类转换为advisors(aspectJAdvisorsBuilder.buildAspectJAdvisors)
如今问题来了,Advisor的实现类哪里来?咱们看前面的标签解析类AnnotationDrivenBeanDefinitionParser的configureAutoProxyCreator方法的一个片断。

标签解析的中间,动态生成一个Advisor

  • 定义一个Advisor
  • 注入interceptor即Advice
  • 注册到spring容器
  以上几步后, findEligibleAdvisors方法就能够找到这个Advisor了。可能有人会有疑问,为何上面没有PointCut?这个后面会处理,没有PointCut时会生成一个TruePointCut,表示所有范围。毕竟@Transactional这种注解,并无限制使用的范围。

5、fescar的 GlobalTransactionScanner如何处理

   GlobalTransactionScanner并无继承AbstractAdvisorAutoProxyCreator,由于fercas的注解并无在xml里配置,也不须要自动找其余Advisor,它只要处理本身的拦截器就行了,看一下它的getAdvicesAndAdvisorsForBean方法。

  很简单,就是返回了以前在wrap里new的GlobalTransactionalInterceptor,GlobalTransactionalInterceptor实现MethodInterceptor,间接实现了Advice。继续跟踪wrap方法,获取Advice和Advisor后,返回Object[](这里是只有一个Advice)。接着把Object数组和bean,传入到createProxy方法,生成代理类。

这里关键是两步:

  • Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); 
  • proxyFactory.getProxy(getProxyClassLoader());

buildAdvisors把Advice和Advisor的Object[] 混合物,处理后统一成Advisor[],放入new的一个ProxyFactory中,proxyFactory继续生成代理类。

咱们看下buildAdvisors关键代码

  若是Object[]里的元素是Advisor,直接返回,若是是Advice并且是MethodInterceptor,包装成一个DefaultPointcutAdvisor,这个Advisor的构造方法,生成一个默认的TruePointcut,也就是前面提到的所有范围。

继续看proxyFactory.getProxy(getProxyClassLoader()) 这句,终于到了选择代理的地方,咱们看下。

  这里传入的config的属性拷贝自GlobalTransactionScanner的属性(点进方法能够看到,不贴代码了),当optimize或者ProxyTargetClass任一属性是true的时候,会进入方法体,进一步判断当前bean是不是接口,注意,这里不是判断是否实现接口,显然,业务service是一个class,这时候,就会使用cglib来生成代理类。咱们来看下GlobalTransactionScanner构造方法。

  终于能够解答了前面的问题,为何是cglib的代理类。和xml配置效果同样,只要找到这个属性配置的地方就行了,若是没配,默认是false。

  最后,动态代理的生成过程再也不叙述,代理类(jdk动态代理)执行业务接口方法的路径是

$proxy.purchase -> InvocationHandler.invoke ->intercept.invoke 

  cglib代理类的路径没看,应该大致一致。

总结

  本篇主要讲解了@GlobalTransactional 生成代理类的整个过程,其实和fercas框架关系不大,由于其原理和spring的@transactionanl注解基本同样(@transactional详解

  整理下咱们的思路,方法上的自定义注解,会为方法所在的类生成aop代理,方法先后加上一些自定义逻辑,好比fescar的全局事务管理。生成代理的路径以下:

  GlobalTransactionScanner.postProcessAfterInitialization->GlobalTransactionScanner.wrapIfNecessary->AbstractAutoProxyCreator.wrapIfNecessary->GlobalTransactionScanner.getAdvicesAndAdvisorsForBean->AbstractAutoProxyCreator.createProxy->AbstractAutoProxyCreator.buildAdvisors->proxyFactory.getProxy

  再总结一下,若是咱们想使用自定义注解生成AOP代理,须要哪些步骤:

  • 自定义注解
  • 编写Intercept类
  • 编写代理生成类继承 AbstractAutoProxyCreator,xml中配置该bean
  • 重写getAdvicesAndAdvisorsForBean 定义遍历范围,效果同xml中aop配置的PointCut
  • 重写wrapIfNecessary ,过滤非本身定义注解的bean
相关文章
相关标签/搜索