美文网首页Java
Spring中AOP相关的API及源码解析,原来AOP是这样子的

Spring中AOP相关的API及源码解析,原来AOP是这样子的

作者: 愿天堂没有BUG | 来源:发表于2022-06-29 15:35 被阅读0次

    一个使用API创建代理的例子

    在进入API分析前,我们先通过两个例子体会下如何使用API的方式来创建一个代理对象,对应示例如下:

    定义通知

    publicclassDmzAfterReturnAdviceimplementsAfterReturningAdvice{@OverridepublicvoidafterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target)throwsThrowable{System.out.println("after invoke method ["+ method.getName() +"],aop afterReturning logic invoked");}}publicclassDmzAroundAdviceimplementsMethodInterceptor{@OverridepublicObjectinvoke(MethodInvocation invocation)throwsThrowable{System.out.println("aroundAdvice invoked");returninvocation.proceed();}}publicclassDmzBeforeAdviceimplementsMethodBeforeAdvice{@Overridepublicvoidbefore(Method method, Object[] args, Object target)throwsThrowable{System.out.println("before invoke method ["+ method.getName() +"],aop before logic invoked");}}publicclassDmzIntroductionAdviceextendsDelegatingIntroductionInterceptorimplementsRunnable{@Overridepublicvoidrun(){System.out.println("running!!!!");}}

    2.切点

    publicclassDmzPointcutimplementsPointcut{@Override@NonNullpublic ClassFilter getClassFilter() {// 在类级别上不进行拦截returnClassFilter.TRUE;}@Override@NonNullpublicMethodMatchergetMethodMatcher() {returnnewStaticMethodMatcherPointcut() {@Overridepublic boolean matches(@NonNullMethod method, Class targetClass) {// 对于toString方法不进行拦截return!method.getName().equals("toString");}};}}

    3.目标类

    publicclassDmzService{@OverridepublicStringtoString(){System.out.println("dmzService toString invoke");return"dmzService";}publicvoidtestAop(){System.out.println("testAop invoke");}}

    4.测试代码

    publicclassMain{publicstaticvoidmain(String[] args){ProxyFactory proxyFactory =newProxyFactory();// 一个Advisor代表的是一个已经跟指定切点绑定了的通知// 在这个例子中意味着环绕通知不会作用到toString方法上Advisor advisor =newDefaultPointcutAdvisor(newDmzPointcut(),newDmzAroundAdvice());// 添加一个绑定了指定切点的环绕通知proxyFactory.addAdvisor(advisor);// 添加一个返回后的通知proxyFactory.addAdvice(newDmzAfterReturnAdvice());// 添加一个方法执行前的通知proxyFactory.addAdvice(newDmzBeforeAdvice());// 为代理类引入一个新的需要实现的接口--RunnableproxyFactory.addAdvice(newDmzIntroductionAdvice());// 设置目标类proxyFactory.setTarget(newDmzService());// 因为要测试代理对象自己定义的方法,所以这里启用cglib代理proxyFactory.setProxyTargetClass(true);// 创建代理对象Object proxy = proxyFactory.getProxy();// 调用代理类的toString方法,通过控制台查看代理逻辑的执行情况proxy.toString();if(proxyinstanceofDmzService) {((DmzService) proxy).testAop();}// 判断引入是否成功,并执行引入的逻辑if(proxyinstanceofRunnable) {((Runnable) proxy).run();}}}

    这里我就不将测试结果放出来了,大家可以先自行思考这段程序将输出什么。接下来我们就来分析上面这段程序中所涉及到的API,通过这些API的学习相信大家可以彻底理解上面这段代码。

    API介绍

    Pointcut(切点)

    对应接口定义如下:

    publicinterfacePointcut{// ClassFilter,在类级别进行过滤ClassFiltergetClassFilter();// MethodMatcher,在方法级别进行过滤MethodMatchergetMethodMatcher();// 一个单例对象,默认匹配所有Pointcut TRUE = TruePointcut.INSTANCE;}

    切点的主要作用是定义通知所要应用到的类跟方法,上面的接口定义也很明显的体现了这一点,我们可以将其拆分成为两个部分

    ClassFilter,接口定义如下:

    publicinterfaceClassFilter{booleanmatches(Class<?> clazz);ClassFilter TRUE = TrueClassFilter.INSTANCE;}

    ClassFilter的主要作用是在类级别上对通知的应用进行一次过滤,如果它的match方法对任意的类都返回true的话,说明在类级别上我们不需要过滤,这种情况下,通知的应用,就完全依赖MethodMatcher的匹配结果。

    MethodMatcher,接口定义如下:

    publicinterfaceMethodMatcher{booleanmatches(Method method, @Nullable Class<?> targetClass);booleanisRuntime();booleanmatches(Method method, @Nullable Class<?> targetClass, Object... args);MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;}

    MethodMatcher中一共有三个核心方法

    matches(Method method, @Nullable Class<?> targetClass),这个方法用来判断当前定义的切点跟目标类中的指定方法是否匹配,它可以在创建代理的时候就被调用,从而决定是否需要进行代理,这样就可以避免每次方法执行的时候再去做判断

    isRuntime(),如果这个方法返回true的话,意味着每次执行方法时还需要做一次匹配

    matches(Method method, @Nullable Class<?> targetClass, Object... args),当之前的isRuntime方法返回true时,会调用这个方法再次进行一次判断,返回false的话,意味这个不对这个方法应用通知

    Advice(通知)

    环绕通知(Interception Around Advice)

    接口定义如下:

    publicinterfaceMethodInterceptorextendsInterceptor{Objectinvoke(MethodInvocation invocation)throwsThrowable;}

    在上面接口定义的invoke方法中,MethodInvocation就是当前执行的方法,当我们调用invocation.proceed就是在执行当前的这个方法,基于此,我们可以在方法的执行前后去插入我们自定义的逻辑,比如下面这样

    // 执行前的逻辑doSomeThingBefore();Objectvar= invocation.proceed;doSomeThingAfter();// 执行后的逻辑retrunvar;

    前置通知(Before Advice)

    publicinterfaceMethodBeforeAdviceextendsBeforeAdvice{voidbefore(Method m, Object[] args, Object target)throwsThrowable;}

    跟环绕通知不同的是,这个接口中定义的方法的返回值是void,所以前置通知是无法修改方法的返回值的。

    如果在前置通知中发生了异常,那么会直接终止目标方法的执行以及打断整个拦截器链的执行

    后置通知(After Returning Advice)

    publicinterfaceAfterReturningAdviceextendsAdvice{voidafterReturning(Object returnValue, Method m, Object[] args, Object target)throwsThrowable;}

    后置通知相比较于前置通知,主要有以下几点不同

    后置通知可以访问目标方法的返回值,但是不能修改

    后置通知是在方法执行完成后执行

    异常通知(Throws Advice)

    publicinterfaceThrowsAdviceextendsAfterAdvice{}

    异常通知中没有定义任何方法,它更像一个标记接口。我们在定义异常通知时需要实现这个接口,同时方法的签名也有要求

    方法名称必须是afterThrowing

    方法的参数个数必须是1个或者4个,如下:

    publicclassOneParamThrowsAdviceimplementsThrowsAdvice{// 如果只有一个参数,那么这个参数必须是要进行处理的异常publicvoidafterThrowing(RemoteException ex)throwsThrowable{// Do something with remote exception}}publicclassFourParamThrowsAdviceimplementsThrowsAdvice{// 如果定义了四个参数,那么这四个参数分别是// 1.m:目标方法// 2.args:执行目标方法所需要的参数// 3.target:目标对象// 4.ex:具体要处理的异常// 并且参数类型必须按照这个顺序定义publicvoidafterThrowing(Method m, Object[] args, Object target, ServletException ex){// Do something with all arguments}}

    我们可以在一个异常通知中定义多个方法,在后续的源码分析中我们会发现,这些方法最终会被注册成对应的异常的handler,像下面这样

    publicstaticclassCombinedThrowsAdviceimplementsThrowsAdvice{publicvoidafterThrowing(RemoteException ex)throwsThrowable{// Do something with remote exception}publicvoidafterThrowing(Method m, Object[] args, Object target, ServletException ex){// Do something with all arguments}}

    引入通知(Introduction Advice)

    引入通知的主要作用是可以让生成的代理类实现额外的接口。例如在上面的例子中,我们为DmzService创建一个代理对象,同时为其定义了一个引入通知

    publicclassDmzIntroductionAdviceextendsDelegatingIntroductionInterceptorimplementsRunnable{@Overridepublicvoidrun(){System.out.println("running!!!!");}}

    在这个引入通知中,我们为其引入了一个新的需要实现的接口Runnable,同时通知本身作为这个接口的实现类。

    通过这个引入通知,我们可以将生成的代理类强转成Runnable类型然后执行其run方法,同时,run方法也会被前面定义的前置通知,后置通知等拦截。

    为了更好的了解引入通知,我们来需要了解下

    DelegatingIntroductionInterceptor这个类。见名知意,这个类就是一个委托引入拦截器,因为我们要为代理类引入新的接口,因为着我们要提供具体的实现的逻辑,而具体的实现的逻辑就可以被委托给这个DelegatingIntroductionInterceptor。

    我们可以看看它的源码

    publicclassDelegatingIntroductionInterceptorextendsIntroductionInfoSupportimplementsIntroductionInterceptor{// 实际实现了引入逻辑的类@NullableprivateObject delegate;// 对外提供了一个带参的构造函数,通过这个构造函数我们可以传入一个// 具体的实现类publicDelegatingIntroductionInterceptor(Object delegate){init(delegate);}// 对子类暴露了一个空参的构造函数,默认将自身作为实现了引入逻辑的委托类// 我们上面的例子中就是使用的这种方法protectedDelegatingIntroductionInterceptor(){init(this);}// 对这个类进行初始化,要通过实际的实现类来找到具体要实现的接口privatevoidinit(Object delegate){Assert.notNull(delegate,"Delegate must not be null");this.delegate = delegate;// 找到delegate所有实现的接口implementInterfacesOnObject(delegate);// 因为我们可能会将DelegatingIntroductionInterceptor本身作为委托者// Spring的设计就是不对外暴露这两个接口// 如果将其暴露,意味着我们可以将代理类强转成这种类型suppressInterface(IntroductionInterceptor.class);suppressInterface(DynamicIntroductionAdvice.class);}// 引入通知本身也是基于拦截器实现的,当执行一个方法时需要判断这个方法// 是不是被引入的接口中定义的方法,如果是的话,那么不能调用目标类的方法// 而要调用委托类的方法publicObjectinvoke(MethodInvocation mi)throwsThrowable{if(isMethodOnIntroducedInterface(mi)) {Object retVal = AopUtils.invokeJoinpointUsingReflection(this.delegate, mi.getMethod(), mi.getArguments());// 这里是处理一种特殊情况,方法的返回值是this的时候// 这里应该返回代理类if(retVal ==this.delegate && miinstanceofProxyMethodInvocation) {Object proxy = ((ProxyMethodInvocation) mi).getProxy();if(mi.getMethod().getReturnType().isInstance(proxy)) {retVal = proxy;}}// 其余情况下直接将委托类的执行结果返回returnretVal;}// 执行到这里说明不是引入的方法,这是Spring提供了一个扩展逻辑// 正常来说这个类只会处理引入的逻辑,通过这个方法可以对目标类中的方法做拦截// 不常用returndoProceed(mi);}protectedObjectdoProceed(MethodInvocation mi)throwsThrowable{returnmi.proceed();}}

    通过查看这个类的源码我们可以发现,所谓的引入其实就是在方法执行的时候加了一层拦截,当判断这个方法是被引入的接口提供的方法的时候,那么就执行委托类中的逻辑而不是目标类中的方法

    关于通知的总结

    通过上文的分析我们可以发现,通知总共可以分为这么几类

    普通的通知(前置,后置,异常等,没有实现MethodInterceptor接口)

    环绕通知(实现了MethodInterceptor接口)

    引入通知(需要提供额外的引入的信息,实现了MethodInterceptor接口)

    上面的分类并不标准,只是为了方便大家记忆跟理解,虽然我们普通的通知没有直接实现MethodInterceptor接口,但其实它的底层也是依赖于拦截器来完成的,大家可以看看下面这个类

    classMethodBeforeAdviceAdapterimplementsAdvisorAdapter,Serializable{@OverridepublicbooleansupportsAdvice(Advice advice){return(adviceinstanceofMethodBeforeAdvice);}// 根据传入的一个前置通知,创建一个对应的拦截器@OverridepublicMethodInterceptorgetInterceptor(Advisor advisor){MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();returnnewMethodBeforeAdviceInterceptor(advice);}}publicclassMethodBeforeAdviceInterceptorimplementsMethodInterceptor,BeforeAdvice,Serializable{privatefinalMethodBeforeAdvice advice;publicMethodBeforeAdviceInterceptor(MethodBeforeAdvice advice){Assert.notNull(advice,"Advice must not be null");this.advice = advice;}// 实际上还是利用拦截器,在方法执行前调用了通知的before方法完成了前置通知@OverridepublicObjectinvoke(MethodInvocation mi)throwsThrowable{this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());returnmi.proceed();}}

    Advisor (绑定通知跟切点)

    一个Advisor实际上就是一个绑定在指定切点上的通知。在前面的例子我们可以发现,有两种添加通知的方式

    // 一个Advisor代表的是一个已经跟指定切点绑定了的通知// 在这个例子中意味着环绕通知不会作用到toString方法上Advisor advisor =newDefaultPointcutAdvisor(newDmzPointcut(),newDmzAroundAdvice());// 添加一个绑定了指定切点的环绕通知proxyFactory.addAdvisor(advisor);// 添加一个返回后的通知proxyFactory.addAdvice(newDmzAfterReturnAdvice());

    一种是直接添加了一个Advisor,还有一种是添加一个Advice,后者也会被转换成一个Advisor然后再进行添加,没有指定切点的通知是没有任何意义的

    publicvoidaddAdvice(Advice advice)throwsAopConfigException{intpos =this.advisors.size();// 默认添加到集合的最后一个位置addAdvice(pos, advice);}// 这个方法添加通知publicvoidaddAdvice(intpos, Advice advice)throwsAopConfigException{    Assert.notNull(advice,"Advice must not be null");// 如果是一个引入通知,那么构建一个DefaultIntroductionAdvisor// DefaultIntroductionAdvisor会匹配所有类if(adviceinstanceofIntroductionInfo) {        addAdvisor(pos,newDefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));    }// 不能直接添加一个不是IntroductionInfo的DynamicIntroductionAdvice(动态引入通知)elseif(adviceinstanceofDynamicIntroductionAdvice) {thrownewAopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");    }else{// 如果是普通的通知,那么会创建一个DefaultPointcutAdvisor// DefaultPointcutAdvisor所定义的切点会匹配所有类以及所有方法addAdvisor(pos,newDefaultPointcutAdvisor(advice));    }}

    ProxyCreatorSupport

    这个类的主要作用是为创建一个AOP代理对象提供一些功能支持,通过它的getAopProxyFactory能获取一个创建代理对象的工厂

    // 这里我只保留了这个类中的关键代码publicclassProxyCreatorSupportextendsAdvisedSupport{privateAopProxyFactory aopProxyFactory;// 空参构造,默认会创建一个DefaultAopProxyFactory// 通过这个ProxyFactory可以创建一个cglib代理或者jdk代理publicProxyCreatorSupport(){this.aopProxyFactory =newDefaultAopProxyFactory();    }// 通过这个方法可以创建一个具体的代理对象protectedfinalsynchronizedAopProxycreateAopProxy(){if(!this.active) {            activate();        }// 实际就是使用DefaultAopProxyFactory来创建一个代理对象// 可以看到在调用createAopProxy方法时,传入的参数是this// 这是因为ProxyCreatorSupport本身就保存了创建整个代理对象所需要的配置信息returngetAopProxyFactory().createAopProxy(this);    }}

    另外通过上面的UML类图还能看到,ProxyCreatorSupport继承了AdvisedSupport,AdvisedSupport继承了ProxyConfig。

    ProxyConfig

    其中ProxyConfig是所有的AOP代理工厂的父类,它包含了创建一个AOP代理所需要的基础的通用的一些配置信息

    // 这里省略了一些getter跟setter方法publicclassProxyConfigimplementsSerializable{// 是否开启cglib代理,默认不开启使用jdk动态代理privatebooleanproxyTargetClass =false;// 是否启用优化,默认为false,按照官网对这个参数的解释// 这个优化是针对cglib,如果设计为true的话,会做一些侵入性的优化// 是否开启在jdk代理的情况下没有影响// 官网中特地说明了,除非对cglib的优化非常了解,否则不要开启这个参数privatebooleanoptimize =false;// 生成的代理类是否需要实现Advised接口,这个接口可以向外提供操作通知的方法// 如果为false会实现// 为true的话,不会实现booleanopaque =false;// 是否将当前的配置类暴露到一个线程上下文中,如果设置为true的话// 可以通过AopContext.currentProxy()来获取到当前的代理对象booleanexposeProxy =false;// 标志着是否冻结整个配置,如果冻结了,那么配置信息将不允许修改privatebooleanfrozen =false;}

    AdvisedSupport

    当我们为某个对象创建代理时,除了需要上面的ProxyConfig提供的一些基础配置外,起码还需要知道

    需要执行的通知是哪些?

    目标对象是谁?

    创建出来的代理需要实现哪些接口?

    而这些配置信息是由AdvisedSupport提供的,AdvisedSupport本身实现了Advised接口,Advised接口定义了管理通知的方法。

    在了解了上面的API后我们来看看Spring提供了几种创建AOP代理的方式

    ProxyFactoryBean

    ProxyFactory

    Auto-proxy

    ProxyFactoryBean的方式创建AOP代理

    使用示例

    <?xml version="1.0" encoding="UTF-8"?><!---->aroundAdvice

    // 目标类publicclassDmzService{@OverridepublicStringtoString(){System.out.println("dmzService toString invoke");return"dmzService";}publicvoidtestAop(){System.out.println("testAop invoke");}}// 通知publicclassDmzAroundAdviceimplementsMethodInterceptor{@OverridepublicObjectinvoke(MethodInvocation invocation)throwsThrowable{System.out.println("aroundAdvice invoked");returninvocation.proceed();}}publicclassSourceMain{publicstaticvoidmain(String[] args){ClassPathXmlApplicationContext cc =newClassPathXmlApplicationContext("application-init.xml");DmzService dmzProxy = ((DmzService) cc.getBean("dmzProxy"));dmzProxy.testAop();}}

    ProxyFactoryBean介绍

    跟普通的FactoryBean一样,这个类的主要作用就是通过getObject方法能够获取一个Bean,不同的是这个类获取到的是代理后的Bean。

    我们查看这个类的继承关系可以发现

    这个类除了实现了FactoryBean接口以及一些Aware接口外,额外还继承了ProxyCreatorSupport类。它是一个factoryBean,所以我们重点就关注它的getObject方法即可。

    publicObject getObject() throws BeansException {// 初始化通知链// 这里主要就是将在XML中配置的通知添加到// AdvisedSupport管理的配置中去initializeAdvisorChain();if(isSingleton()) {// 如果是单例的,那么获取一个单例的代理对象returngetSingletonInstance();    }else{if(this.targetName ==null) {            logger.warn("Using non-singleton proxies with singleton targets is often undesirable. "+"Enable prototype proxies by setting the 'targetName' property.");        }// 如果是原型的,获取一个原型的代理对象returnnewPrototypeInstance();    }}

    关于这段代码就不做过多分析了,它其实就两步(不管是哪种方式创建代理,都分为这两步)

    完善创建代理需要的配置信息

    创建代理

    其中配置信息分为两部分,其一是AppConfig管理的通用的配置信息,其二是AdvisedSupport管理的通知信息。通用的配置信息我们可以直接在XML中配置,例如在上面的例子中我们就配置了proxyTargetClass属性,而通知信息即使我们在XML中配置了也还需要做一层转换,在前面我们也提到过了,所有的Advice都会被转换成Advisor添加到配置信息中。

    ProxyFactory的方式创建AOP代理

    使用示例(略,见开头)

    ProxyFactory介绍

    从上面我们可以看出,ProxyFactory也继承自ProxyCreatorSupport,从之前的例子我们也能感受到,使用它的API来创建一个代理对象也是要先去设置相关的配置信息,最后再调用创建代理的方法

    我们之后要分析的自动代理内部就是通过创建了一个ProxyFactory来获取代理对象的。

    我们可以对比下ProxyFactoryBean跟ProxyFactory在创建代理对象时的代码

    ProxyFactory

    publicObjectgetProxy(){// 调用了ProxyCreatorSupport的createAopProxy()方法创建一个AopProxy对象// 然后调用AopProxy对象的getProxy方法returncreateAopProxy().getProxy();}

    ProxyFactoryBean

    privatesynchronized Object getSingletonInstance() {if(this.singletonInstance ==null) {this.targetSource = freshTargetSource();if(this.autodetectInterfaces && getProxiedInterfaces().length ==0&& !isProxyTargetClass()) {            Class targetClass = getTargetClass();if(targetClass ==null) {thrownew FactoryBeanNotInitializedException("Cannot determine target class for proxy");            }            setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass,this.proxyClassLoader));        }super.setFrozen(this.freezeProxy);// 重点就看这里// 这里调用了ProxyCreatorSupport的createAopProxy()方法创建一个AopProxy对象// 而getProxy方法就是调用创建的AopProxy的getProxy方法this.singletonInstance = getProxy(createAopProxy());    }returnthis.singletonInstance;}protectedObject getProxy(AopProxy aopProxy) {returnaopProxy.getProxy(this.proxyClassLoader);}

    综上,我们可以得出结论,不管是通过哪种方式创建AOP代理,核心代码就一句

    createAopProxy().getProxy()

    这句代码也是我们接下来源码分析的重点

    Auto-proxy(实现自动AOP代理)

    自动代理机制的实现其实很简单,就是通过Bean的后置处理器,在创建Bean的最后一步对Bean进行代理,并将代理对象放入到容器中。

    实现自动代理的核心类就是AbstractAutoProxyCreator。我们来看看它的继承关系

    为了更好的体会自动代理的作用,我们对它的三个具体的实现类来进行分析,分别是

    BeanNameAutoProxyCreator

    DefaultAdvisorAutoProxyCreator

    AnnotationAwareAspectJAutoProxyCreator

    BeanNameAutoProxyCreator

    使用示例

    <?xml version="1.0" encoding="UTF-8"?><!--使用很简单,只要配置一个BeanNameAutoProxyCreator即可--><!--使用cglib代理--><!--对所有以dmz开头的bean进行自动代理--><!--添加两个通知-->beforeAdvicearoundAdvice

    publicclassSourceMain{publicstaticvoidmain(String[] args){ClassPathXmlApplicationContext cc =newClassPathXmlApplicationContext("application-init.xml");DmzService dmzProxy = ((DmzService) cc.getBean("dmzService"));dmzProxy.testAop();}}// 程序打印:// before invoke method [testAop],aop before logic invoked// aroundAdvice invoked// testAop invoke

    DefaultAdvisorAutoProxyCreator

    使用示例

    在上面例子的基础上我们要修改配置文件,如下:

    <?xml version="1.0" encoding="UTF-8"?><!--这两个参数标明了我们要使用所有以dmz开头的Advisor类型的通知

        这里必须配置是Advisor,不能是Advice或者interceptor,

    可以看到DefaultAdvisorAutoProxyCreator跟BeanNameAutoProxyCreator的区别在于

    BeanNameAutoProxyCreator需要指定要被代理的bean的名称,

    而DefaultAdvisorAutoProxyCreator不需要,它会根据我们传入的Advisor

      获取到需要被代理的切点

    -->

    测试代码就不放了,大家可以自行测试,肯定是没问题的

    AnnotationAwareAspectJAutoProxyCreator

    我们正常在使用AOP的时候都会在配置类上添加一个@EnableAspectJAutoProxy注解,这个注解干了什么事呢?

    实际就是向容器中注册了一个

    AnnotationAwareAspectJAutoProxyCreator。

    @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented// 这里导入了一个类@Import(AspectJAutoProxyRegistrar.class)public@interfaceEnableAspectJAutoProxy {booleanproxyTargetClass()defaultfalse;booleanexposeProxy()defaultfalse;}

    通过@EnableAspectJAutoProxy导入了一个AspectJAutoProxyRegistrar,这个类会向容器中注册一个

    AnnotationAwareAspectJAutoProxyCreator,对应源码如下:

    classAspectJAutoProxyRegistrarimplementsImportBeanDefinitionRegistrar{@OverridepublicvoidregisterBeanDefinitions(

    AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){// 在这里完成的注册// 最终会调用到AopUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法// 完成AnnotationAwareAspectJAutoProxyCreator这个bd的注册AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);// 解析注解的属性// proxyTargetClass:为true的话开启cglib代理,默认为jdk代理// exposeProxy:是否将代理对象暴露到线程上下文中AnnotationAttributes enableAspectJAutoProxy =AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);if(enableAspectJAutoProxy !=null) {if(enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}if(enableAspectJAutoProxy.getBoolean("exposeProxy")) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}

    前面已经说过了,自动代理机制实际上就是Spring在内部new了一个ProxyFactory,通过它创建了一个代理对象。对应的代码就在AbstractAutoProxyCreator中的createProxy方法内,源码如下:

    protectedObject createProxy(Class beanClass,@NullableString beanName,@NullableObject[] specificInterceptors, TargetSource targetSource) {if(this.beanFactory instanceof ConfigurableListableBeanFactory) {        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory, beanName, beanClass);    }// 看到了吧,这里创建了一个proxyFactoryProxyFactory proxyFactory = new ProxyFactory();    proxyFactory.copyFrom(this);if(!proxyFactory.isProxyTargetClass()) {if(shouldProxyTargetClass(beanClass, beanName)) {            proxyFactory.setProxyTargetClass(true);        }else{            evaluateProxyInterfaces(beanClass, proxyFactory);        }    }    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);    proxyFactory.addAdvisors(advisors);    proxyFactory.setTargetSource(targetSource);    customizeProxyFactory(proxyFactory);    proxyFactory.setFrozen(this.freezeProxy);if(advisorsPreFiltered()) {        proxyFactory.setPreFiltered(true);    }// 通过proxyFactory来创建一个代理对象returnproxyFactory.getProxy(getProxyClassLoader());}

    关于这个类的执行流程在下篇文章中我再详细介绍,接下来我们要分析的就是具体创建AOP代理的源码了。对应的核心源码就是我们之前所提到的

    createAopProxy().getProxy();

    这行代码分为两步,我们逐步分析

    调用AopProxyFactory的createAopProxy()方法获取一个AopProxy对象

    调用AopProxy对象的getProxy()方法

    核心源码分析

    createAopProxy方法分析

    AopProxyFactory在Spring中只有一个默认的实现类,就是DefaultAopProxyFactory,它的对应的createAopProxy的是实现代码如下:

    publicclassDefaultAopProxyFactoryimplementsAopProxyFactory,Serializable {// 就是通过AOP相关的配置信息来决定到底是使用cglib代理还是jdk代理@OverridepublicAopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {// 如果开启了优化,或者ProxyTargetClass设置为true// 或者没有提供代理类需要实现的接口,那么使用cglib代理// 在前面分析参数的时候已经说过了// 默认情况下Optimize都为false,也不建议设置为true,因为会进行一些侵入性的优化// 除非你对cglib的优化非常了解,否则不建议开启if(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {            Class targetClass = config.getTargetClass();if(targetClass ==null) {thrownew AopConfigException("TargetSource cannot determine target class: "+"Either an interface or a target is required for proxy creation.");            }// 需要注意的是,如果需要代理的类本身就是一个接口// 或者需要被代理的类本身就是一个通过jdk动态代理生成的类// 那么不管如何设置都会使用jdk动态代理if(targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {returnnew JdkDynamicAopProxy(config);            }returnnew ObjenesisCglibAopProxy(config);        }// 否则都是jdk代理else{returnnew JdkDynamicAopProxy(config);        }    }// 判断是否提供代理类需要实现的接口privateboolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {        Class[] ifcs = config.getProxiedInterfaces();return(ifcs.length ==0|| (ifcs.length ==1&& SpringProxy.class.isAssignableFrom(ifcs[0])));    }}

    getProxy方法分析

    从对createAopProxy方法的分析可以看到,我们要么执行的是ObjenesisCglibAopProxy中的getProxy方法,要么就是JdkDynamicAopProxy的getProxy方法,二者的区别在于一个是通过cglib的方式生成代理对象,而后者则是通过jdk的方式生成动态代理。

    这里我只分析一个JdkDynamicAopProxy,首先我们来看看这个类的继承关系

    可以看到这个类本身就是一个InvocationHandler,这意味着当调用代理对象中的方法时,最终会调用到JdkDynamicAopProxy的invoke方法。

    所以对于这个类我们起码应该关注两个方法

    getProxy方法

    invoke方法

    getProxy方法源码如下:

    publicObject getProxy(@NullableClassLoader classLoader) {if(logger.isDebugEnabled()) {        logger.debug("Creating JDK dynamic proxy: target source is "+this.advised.getTargetSource());    }// 这里获取到代理类需要实现的所有的接口Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised,true);// 需要明确是否在接口定义了hashCode以及equals方法// 如果接口中没有定义,那么在调用代理对象的equals方法的时候// 如果两个对象相等,那么意味着它们的目标对象,通知以及实现的接口都相同findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);returnProxy.newProxyInstance(classLoader, proxiedInterfaces,this);}

    我们再来看看到底是怎么获取到需要实现的接口的

    staticClass[] completeProxiedInterfaces(AdvisedSupport advised,booleandecoratingProxy) {// 第一步:获取在配置中指定的需要实现的接口Class[] specifiedInterfaces = advised.getProxiedInterfaces();// 第二步:如果没有指定需要实现的接口,但是需要代理的目标类本身就是一个接口// 那么将其添加到代理类需要实现的接口的集合中// 如果目标类本身不是一个接口,但是是经过jdk代理后的一个类// 那么获取这个代理后的类所有实现的接口,并添加到需要实现的接口集合中if(specifiedInterfaces.length ==0) {        Class targetClass = advised.getTargetClass();if(targetClass !=null) {if(targetClass.isInterface()) {                advised.setInterfaces(targetClass);            }elseif(Proxy.isProxyClass(targetClass)) {                advised.setInterfaces(targetClass.getInterfaces());            }            specifiedInterfaces = advised.getProxiedInterfaces();        }    }// 第三步:为代理类添加三个默认需要实现的接口,分别是// 1.SpringProxy,一个标记接口,代表这个类是通过Spring的AOP代理生成的// 2.Advised,提供了管理通知的方法// 3.DecoratingProxy,用户获取到真实的目标对象// 这个真实对象指的是在嵌套代理的情况下会获取到最终的目标对象// 而不是指返回这个ProxyFactory的targetbooleanaddSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);booleanaddAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);booleanaddDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));intnonUserIfcCount =0;if(addSpringProxy) {        nonUserIfcCount++;    }if(addAdvised) {        nonUserIfcCount++;    }if(addDecoratingProxy) {        nonUserIfcCount++;    }    Class[] proxiedInterfaces =newClass[specifiedInterfaces.length + nonUserIfcCount];    System.arraycopy(specifiedInterfaces,0, proxiedInterfaces,0, specifiedInterfaces.length);intindex = specifiedInterfaces.length;if(addSpringProxy) {        proxiedInterfaces[index] = SpringProxy.class;        index++;    }if(addAdvised) {        proxiedInterfaces[index] = Advised.class;        index++;    }if(addDecoratingProxy) {        proxiedInterfaces[index] = DecoratingProxy.class;    }returnproxiedInterfaces;}

    invoke方法分析

    在确认了需要实现的接口后,直接调用了jdk的动态代理方法,这个我们就不做分析了,接下来我们来看看Spring是如何将通知应用到代理对象上的,对应的要分析的代码就是JdkDynamicAopProxy的invoke方法,源码如下:

    // 这个方法的代码稍微有点长,代码也比较难,希望大家能耐心看完publicObject invoke(Object proxy, Method method, Object[] args) throws Throwable {    Object oldProxy =null;    boolean setProxyContext =false;    TargetSource targetSource =this.advised.targetSource;    Object target =null;try{// 首先处理的是hashCode跟equals方法// 如果接口中没有定义这两个方法,那么会调用本类中定义的equals方法// 前面我们也说过了,只有当两个类的目标对象,通知以及实现的接口都相等的情况下// equals才会返回true// 如果接口中定义了这两个方法,那么最终会调用目标对象中的方法if(!this.equalsDefined && AopUtils.isEqualsMethod(method)) {returnequals(args[0]);        }elseif(!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {returnhashCode();        }// 也就是说我们调用的是DecoratingProxy这个接口中的方法// 这个接口中只定义了一个getDecoratedClass方法,用于获取到// 最终的目标对象,在方法实现中会通过一个while循环来不断接近// 最终的目标对象,直到得到的目标对象不是一个被代理的对象才会返回elseif(method.getDeclaringClass() == DecoratingProxy.class){returnAopProxyUtils.ultimateTargetClass(this.advised);        }// 说明调用的是Advised接口中的方法,这里只是单纯的进行反射调用elseif(!this.advised.opaque && method.getDeclaringClass().isInterface() &&                method.getDeclaringClass().isAssignableFrom(Advised.class)){returnAopUtils.invokeJoinpointUsingReflection(this.advised, method, args);        }        Object retVal;// 说明需要将代理类暴露到线程上下文中// 调用AopContext.setCurrentProxy方法将其放入到一个threadLocal中if(this.advised.exposeProxy) {            oldProxy = AopContext.setCurrentProxy(proxy);            setProxyContext =true;        }// 接下来就是真正的执行代理逻辑了target = targetSource.getTarget();        Class targetClass = (target !=null? target.getClass() :null);// 先获取整个拦截器链List chain =this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// 如果没有进行拦截,直接反射调用方法if(chain.isEmpty()) {            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);        }// 否则开始执行整个链条else{            MethodInvocation invocation =                new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);            retVal = invocation.proceed();        }// 这里是处理一种特殊情况,就是当执行的方法返回值为this的情况// 这种情况下,需要返回当前的代理对象而不是目标对象Class returnType = method.getReturnType();if(retVal !=null&& retVal == target &&            returnType != Object.class&&returnType.isInstance(proxy) &&            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {            retVal = proxy;        }elseif(retVal ==null&& returnType !=Void.TYPE && returnType.isPrimitive()) {thrownew AopInvocationException("Null return value from advice does not match primitive return type for: "+ method);        }returnretVal;    }finally{if(target !=null&& !targetSource.isStatic()) {            targetSource.releaseTarget(target);        }if(setProxyContext) {            AopContext.setCurrentProxy(oldProxy);        }    }}

    在上面整个流程中,我们抓住核心的两步

    获取整个拦截器链

    开始在拦截器链上执行方法

    我们先看第一步,对应源码如下:

    publicList getInterceptorsAndDynamicInterceptionAdvice(Method method,@NullableClass targetClass) {    MethodCacheKey cacheKey = new MethodCacheKey(method);    List cached =this.methodCache.get(cacheKey);if(cached ==null) {// 调用了advisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice方法cached =this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);this.methodCache.put(cacheKey, cached);    }returncached;}

    publicList getInterceptorsAndDynamicInterceptionAdvice(    Advised config, Method method,@NullableClass targetClass) {    List interceptorList =newArrayList(config.getAdvisors().length);    Class actualClass = (targetClass !=null? targetClass : method.getDeclaringClass());// 是否有引入通知booleanhasIntroductions = hasMatchingIntroductions(config, actualClass);    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();// 获取到所有的通知for(Advisor advisor : config.getAdvisors()) {// 除了引入通知外,可以认为所有的通知都是一个PointcutAdvisorif(advisorinstanceofPointcutAdvisor) {            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;// config.isPreFiltered:代表的是配置已经过滤好了,是可以直接应用的// 这句代码的含义就是配置是预过滤的或者在类级别上是匹配的if(config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {// 接下来要判断在方法级别上是否匹配MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();if(MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {// 将通知转换成对应的拦截器// 有些通知本身就是拦截器,例如环绕通知// 有些通知需要通过一个AdvisorAdapter来适配成对应的拦截器// 例如前置通知,后置通知,异常通知等// 其中MethodBeforeAdvice会被适配成MethodBeforeAdviceInterceptor// AfterReturningAdvice会被适配成AfterReturningAdviceInterceptor// ThrowAdvice会被适配成ThrowsAdviceInterceptorMethodInterceptor[] interceptors = registry.getInterceptors(advisor);// 如果是动态的拦截,会创建一个InterceptorAndDynamicMethodMatcher// 动态的拦截意味着需要根据具体的参数来决定是否进行拦截if(mm.isRuntime()) {for(MethodInterceptor interceptor : interceptors) {                            interceptorList.add(newInterceptorAndDynamicMethodMatcher(interceptor, mm));                        }                    }else{                        interceptorList.addAll(Arrays.asList(interceptors));                    }                }            }        }elseif(advisorinstanceofIntroductionAdvisor) {// 说明是引入通知IntroductionAdvisor ia = (IntroductionAdvisor) advisor;if(config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {// 前文我们有提到过,引入通知实际就是通过一个拦截器// 将方法交由引入的类执行而不是目标类Interceptor[] interceptors = registry.getInterceptors(advisor);                interceptorList.addAll(Arrays.asList(interceptors));            }        }else{// 可能会扩展出一些通知,一般不会Interceptor[] interceptors = registry.getInterceptors(advisor);            interceptorList.addAll(Arrays.asList(interceptors));        }    }returninterceptorList;}

    在构建好拦截器链后,接下来就是真正执行方法了,对应代码就是

    // 先创建一个MethodInvocationMethodInvocation invocation =newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);// 开始在拦截器链上执行这个方法retVal = invocation.proceed();

    最后的关键代码就落在了

    ReflectiveMethodInvocation的proceed方法

    publicObject proceed() throws Throwable {// 满足这个条件,说明执行到了最后一个拦截器,那么直接反射调用目标方法if(this.currentInterceptorIndex ==this.interceptorsAndDynamicMethodMatchers.size() -1) {returninvokeJoinpoint();  }// 获取到下一个要执行的拦截器Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if(interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {// 前面构建拦截器链的时候我们可以看到,动态的拦截的话会创建一个InterceptorAndDynamicMethodMatcherInterceptorAndDynamicMethodMatcher dm =            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;if(dm.methodMatcher.matches(this.method,this.targetClass,this.arguments)) {returndm.interceptor.invoke(this);      }else{// 如果匹配失败了,执行拦截器链中的下一个拦截逻辑returnproceed();      }  }else{// 调用拦截器中的invoke方法,可以看到这里将this作为参数传入了// 所以我们在拦截器中调用 MethodInvocation的proceed时又会进行入当前这个方法// 然后去执行链条中的下一个拦截器 return((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);  }}

    总结

    本文主要是为下篇文章做准备,下篇文章将会结束整个IOC流程的分析,IOC的最后一步便是为Bean创建代理。本文已经分析了代理的具体创建逻辑,在下篇文章中我们主要结合Spring的启动流程来看一看Spring是如何将通知添加到创建代理的配置信息中去的。

    关于整个IOC跟AOP的模块还会有两篇文章,一篇用于结束整个IOC流程,另外一篇专门探讨Spring中循环依赖的解决。

    相关文章

      网友评论

        本文标题:Spring中AOP相关的API及源码解析,原来AOP是这样子的

        本文链接:https://www.haomeiwen.com/subject/egotbrtx.html