Spring学习笔记-源码分析AOP原理

一、基于注解的AOP实现

1,首先创建我们的几个测试类,一个切面类LogAspects,需要被切入的业务类MathCaculator,配置类MainConfigOfAOP、以及我们的junit测试类IocTestAOP,

代码如下:

切面类:

       切面类里面写了PointCut、logStart、mathEnd、logReturn、logException这几个方法,具体注解及含义见注释,注意切面类需加上@Aspect注解,让Spring了解该类是切面类。

import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 * 切面类
 * @Aspect 告诉spring容器这是一个切面类
 */
@Aspect
public class LogAspects {
    
    
    /**
     * 抽取公共的切入点表达式
     * 1,本类引用
     * 2,其他的切面引用
     */
    @Pointcut("execution(public int com.wjs.aop.MathCaculator.*(..))")
    public void PointCut(){}
    /**
     * @Before 需要被切面的方法执行前调用该方法

     * ("PointCut()"),切入点表达式,指定在哪个方法切入
     */
    @Before("PointCut()")

    //@Before("public int com.wjs.aop.MathCaculator.*(..)")
    public void logStart(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        //获取参数集合
        Object[] params = joinPoint.getArgs();
        
        System.out.println(name+"---------开始,{"+Arrays.asList(params)+"}");
    }
    
    /**
     * @After 需要被切面的方法执行成功后调用该方法
     */
    @After("PointCut()")
    public void mathEnd(JoinPoint joinPoint){
        //获取方法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"---------结束,{}");
    }
    
    /**
     * @AfterReturning 需要被切面的方法成功返回后调用
     */
    @AfterReturning(value = "PointCut()",returning = "result")
    public void logReturn(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"---------返回,{"+result+"}");
    }
    
    /**
     * @AfterThrowing 需要被切面的方法报异常后调用
     */
    @AfterThrowing(value="PointCut()",throwing = "e")
    public void logException(Exception e){
        
        System.out.println("---------异常,{"+e+"}");
    }
}

 业务类:

         我们在业务类里面写上要被切面的方法。

@Component
public class MathCaculator {
    
    public int div(int i,int j){
        
        return i/j;
    }

}

配置类: 

        将我们的类对象添加到bean容器中,注意:必须加上@EnableAspectJAutoProxy注解,开启基于注解的aop模式,这样aop才会生效,


@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
    
    @Bean
    public MathCaculator mathCaculator(){
        
        return new MathCaculator();
    }
    
    
    @Bean
    public LogAspects logAspets(){
        
        return new LogAspects();
    }
}

测试类:

        测试类我们首先创建容器,通过容器的getBean方法拿到容器中的业务类bean对象,(注意:不要直接new一个业务类对象,这样的话切面不会生效),最后调用业务类方法。

import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.wjs.aop.MathCaculator;
import com.wjs.common.MainConfigOfAOP;

public class TestAop {
    
    
    @Test
    public void test() {
        
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
        
        MathCaculator mathCaculator = applicationContext.getBean(MathCaculator.class);
        
        String[] names = applicationContext.getBeanDefinitionNames();
        
        for (String name : names) {
            
            System.out.println(name);
            
        }
        
        mathCaculator.div(1, 1);
    }

}

测试结果:

div---------开始,{[1, 1]}
div---------结束,{}
div---------返回,{1}

二、实现原理解析

        为什么我们添加了一些注解后,就能实现在业务类方法调用的前后调用一些切面方法呢?接下来我们来分析下:首选来了解下@EnableAspectJAutoProxy注解,了解下它是干什么的。

@EnableAspectJAutoProxy注解

         我们点进去看下它的源码,通过源码分析我们发现了它给我们容器中注册了个自定义的组件AspectJAutoProxyRegistrar,

         进入组件发现其实它就做了一件事情,那就是注册一个名字为internalAutoProxyCreator,类型为AnnotationAwareAspectJAutoProxyCreator的组件到我们的容器中。从源码了解,会先去判断我们的容器中有没有这个名字为internalAutoProxyCreator的组件,若没有则创建一个类型为AnnotationAwareAspectJAutoProxyCreator的该对象,并注册到容器中。

具体进入AnnotationAwareAspectJAutoProxyCreator类,一步步下去,看下他的继承以及实现,发现他实际是一个后置处理器,同时也实现了beanFactoryAware,自动装配beanFactory

AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator

AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator

AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator

AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator 

AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware

SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor 

 InstantiationAwareBeanPostProcessor extends BeanPostProcessor 

其中各类做了哪些后置处理器的内容以及beanFactory的内容,如下:

 

BeanPostProfessor对象的创建

         首选进入容器的创建方法,AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);

方法中先传入我们的配置类,创建ioc容器,注册配置类,最后调用refresh方法,进入refresh方法,发现它会注册一个后置处理器去对bean的创建进行一个拦截,

进入该方法,

1、先拿到ioc容器已经定义的所有的需要创建对象的beanPostProfessor,

2、调用beanFactory.addBeanPostProcessor方法向容器中加入别的beanPostProfessor,

3、对beanPostProfessor进行一个分类排序,优先级如下,

       先注册实现了PriorityOrdered接口的beanPostProfessor,

       再注册实现了Ordered接口的beanPostProfessor,

       最后注册没有实现优先级接口的beanPostProfessor,

4、通过对AnnotationAwareAspectJAutoProxyCreator的继承及实现类了解,它是实现了Ordered接口的,

跟着断点走,根据bean名通过调动beanFactory.getBean(ppName, BeanPostProcessor.class)方法获取该BeanPostProcessor,这个getbean方法实际上走到了AbstractBeanFactory.doGetBean方法,先判断单例缓存中是否已经手动注册了该单例,

不是,往下走,进入实例创建,先判断是否是单实例bean ,是,调用getSingleton方法,

 

由于我们并没有实例化这个BeanPostProfessor对象,所以它是null,走到最后调用singletonFactory.getObject()方法,

我们回到之前AbstractBeanFactory类,发现它实际上就是去创建了一个BeanPostProfessor对象,

也就是说一开始的注册BeanPostProfessor,实际上就是去创建BeanPostProfessor对象,包括之前AnnotationAwareAspectJAutoProxyCreator后置处理器对象,然后存入容器中,最后回到最开始,走下去,将该beanPostProfessor注册到beanFactory中。

自此beanPostProfessor创建完成。

代理对象的创建

applyBeanPostProcessorsBeforeInstantiation方法

        当我们创建bean实例的时候,也就是调用之前的createBean方法,方法在创建bean之前会去调用一个resolveBeforeInstantiation方法,该方法表示给后置处理器也就是beanPostProfessor一个机会去返回一个代理对象来替换目标对象bean实例,如果能创建则直接使用,如果不行则调用doCreateBean方法去创建bean实例,

 进入该方法,会去调用applyBeanPostProcessorsBeforeInstantiation方法,如果bean不为空则再调用applyBeanPostProcessorsAfterInitialization方法对bean进行赋值,

首先进入applyBeanPostProcessorsBeforeInstantiation方法,它先遍历后置处理器,并判断当前后置处理器是不是InstantiationAwareBeanPostProcessor类型,是,进入postProcessBeforeInstantiation方法,

方法中会对当前bean做一个判断,判断条件包含两个方法,

1、isInfrastructureClass(beanClass)方法,判断当前bean是不是Advice、Advisor、AopInfrastructureBean类型,以及通过this.aspectJAdvisorFactory.isAspect(beanClass),判断当前beanClass是不是切面类,就是检测有没有@Aspects注解,是的就加入增强器集合中,状态标识为false表示不需增强。

2、shouldSkip(beanClass, beanName),是否需要跳过,拿到所有的增强器,遍历判断当前增强器是不是AspectJPointcutAdvisor类型,不是,引用父类方法,直接返回false,

执行到结束,applyBeanPostProcessorsBeforeInstantiation执行完毕,若bean不为空则继续执行applyBeanPostProcessorsAfterInitialization方法。

applyBeanPostProcessorsAfterInitialization方法

       刚执行到applyBeanPostProcessorsAfterInitialization方法,首先也是拿到所有后置处理器,遍历后调用postProcessAfterInitialization方法,最后会执行一个wrapIfNecessary(bean, beanName, cacheKey)方法,方法意思就是如果有必要的话,对当前的bean进行一个包装,

包装方法中,首先也会做一个isInfrastructureClass(beanClass)及shouldSkip(beanClass, beanName)的判断,然后创建一个代理对象,如果我们有增强器的话,通过getAdvicesAndAdvisorsForBean方法获取该bean的所有增强器,如果有则将该bean添加到增强器集合中,并进行代理对象的创建,没有则保存到增强器容器中,标识状态false不需要增强。

getAdvicesAndAdvisorsForBean方法,先获取所有的增强器,再获取所有的可用于当前bean的增强器,不为空,返回这些可以被应用的增强器,

createProxy方法,创建代理对象,也是先拿到所有的增强器,然后将这些增强器加入到proxyFactory代理工厂,最后进行代理对象的创建,

来到代理对象创建的方法,这里会做一个判断,如果当前类是一个接口类,则返回一个new JdkDynamicAopProxy(config),JDK动态代理对象,不是,则返回一个new ObjenesisCglibAopProxy(config),Cglib动态代理对象,因为我们并不是接口,则返回的是一个cglib的动态代理对象。

小结

        从上述内容我们可以了解到,所有的bean实例初始化之前都会被后置处理器做一个拦截,后置处理器会尝试返回该bean的实例,当我们有做过切面处理的时候,后置处理器就会返回一个当前bean的cglib动态代理对象,所以我们后面通过容器获取的bean对象,实际上是后置处理器为我们创建的动态代理对象。

切面方法的执行时机

        方法位置断点,发现了容器创建的对象已经是cglib的代理对象了,执行方法会先调用cglibAopProxy.intercept方法,调用getInterceptorsAndDynamicInterceptionAdvice方法,传入目标对象,方法,生成一个拦截器链chain,

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

getInterceptorsAndDynamicInterceptionAdvice方法中主要做的事情就是拿到所有的增强器,然后将增强器advisor转换成拦截器interceptor然后存入集合,生成拦截器链, 

往下走,如果生成的拦截器链为空,也就是没用增强器的情况下,就直接执行目标的方法,

最后,创建一个方法的调用,就是调用这个proceed方法,

mi=new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy);

Object retVal=.mi.proceed();

进入proceed方法,首选对当前拦截器序号做一个判断,从-1开始,this.interceptorsAndDynamicMethodMatchers就是我们的之前说的5个拦截器,size为5,然后++自增获取下一个拦截器并调用拦截器的invoke()方法,

1,首先是ExposeInvocationInterceptor拦截器,进入它的invoke()方法,它会再次调用proceed()方法,

2,自增拿到AspectJAfterThrowingAdvice拦截器,进入它的invoke()方法,继续调用proceed()方法,

3,再自增拿到AfterReturningAdviceInterceptor拦截器,进入它的invoke()方法,也会调用proceed()方法,

4,再自增拿到AspectJAfterAdvice拦截器,进入它的invoke()方法,继续调用proceed()方法,

5,最后拿到MethodBeforeAdviceInterceptor拦截器,进入它的invoke()方法,这次先执行了advice.before()的方法(执行切面类logStart()方法),然后返回调用proceed方法,

currentInterceptorIndex已自增到4,然后返回目标方法的执行,继续执行,回到步骤4,执行finally代码块中的invokeAdviceMethod方法(执行切面类logEnd()方法),继续,回到步骤3,执行adive.afterReturning()方法(执行切面类logReturn()方法),

注意:步骤3的代码是没有进行异常捕获的,如果之前出现异常则这一层会直接抛给上级,由步骤2进行异常捕获,步骤2catch中会执行invokeAdviceMethod方法(执行切面类logException()方法),

至此,切面类方法切入到业务类方法就已实现,大致流程图如下:

三、总结

1,@EnableAspectJAutoProxy注解,开启AOP功能;

2,@EnableAspectJAutoProxy给容器中注册一个组件AnnotationAwareAspectJAutoProxyCreator;

3,AnnotationAwareAspectJAutoProxyCreator这个组件是个后置处理器;

4,容器的创建流程:

      1),registerBeanPostProcessors(beanFactory),注册后置处理器,创建AnnotationAwareAspectJAutoProxyCreator对象;

      2),finishBeanFactoryInitialization(beanFactory),初始化剩下的实例bean;

               1),创建业务逻辑组件和切面组件;

               2),AnnotationAwareAspectJAutoProxyCreator拦截bean组件的创建过程;

               3),判断组件是否需要增强,是的话切面通知方法包装成增强器(Advisor),创建并返回一个cglib的代理对象;

5,执行目标方法;

     1),代理对象执行目标的方法;

     2),执行cglibAopPoxy的intercept方法

     3),得到目标方法的拦截器链(目标方法的拦截器链被包装成MethodInterceptor类型拦截器);

     4),利用拦截器的链式机制,执行每一个拦截器,输出结果;

              结果:正常执行:前置通知==>目标方法执行==>后置通知==>结果返回通知

                         异常执行:前置通知==>目标方法执行==>后置通知==>异常返回通知

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值