Spring注解-5.AOP

目录

一、使用示例

二、原理

2.1 注册AnnotationAwareAspectJAutoProxyCreator

2.2 解析并缓存切面

2.3 适配切面->生成代理 

2.3.1 getAdvicesAndAdvisorsForBean-查找匹配切面

2.3.2 createProxy-生成代理对象


一、使用示例

 * 1、导入aop模块;Spring AOP:(spring-aspects)
 * 2、定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,xxx)
 * 3、定义一个日志切面类(LogAspects):切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行;
 * 		通知方法:
 * 			前置通知(@Before):logStart:在目标方法(div)运行之前运行
 * 			后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
 * 			返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
 * 			异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
 * 			环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
 * 4、给切面类的目标方法标注何时何地运行(通知注解);
 * 5、将切面类和业务逻辑类(目标方法所在类)都加入到容器中;
 * 6、必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect)
 * [7]、给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】
 * 		在Spring中很多的 @EnableXXX;
 * 
 * 三步:
 * 	1)、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
 * 	2)、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
 *  3)、开启基于注解的aop模式;@EnableAspectJAutoProxy

切面类

/**
 * 切面类
 * @author lfy
 * 
 * @Aspect: 告诉Spring当前类是一个切面类
 *
 */
@Aspect
public class LogAspects {
	
	//抽取公共的切入点表达式
	//1、本类引用
	//2、其他的切面引用
	@Pointcut("execution(public int com.atguigu.aop.MathCalculator.*(..))")
	public void pointCut(){};
	
	//@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
	@Before("pointCut()")
	public void logStart(JoinPoint joinPoint){
		Object[] args = joinPoint.getArgs();
		System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+Arrays.asList(args)+"}");
	}
	
	@After("com.atguigu.aop.LogAspects.pointCut()")
	public void logEnd(JoinPoint joinPoint){
		System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
	}
	
	//JoinPoint一定要出现在参数表的第一位
	@AfterReturning(value="pointCut()",returning="result")
	public void logReturn(JoinPoint joinPoint,Object result){
		System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
	}
	
	@AfterThrowing(value="pointCut()",throwing="exception")
	public void logException(JoinPoint joinPoint,Exception exception){
		System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
	}

}

        业务类

public class MathCalculator {
	
	public int div(int i,int j){
		System.out.println("MathCalculator...div...");
		return i/j;	
	}

}

        配置类

@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
	 
	//业务逻辑类加入容器中
	@Bean
	public MathCalculator calculator(){
		return new MathCalculator();
	}

	//切面类加入到容器中
	@Bean
	public LogAspects logAspects(){
		return new LogAspects();
	}
}

        测试

		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
		MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
		mathCalculator.div(1, 0);		
		applicationContext.close();

二、原理

        Spring AOP的功能从使用上直白的说,就是根据我们的配置来生成代理类拦截指定的方法,将指定的advice织入。

2.1 注册AnnotationAwareAspectJAutoProxyCreator

  @EnableAspectJAutoProxy 注解用来开启 Spring AOP注解的使用。这个的作用就是自动让 ioc 容器中的所有 advisor 匹配方法,advisor 内部都是有 advice 的,让它们内部的 advice 来执行拦截处理

        @EnableAspectJAutoProxy 注解内部使用@Import 注解将 AspectJAutoProxyRegistrar注入到 IoC 容器当中,最终给容器中注册一个AnnotationAwareAspectJAutoProxyCreator

        AnnotationAwareAspectJAutoProxyCreator其实是一个后置处理器。它里面有个方法:postProcessBeforeInstantiation 很显然是在bean实例化前调用的。

2.2 Bean实例化前解析并缓存切面

        postProcessBeforeInstantiation  -> shouldSkip -> findCandidateAdvisors

      Bean的类型是切面,就解析并且缓存切面的advisor信息。就是是否标注了@Aspect注解,有的话就遍历这个Bean中所有未标注@PointCut的方法,也就是@Before,@After,@Around等标注的方法,对于这些方法都会新建一个Advisor对象,存到advisorsCache中。

Advisor:切面由 Advise Pointcut 组成
Advise:通知方法(就是我们的@Before 注解的方法)、Pointcut:切点(被增强的方法)

        此时拦截表达式(切点)还是空的

2.3 Bean初始化后适配切面->生成代理 

        AbstractAutoProxyCreatorpostProcessAfterInitialization执行时(也就是执行Bean的后置处理器),会取出最开始缓存advisors信息。看是否与当前的Bean适配,决定是否创建代理对象

        这个过程中最核心的就是wrapIfNecessary方法,它又包含两个重要方法:getAdvicesAndAdvisorsForBeancreateProxy

        其核心思想如下:

  • 获取当前bean所适用的拦截器列表
  • 如果拦截器列表为空,说明当前bean不需要进行aop增强,那么直接返回原始bean;
  • 如果拦截器列表不为空,说明当前bean需要进行aop增强,那么为它生成代理对象返回,生成代理对象的方式无非就是jdk proxy或者是cglib proxy两种方式,根据bean的情况具体判断

2.3.1 getAdvicesAndAdvisorsForBean-查找匹配切面

        1. findEligibleAdvisors 会从缓存取出所有切面信息(candidateAdvisors)

        2.findAdvisorsThatCanApply根据advisor信息中的表达式进行方法对当前Bean匹配,要先确定是否匹配,然后确定方法是否匹配。最后将匹配到的advisor排序(为了后续封装成执行链)

        如果是第一次执行适配方法findAdvisorsThatCanApply的话,candidateAdvisors中的拦截表达式还是空的,需要进行表达式获取,也就是@Pointcut的value

2.3.2 createProxy-生成代理对象

        1.首先要创建一个代理工厂-ProxyFactory 

  ProxyFactory 在创建过程中封装保存筛选后的 Advisor 集合以及其他的一些属性(织入增强器)。而在后面创建代理类的时候,将 ProxyFactory 作为参数传递给了 JdkDynamicAopProxy 和 ObjenesisCglibAopProxy

        2.接下来获取代理对象,根据Bean类型来判断使用JDK代理还是cglib代理

        3.1 对于JDK代理方式

        1)在 JdkDynamicAopProxy的invoke 方法中获取到了当前方法拦截链将所有的Advisor中的Advice 转换成 MethodInterceptor),存在数组中。

        2)调用 invocation.proceed方法时。对于每个拦截器判断是否适用于当前方法,如果适用则调用拦截器invoke 方法来执行增强方法

        3.2 对于cglib代理方式

        组件的代理对象(cglib增强后的对象)中保存了详细信息(比如增强器,目标对象,xxx); 

         1)根据ProxyFactory对象获取所有增强器,将其转为Interceptor存在数组中形成拦截器链。      

         2)调用 invocation.proceed方法时。链式获取每一个拦截器,拦截器执行 invoke 方法来执行增强方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值