Spring AOP学习笔记

Spring AOP小记



一、Spring AOP的使用

1.基于纯注解的 AOP

需要用到的几个注解如下:

@EnableAspectJAutoProxy 开启全局AOP配置
@Aspect 告诉Spring 哪个类是切面类
通知方法:

		@Before : 前置通知  具体用法 			@before(value = "execution(public int com.itguigu.bean.person.doI(..))")
		@After : 后置通知					@After(value = "execution(* com.itguigu.bean.person.*(..))")
		@AfterReturning :返回通知
		@AfterThrowing :异常通知
		@Around : 环绕通知
		它们的执行顺序为 环绕前置通知 -> 前置通知 -> 执行目标方法 ->  环绕后置返回/环绕后置异常 ->  后置环绕通知 ->  后置通知 -> 返回通知/异常通知

@PointCut : 用来抽取某个被增强的切入点,具体用法如下:


```java
    @Pointcut(value = "anyMethod()")
    private void anyMethod(){}//定义一个切入点

	@AfterReturning(value = "anyMethod()" ,returning = "res")
    public void doAfter(JoinPoint joinPoint,Object res)

	@Before(value = "execution(* com.itguigu.bean.PersonImpl.*(..))")
    public void doAccessCheck(JoinPoint joinPoint)

	@AfterThrowing(value = "anyMethod()",throwing = "e")

JoinPoint 是一个切点类,可以用来获取目标方法的签名,方法名,方法参数,使用AOP也能获取目标方法执行的结果或异常信息。

具体的注解配置方法 (引入正确的依赖): 写一个被增强类:
public class Calcultor {
    public int div(int i,int j){
        return i/j;
    }
}

写一个切面类:记得加上@Aspect( )

@Aspect
public class MyInterceptor {
    @Pointcut("execution(* com.itguigu.bean.PersonImpl.*(..))")
    private void anyMethod(){}//定义一个切入点

    @Before(value = "execution(* com.itguigu.bean.PersonImpl.*(..))")
    public void doAccessCheck(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        System.out.println("前置通知:"+joinPoint.getSignature().getName()+"方法即将开始执行,参数为:" + Arrays.asList(args));
    }

    @AfterReturning(value = "anyMethod()" ,returning = "res")
    public void doAfter(JoinPoint joinPoint,Object res){
        System.out.println("返回通知"+joinPoint.getSignature().getName()+"方法结束,"+"返回的结果为"+res);
    }

    @After("anyMethod()")
    public void after(JoinPoint joinPoint){
        System.out.println("后置通知:"+joinPoint.getSignature().getName());
    }

    @AfterThrowing(value = "anyMethod()",throwing = "e")
    public void doAfterThrow(Exception e){
        System.out.println("异常通知:"+e);
    }

    @Around("anyMethod()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("进入环绕通知");
        Object object = pjp.proceed();//执行该方法
        System.out.println("退出环绕方法");
        return object;
    }

全注解配置,所以需要一个配置类,加上 @Configuration + @EnableAspectJAutoProxy
@EnableAspectJAutoProxy代表启用AspectJ 框架的自动代理,这个时候Spring 才会自动生成代理对象,进而使用AOP
把相关的类注册到容器中 用到注解@Bean

@Configuration
@EnableAspectJAutoProxy
public class MyConfigure {
    @Bean
    public Calcultor calcultor(){
        return new Calcultor();
    }
    @Bean
    public MyAspect myAspect(){
        return new MyAspect();
    }
    @Bean
    public Man man(){
        return new Man();
    }
}

最后:getBean() 底层是实现动态代理,JDK代理或者cglib 代理的区别

		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfigure.class);
        Calcultor bean = context.getBean(Calcultor.class);
        int i = bean.div(1,1);
        System.out.println(i);

2.使用xml文件

	将 Bean 注册都容器中,并且配置AOP ,配置AOP时要引入xml的AOP约束
    <bean id="personImpl" class="com.itguigu.bean.PersonImpl"/>
    <bean id="myInterceptor" class="com.itguigu.LogAspect.MyInterceptor" />
    <aop:config>
        <aop:aspect ref="myInterceptor">
            <aop:before method="doAccessCheck" pointcut="execution(*com.itguigu.bean.PersonImpl.div(..))"/>
            <aop:after method="after" pointcut="execution(* com.itguigu.bean.PersonImpl.div(..))"/>
        </aop:aspect>

    </aop:config>

3.使用xml文件和注解相结合

在Bean上 加入 @Component 注解 ,使得能够被扫描如容器中,其次在切面类加入注解@Aspect 来告诉Spring 哪个类是切面类

将组件扫描如容器中
<context:component-scan base-package="com.itguigu" /> 
开启AOP,作用形同于 @EnableAspectJAutoProxy 
<aop:aspectj-autoproxy />

代码如下(示例):

二、源码分析

1.@EnableAspectJAutoProxy


@EnableAspectJAutoProxy  >>  @Import({AspectJAutoProxyRegistrar.class})
给容器中引入了AnnotationAwareAspectJAutoProxyCreator 类,AspectJAwareAdvisorAutoProxyCreator这个类 实现了两个接口:
BeanPostProcessor 和 BeanFactoryAware
BeanPostProcessor可以用来拦截容器Bean 初始化前后和实例化前后:

public interface BeanPostProcessor {


	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

    @Nullable
    default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }



2.在初始化之前会调用函数isInfrasturep 判断Bean是否与切面有关,有关就放入AdviceBean集合;
在Bean实例化后,
1)找到被拦截的Bean的增强器(通知方法),以及切入点
2)将增强器保存到proxyFactory中
3)看被拦截的Bean是否实现了接口,决定使用jdk动态代理还是 cglib代理(关键在于被代理类是否实现了接口)
4)返回被拦截的Bean的代理对象,注册到容器

3.动态代理反射执行目标方法的过程:
1)保存所有的增强器,转化为一个拦截器链

// 执行器链是一个list集合,传入了被代理对象及目标方法
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
	首先,缓存Map(methodCache)中找是否有已经创建了目标方法的执行器(增强器?拦截器),有就直接获取
	如果没有,就调用方法 advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass); 创建执行器链
	
		具体的创建流程:
		public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass)
		先判断传入容器的切面类的增强器 **advisor**(Before、After等) 是否实现了pointcut接口,如果实现了,就把这些增强器转而pointcut类型
		if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
		然后在调用 getInterceptors(advisor) 方法,看这些增强器是否实现了MethodInterceptor接口,如果实现了,就直接把增强器添加到list中(执行器链),否则就把它们转为拦截器,再添加到list中(执行器链).

		Advice advice = advisor.getAdvice();
		if (advice instanceof MethodInterceptor) {
			interceptors.add((MethodInterceptor) advice);
		}
		for (AdvisorAdapter adapter : this.adapters) {
			if (adapter.supportsAdvice(advice)) {
				interceptors.add(adapter.getInterceptor(advisor));
			}
		}
		最后就能返回一个list集合,集合的长度为增强器数目加1,1为一个默认的增强器,不起作用,最后把list放到缓存中。
				

2)如果没有拦截器链,直接执行目标方法

	if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
		// We can skip creating a MethodInvocation: just invoke the target directly.
		// Note that the final invoker must be an InvokerInterceptor, so we know
		// it does nothing but a reflective operation on the target, and no hot
		// swapping or fancy proxying.
		Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
		retVal = methodProxy.invoke(target, argsToUse);

3)有拦截器链,就将目标方法,拦截器链等信息传入并创建CglibMethodInvocation对象,调用 Proceed() 方法依次执行拦截器链,获取返回值。

	retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

在proceed() 方法内定义了一个常量 this.currentInterceptorIndex = -1

	//如果执行链的长度为0,就直接执行 invokeJoinpoint(),反射调用目标方法
	if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}
	
	如果执行器链长度不为0,this.currentInterceptorIndex 自增
	Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
	
	然后依次递归调用执行器链,形成责任链
	return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

==========================================
单步调试AOP ,依次得到的方法调用流程:

	//默认的执行器
	public Object invoke(MethodInvocation mi) throws Throwable {
		MethodInvocation oldInvocation = invocation.get();
		invocation.set(mi);
		try {
			return mi.proceed();
		}
		finally {
			invocation.set(oldInvocation);
		}
	}
//异常通知的执行器
@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		catch (Throwable ex) {
			if (shouldInvokeOnThrowing(ex)) {
				invokeAdviceMethod(getJoinPointMatch(), null, ex);
			}
			throw ex;
		}
	}
	//返回通知的执行器
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		Object retVal = mi.proceed();
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}
	//后置通知的执行器
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		finally {
			invokeAdviceMethod(getJoinPointMatch(), null, null);
		}
	}
	//前置通知的执行器
	public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() ); // 此处调用前置通知

			@Override
			public void before(Method method, Object[] args, Object target) throws Throwable {
				invokeAdviceMethod(getJoinPointMatch(), null, null);
			}
		return mi.proceed();//再次递归,此时执行器链长度来到0,this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1 条件成立,执行之前的return invokeJoinpoint()方法,反射调用目标方法

调用完目标方法后,依次递归退出回到 后置通知 --> 返回通知 --> 异常通知 --> 默认的执行器,至此,AOP 就完成了所有增强方法和目标方法的调用。

三、图示分析

调用图示如下:

在这里插入图片描述


以上便是我对AOP执行过程的一点理解,烦请各路大神批评指正 ,也参考了其他博主,特别是(segmengfault 简相杰)的Spring注解版的博文,表示衷心感谢,致礼。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值