AOP:【动态代理】
指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式
1.导入aop模块Spring AOP:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.定义一个业务逻辑类(CalculateController)在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,xxx)
3.定义一个日志切面类(LogAop):切面类里面的方法需要动态感知CalculateController.calculateNum运行到哪里然后执行
通知方法:
前置通知(@Before):logStart:在目标方法(calculateNum)运行之前运行
后置通知(@After):logEnd:在目标方法(calculateNum)运行结束之后运行(无论方法正常结束还是异常结束)
返回通知(@AfterReturning):logReturn:在目标方法(calculateNum)正常返回之后运行
异常通知(@AfterThrowing):logException:在目标方法(calculateNum)出现异常以后运行
环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
4.给切面类的目标方法标注何时何地运行(通知注解)
5.将切面类和业务逻辑类(目标方法所在类)都加入到容器中
6.必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect)
7.给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】
在Spring中很多的 @EnableXXX
三步:
1)将业务逻辑组件和切面类都加入到容器中告诉Spring哪个是切面类(@Aspect)
2)在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
3)开启基于注解的aop模式@EnableAspectJAutoProxy
配置
// @EnableAspectJAutoProxy 开启基于注解的aop模式
@EnableAspectJAutoProxy
@Configuration
public class MyAopConfig {
@Bean
public CalculateController calculateController(){
return new CalculateController()
}
@Bean
public LogAop logAop(){
return new LogAop()
}
}
/**
* 切面类
*/
// @Aspect: 告诉Spring当前类是一个切面类
@Aspect
public class LogAop {
//抽取公共的切入点表达式
//1、本类引用
//2、其他的切面引用
@Pointcut("execution(public int com.example.studywork.work.controller.CalculateController.*(..))")
public void pointCut(){}
@Before(value ="pointCut()")
public void logStart(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName()+"方法运行前。。。参数列表是:{"+ Arrays.asList(joinPoint.getArgs())+"}")
}
// 外部切面类引用可以用全类名
@After("com.example.studywork.work.aop.LogAop.pointCut()")
public void logEnd(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName()+"方法结束。。。")
}
//JoinPoint一定要出现在参数表的第一位
@AfterReturning(value = "pointCut()",returning = "obj")
public void logReturn(JoinPoint joinPoint,Object obj){
System.out.println(joinPoint.getSignature().getName()+"方法正常返回。。。运行结果是:{"+obj+"}")
}
@AfterThrowing(value = "pointCut()",throwing = "e")
public void logxception(JoinPoint joinPoint,Exception e){
System.out.println(joinPoint.getSignature().getName()+"方法异常返回。。。异常结果是:{"+e+"}")
}
}
// 业务
public class CalculateController {
public int calculateNum(int i, int j){
System.out.println("CalculateController类的calculateNum方法正在运行")
return i/j
}
}
输出
@Test
public void test() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyAopConfig.class)
CalculateController bean = applicationContext.getBean(CalculateController.class)
bean.calculateNum(1,1)
}
输出结果
异常输出
@Test
public void test() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyAopConfig.class)
CalculateController bean = applicationContext.getBean(CalculateController.class)
bean.calculateNum(1,0)
}
输出结果
下图是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的查找、筛选、排序。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)