spring-AOP(二) 自动代理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring-AOP(二) 自动代理相关的知识,希望对你有一定的参考价值。

参考技术A

下图是AOP自动代理的流程图

spring中已经定义了创建代理的工厂类 ProxyFactory,通过 ProxyFactory 创建代理,必须要一个被代理对象和一个增强Advisor列表

spring的动态代理实质就是对象创建完毕之后,查找筛选能够应用于该对象上的Advisor列表,然后调用ProxyFactory创建代理对象返回。

Spring中定义了 AbstractAutoProxyCreator 类用于实现自动代理。

从继承图可以看出该类实现了 BeanPostProcessor 接口,覆写了postProcessAfterInitialization 方法。spring的bean初始化完成后会遍历注册的所有BeanPostProcessor实现类对象,调用其postProcessAfterInitialization方法,该方法可以返回一个新的对象覆盖原有对象,在此spring提供一个创建代理并覆盖被代理对象的机会

遍历容器中注册的所有BeanPostProcessor,调用postProcessAfterInitialization方法,如果返回一个新的对象会覆盖掉原始对象注册到spring容器中

上面分析了AbstractAutoProxyCreator是实现自动代理的关键,那么在spring中如何配置一个AbstractAutoProxyCreator的实现类对象呢,spring又是如何根据配置注册该对象的

在spring中可以通过两种配置,启动自动代理

这两中方法最终都是向spring容器中注册了一个AnnotationAwareAspectJAutoProxyCreator实例,这样在spring初始化完对象后就可以调用AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization方法了

上面分析了spring是在何时何处开始创建代理的,解下来分析AbstractAutoProxyCreator进行自动代理的总体实现

在postProcessAfterInitialization方法中判断 被代理对象不为空,调用wrapIfNecessary判断是否对目标类进行代理

createProxy方法中通过ProxyFactory设置AOP配置,如被代理对象和Advisor列表,调用getProxy创建代理对象。

至此AbstractAutoProxyCreator完成了自动创建代理的总体实现,在该抽象类中没有实现获取Adviso列表r的功能,交由各个子类去实现。

分析完spring自动代理的整体实现,接下来看下AbstractAutoProxyCreator的子类AbstractAdvisorAutoProxyCreator是如何进行查找 Advisor、筛选Advisor、排序Advisor的。

AbstractAdvisorAutoProxyCreator实现了getAdvicesAndAdvisorsForBean方法,用于获取应用于目标类的Advisor列表

在findEligibleAdvisors方法定义了查找 Advisor、筛选Advisor、排序Advisor三步操作

在spring中可以通过注册Advisor的bean来实现对目标类的增强代理。spring会筛选出容器中所有Advisor类型的bean,用于对容器中的对象进行增强代理,查找的功能由AbstractAdvisorAutoProxyCreator类实现

将查找功能委托给advisorRetrievalHelper(BeanFactoryAdvisorRetrievalHelperAdapter)实现,该方法的功能就是获取Advisor类型的bean对象返回

spring除了支持配置或者手动注册 Advisor类型的bean之外,还支持通过 @Aspect、@Before、@After等AOP注解来声明Advisor。Advisor的查找解析由子类AnnotationAwareAspectJAutoProxyCreator实现

在AnnotationAwareAspectJAutoProxyCreator的findCandidateAdvisors方法中,第一步首先调父类的方法获取到容器中注册的所有Advisor,然后再委托aspectJAdvisorsBuilder解析注解获取Advisor,然后合并两个结果返回

BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors方法解析AOP注解封装为Advisor对象。

spring定义了一个AspectJAdvisorFactory接口用于解析AOP的注解,接口主要定义了两个功能

AspectJAdvisorFactory的实现类ReflectiveAspectJAdvisorFactory提供了具体实现

调用getAdvisor方法,获取通知方法上的Pointcut表达式,如果在方法上未解析到Pointcut,则跳过,然后根据通知方法和对应Pointcut封装返回一个Advisor的实现类InstantiationModelAwarePointcutAdvisorImpl对象

解析获取通知方法的Pointcut表达式,在该方法中会过滤掉不带Aop注解的非通知型方法,判断一个方法是不是通知就是判断该方法是否声明了切点

最终封装返回的Advisor是InstantiationModelAwarePointcutAdvisorImpl类型,在该实现类的getAdvice方法会回调ReflectiveAspectJAdvisorFactory中的getAdvice方法,ReflectiveAspectJAdvisorFactory会根据通知方法上不同的注解,创建对应的Advice

回调ReflectiveAspectJAdvisorFactory中的getAdvice,策略创建对应的Advice实现类对象

获取到spring容器中所有的Advisor之后,再回到AbstractAdvisorAutoProxyCreator类中,接下来筛选能够应用到目标对象的Advisor。通过Advisor中的Pointcut的ClassFilter和MethodMatcher来对目标对象进行匹配。 在这个阶段的筛选,只要Advisor能应用到目标类型的任意一个方法上都会返回成功

接下来看AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply方法,将筛选的工作委托给AopUtils来实现

AopUtils.findAdvisorsThatCanApply方法中遍历所有的Advisor,然后调用canApply方法判断是否符合,在当中会区分引介增强和切点增强,引介增强是类级别的,只需要根据切点的ClassFilter对目标类进行判断就行。

canApply方法中,通过Pointcut来进行匹配,引介增强IntroductionAdvisor直接根据其ClassFilter判断目标类型,PointcutAdvisor根据其中的Pointcut来进行筛选

Pointcut会首先使用ClassFilter对目标类型进行过滤。如果通过,再使用 MethodMatcher 校验 类中所有的方法,只要有一个方法匹配上,代表该Advisor符合条件。

在这里宁可多通过,也不要校验太严格,因为在代理类中具体方法执行的时候,还会再一次使用Pointcut进行校验。所以该方法中会获取目标类型的所有接口,判断接口中的方法,有一个符合也会返回true

当获取到目标类型上的所有Advisor后,还需要对Advisor进行排序。Advisor的顺序决定了通知方法的应用顺序。

Advisor的排序主要分为两种

接下来看下spring是怎么实现排序的

AbstractAdvisorAutoProxyCreator.sortAdvisors提供了基于Ordered的排序,由spring的AnnotationAwareOrderComparator统一处理Ordered实现类或者添加@Ordered注解类的排序

AspectJAwareAdvisorAutoProxyCreator覆写了父类的sortAdvisors方法,在基于Ordered排序基础上提供了同一切面下不同通知之间的排序,具体排序实现委托给了PartialOrder

至此完成Advisor的查找、筛选、排序。

spring-aop

一、AOP与过滤器的区别

 技术图片

 

二、代理模式

代理的模式的作用:就是使用一个代理类来管理被代理类对象(源对象)的统一处理。我们将这种统一处理的理解称为控制访问。

代理模式的缺陷:
1. 代理类必须要继承或者实现一个基类或者接口!!(很笨重)。每个接口都要实现一个新的代理类。限制了代理的源对象的类型!!!
2. 每个方法的逻辑处理,还是要重复编写。(代理模式并不可以减少代码)

如何解决代理模式的缺陷呢?可以使用动态代理模式(不需要指定代理类的固定接口)。

三、动态代理


动态代理模式的问题:它会拦截的被代理类对象的方法,无法通过规则来拦截指定的方法。

 

四、AOP(主要用在日志管理):可通过规则拦截指定的方法

注意点:

 技术图片

 

在(使用@Aspect声明为切面类)Aop类中使用注解

 技术图片

 

@xxx(value="execution(拦截规则)")

拦截规则:

表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))

写法说明:

全匹配方式:

public void cn.gzsxt.service.impl.CustomerServiceImpl.saveCustomer()

访问修饰符可以省略

void cn.gzsxt.service.impl.CustomerServiceImpl.saveCustomer()

返回值可以使用*号,表示任意返回值

* cn.gzsxt.service.impl.CustomerServiceImpl.saveCustomer()

包名可以使用*号,表示任意包,但是有几级包,需要写几个*

* *.*.*.*.CustomerServiceImpl.saveCustomer()

使用..来表示当前包,及其子包

* com..CustomerServiceImpl.saveCustomer()

类名可以使用*号,表示任意类

* com..*.saveCustomer()

方法名可以使用*号,表示任意方法

* com..*.*()

参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数

* com..*.*(*)

参数列表可以使用..表示有无参数均可,有参数可以是任意类型

* com..*.*(..)

全通配方式:

* *..*.*(..)

 

 

1.Joinpoint(连接点):

所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。

---就是根据规则,可以指定拦截的方法,我们将每一个被拦截的方法称为连接点。

 

2.Pointcut(切入点):

--所谓的切入点,就是拦截方法设置的规则

所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。

 

3.Advice(通知/增强,场景:在之前拦截,还是方法执行后拦截呢?):

--就是可以设置在方法之前拦截或者方法执行之后拦截或者方法出异常后拦截,或者方法之前和之后都拦截。我们将这些拦截场景称为通知

 

4.Aspect(切面):

--所谓的切面就是我们的拦截处理类。

是切入点和通知(引介)的结合。

 

基于xml配置的大体框架:

<bean name="aop" class="cn.gzsxt.aop.StudentAOP"></bean>

<!-- aop配置 -->

<aop:config >

        <aop:pointcut expression="execution(* cn.gzsxt.service..*.*(..))" id="public_pointcut"/>

<!-- 配置切面 -->

   <aop:aspect id="aop" ref="aop">

         <!-- 方法执行前 -->

<aop:before method="before" pointcut-ref="public_pointcut"/>

<!-- 方法执行后 -->

<aop:after method="after" pointcut-ref="public_pointcut"/>

<!-- 方法执行前后都需要拦截 -->

<aop:around method="around" pointcut-ref="public_pointcut" />

<!-- 方法正常执行后 ,如果出了异常就不拦截了-->

<aop:after-returning method="afterReturning" pointcut-ref="public_pointcut"/>

<aop:after-throwing method="afterThrowing" pointcut-ref="public_pointcut"/>

</aop:aspect>

</aop:config>

 


五、定时任务

注意点:

1.在配置文件中

 技术图片

六、发送邮件

1.导包

技术图片

 

在xml中配置发送类

 技术图片

 

以上是关于spring-AOP(二) 自动代理的主要内容,如果未能解决你的问题,请参考以下文章

spring-aop

Spring-AOP

spring-AOP动态代理

Spring-AOP用法总结

三:Spring-AOP源码

spring-aop代理的生效原理