Spring Aop 精简版

1.切面术语

  • Aspect【切面】:横切多个类的关注点的模块化。事务管理是企业Java应用程序中横切关注点的一个很好的例子。在Spring AOP中,切面是通过使用标准类(基于schema的风格)或使用@Aspect注解标注的标准类(@AspectJ风格)来实现的。
  • Join point【连接点】:Spring并没有抽象自己的连接点,而是复用AspectJ第三方包的JointPoint。切面规范并不是由Spring提出的,它只是一个包装者(基于AspectJ这个第三方包进行包装)。标准的定义中,JointPoint【连接点】是应用程序执行过程中的一个候选点,可以在该候选点插入切面。该候选点可以是方法调用时、异常抛出时,甚至可以是属性字段被修改时。我们可以在这些JointPoint【连接点】将切面的代码插入到应用程序的正常流程中,以提供新的行为。But:Spring只提供了狭义的JointPoint【连接点】,即Spring仅仅支持方法调用时这唯一的JointPoint【连接点】类型!!
  • Advice【增强/通知/建言】:这个词的中文表述很多,我们不纠结于用哪个表述。Advice表示的是切面【Aspect】在特定连接点【Join Point】上采取的操作。Advice有多种类型:@Before、@After、@Around。许多AOP框架(包括Spring)将Advice建模为拦截器,在连接点【Join Point】周围维护了一个拦截器链【chain of interceptors 】。(具体而言,@Aspect声明了一个切面,它只是一个标准的Java类,而在程序执行到了连接点【Join Pont】时,我们需要将切面插入其中,这就要求切面需要有行为,Advice即为切面的行为。为了更细致的控制切面,Spring提供了多种类型的Advice,并在底层以拦截器的模式实现它们,这样在进入连接点【Join Point】时,就链式拦截,以调用适当的Advice进行增强 )
  • Pointcut【切入点】:它是一个匹配连接点【Join Point】的谓词(谓词就是一个条件表达式)。Advice与切入点【Pointcut】表达式相息息相关,并在切入点【Pointcut】匹配的任何连接点【JoinPoint】上运行。Spring仅仅支持方法执行时连接点【Join Point】,因此Spring所支持的所有切入点表达式语法,都是为了匹配方法的执行而已~~
  • AOP proxy【AOP代理】:由AOP框架创建的一个对象,该对象满足/实现了切面的契约(如增强方法的执行,等等)。在Spring框架中,一个AOP代理可以是JDK动态代理或CGLIB代理。
  • Advisor【增强器】:Spring中特有的一个概念,它是Advice和Pointcut复合体。

2. < aop:aspectj-autoproxy >的魅力

< aop:aspectj-autoproxy>启动了切面自动代理,它属于自定义命名空间,因此在配置文件读取时,需要通过NamespaceHandlerResolver来根据其命名空间地址解析出NamespaceHandler,再对其进行解析。

public class AopNamespaceHandler extends NamespaceHandlerSupport {
	@Override
	public void init() {
		// In 2.0 XSD as well as in 2.1 XSD.
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

		// Only in 2.0 XSD: moved to context namespace as of 2.1
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}
}

负责解析该元素的BeanDefinitionParser是AspectJAutoProxyBeanDefinitionParser

<aop:aspectj-autoproxy proxy-target-class="" expose-proxy="">
	<aop:include name="" />
</aop:aspectj-autoproxy>

  • 1 向BeanDefinitionRegistry注册AnnotationAwareAspectJAutoProxyCreator的BeanDefinition
    • 1.1 检查BeanDefinitionRegistry中是否已有同类型的BeanDefinition被注册
      • case 有:判断谁的优先级更高,就使用哪一个BeanDefinition
      • case 没有 :创建AnnotationAwareAspectJAutoProxyCreator对应的BeanDefinition,并设置优先级为最高,设置其BeanName,最终注册到BeanDefinitionRegistry上
  • 2 自定义配置BeanDefinition
    • 2.1 判断 proxy-target-class 是否为True,如果是的话,那么暗示强制使用CGLib,并将该配置信息设置进BeanDefinition中
    • 2.2 判断 expose-proxy 是都为True,如果是的话,那么暗示需要暴露代理,并将该配置信息设置进BeanDefinition中
  • 3 判断是否有< aop:include name="" /> 子元素,如果有的话,暗示指定待代理的Bean的命名规范,只有符合命名规范的才会被切面代理,include可以有多个,多个之间是或的关系。该配置也会设置进BeanDefinition中

3. Aop代理

负责代理的BeanPostProcessor是AnnotationAwareAspectJAutoProxyCreator

  • 1 判断targetSourcedBeans【Set< BeanName>】缓存中是否有BeanName存在,若存在,暗示该BeanName所对应的Bean实例是通过TargetSource代理的,而不需要Spring进行代理,因此直接返回;如果不存在,那么可以继续执行Spring的代理流程
  • 2 getAdvicesAndAdvisorsForBean(Class,BeanName,TargetSource),获取可以应用到该Class类的增强器,这是一个复合方法,下面分步骤表述
    • 2.1 findCandidateAdvisors():获取系统中所有的增强器,无论该增强器的切点表达式是什么。

      • ⑴ super.findCandidateAdvisors():调用父类的findCandidateAdvisors()方法,父类会从注册的BeanDefinition中,找出所有class类型是Advisor的的BeanName,然后BeanFactory.getBean(BeanName)提取出Advisor实例对象
      • ⑵ aspectJAdvisorsBuilder.buildAspectJAdvisors():将查找基于注解(即@Aspect)的切面中的Advisor的任务,委托给了BeanFactoryAspectJAdvisorsBuilder对象
        • ①找出容器中所有注册的BeanDefinition
        • ②提取BeanDefinition的Class类型,判断是否@Aspect标注,只保留@Aspect注解标注的
        • ③提取该切面中的所有Advisor
          • Ⅰ反射获取该切面类中所有的Method(不包括以@Pointcut注解的方法),然后从方法的元数据中提取切点表达式,通俗而言,就是提取@Around(value="")中的value属性值,将其包装为AspectJExpressionPointcut切点对象。然后根据注解类型,生成不同的Advice独享,比如:@Around被包装成AspectJAroundAdvice对象,@Before被包装成AspectJMethodBeforeAdvice对象等等。最后将Advice与AspectJExpressionPointcut组合成InstantiationModelAwarePointcutAdvisorImpl对象,该对象是一个Advisor的具体实现,表示切点增强。
          • Ⅱ 根据需要,生成一个SyntheticInstantiationAdvisor,该对象是一个Advisor的具体实现,表示同步增强
          • Ⅲ 发射获取该切面类中所有的Filed,然后对其过滤,只保留以@DeclareParents注解标注的属性,并将其封装为一个DeclareParentsAdvisor对象,该对象是一个Advisor的具体实现,表示引介增强。
    • 2.2 findAdvisorsThatCanApply(List< Advisor>,Class,BeanName):从上面获得Advisors中,找出那些是可以应用到该类的

      • ⑴ 首先筛选引介增强,根据@@DeclareParents(value=“目标类型表达式”)中value表达式的值,来进行类型的过滤
      • ⑵ 然后筛选PointcutAdvisor,即切点增强,核心逻辑就是根据切点表达式,对类型进行过滤
      • ⑶ 剩下的Advisor直接默认保留,不过滤
  • 3 createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean):创建代理
    • 3.1 创建ProxyFactory
    • 3.2 buildAdvisors(BeanName,Object[] ):保证Object[] 中所有的对象均适配Advisor接口,并返回Advisor[]
    • 3.3 proxyFactory.getProxy(getProxyClassLoader()):构建代理对象
      • ⑴ createAopProxy() :即确定AopProxy的类型,到底是用ObjenesisCglibAopProxy还是JdkDynamicAopProxy,通俗而言,就是确定使用JDK的动态代理,还是CGLib的代理。以JdkDynamicAopProxy为例,它是一个AopProxy的实现,因此他必须提供对getproxy(ClassLoader)方法的是实现,JdkDynamicAopProxy还实现了InvocationHandler,提供了invoke(Object proxy, Method method, Object[] args)方法的实现。Spring将Proxy和InvocationHandler结合抽象在一起,让我们只需要一个AopProxy实现,即可完成动态代理。
      • ⑵ getProxy(classLoader):调用给定的代理类(比如JdkDynamicAopProxy)的getProxy(ClassLoder)方法,获取代理对象,其实源码中,该方法是调用了Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)来创建的代理,我们说过JdkDynamicAopProxy还实现了InvocationHandler,因此入参最后一个直接传this即可。

4. invoke()

代理的核心方法,就是invoke()方法

  • 1 TargetSource.getTarget();
  • 2 获取可以应用到当前执行的方法的advisror链
  • 3 构建MethodInvocation
    • 3.1 获取所有增强器
    • 3.2 new ReflectiveMethodInvocation(),将增强器和带调用的方法,组合成一个MethodInvocation对象
  • 4 MethodInvocation.proceed();
  • 5 TargetSource.releaseTarget(target);

事务

事务的开启是通过< tx:annotation-driven transaction-manager=“transactionManager”/>开启的,我们来概括一下该注解的BeanDefinition的加载过程:

  • 1 首先根据该元素的命名空间地址,借助NamesapceHandelrResolver,解析出该命名空间的NamespaceHandler,其实就是TxNamespaceHandelr,将该元素的解析交给它来处理
  • 2 TxNamespaceHandelr在其init方法中注册了Element与BeanDefinitionParser的映射关系,,我们这里选择的是AnnotationDrivenBeanDefinitionParser来对元素进行解析

	@Override
	public void init() {
		registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
		registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
		registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
	}
  • 3 我们以默认情况来分析,首先注册一个BeanPostProcessor,注册过程与AOP的一样,只不过Aop注册的是AnnotationAwareAspectJAutoProxyCreator,而事务注册的是InfrastructureAdvisorAutoProxyCreator,二者均是负责Aop代理的,而且Spring仅允许二者存在其一,AnnotationAwareAspectJAutoProxyCreator的优先级要大于InfrastructureAdvisorAutoProxyCreator,因此,如果我们没有显示的使用< aop:aspectj-autoproxy>注解的话,那么可能就会使用InfrastructureAdvisorAutoProxyCreator,否则,事务就不会再注册InfrastructureAdvisorAutoProxyCreator,因为它的优先级比较低。
  • 4 创建一个TransactionAttributeSource的BeanDefinition,然后注册到BeanDefinitionRegistry
  • 5 创建一个TransactionInterceptor的BeanDefinition,然后注册到BeanDefinitionRegistry,它实现了MethodInterceptor,而MethodInterceptor则继承自Advice,因此TransactionInterceptor算是一个增强
  • 6 创建一个BeanFactoryTransactionAttributeSourceAdvisor的BeanDefinition,然后注册到BeanDefinitionRegistry,BeanFactoryTransactionAttributeSourceAdvisor是一个Advisor,即增强器,他会被组织进Aop中,也就是说,Spring的事务是借助Aop代理来实现的。Advisor是Advice + Pointcut,Advice 指的是上面的TransactionInterceptor,而Pointcut则是由BeanFactoryTransactionAttributeSourceAdvisor提供的默认实现,该Pointcut的功能,就是判断Method的Annotation是否有@Transaction,有的话,则满足切点表达式,否则不满足。Advice则是Spring事务的核心了,最终方法执行时,MethodInterceptor会作为拦截器链的一环,实现对方法的事务增强
TransactionInterceptor

核心方法,是它的invoke(…)方法

  • 1 获取TransactionAttributeSource,解析事务Element时,我们注册了,TransactionAttributeSource是一个策略接口,具体的实现知道如何获取事务属性,无论是从配置中,还是源码中的注解中,等等
  • 2 通过TransactionAttributeSource,获取TransactionAttribute,它是一个TransactionDefinition的实现类
  • 3 获取配置的TransactionManager,一般就是PlatformTransactionManager
  • 4 开启事务,并将事务封装进TransactionInfo中,并将TransactionInfo绑定到当前线程的ThreadLocal中
    • 4.1 通过PlatformTransactionManager的getTransaction(TransactionDefinition)方法,获取事务,该方法中有事务传播机制与事务隔离级别的处理,并返回一个事务对象TransactionStatus
    • 4.2 构建一个TransactionInfo的事务信息对象,并将TransactionStatus、TransactionAttribute等包装进其中
    • 4.3 将TransactionInfo绑定到当前线程的ThreadLocal变量中
  • 5 执行目标方法
  • 6 清理TransactionInfo
  • 7 提交事务
  • 8 回滚事务(Optional)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值