0


【Spring从成神到升仙系列 三】2023年再不会 AOP 源码,就要被淘汰了

  • 👏作者简介:大家好,我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,阿里云专家博主
  • 📕系列专栏:Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙、Spring从成神到升仙系列
  • 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
  • 🍂博主正在努力完成2023计划中:以梦为马,扬帆起航,2023追梦人
  • 📝联系方式:hls1793929520,和大家一起学习,一起进步👀

在这里插入图片描述

文章目录

Spring AOP源码解析

一、引言

对于Java开发者而言,关于

Spring

,我们一般当做黑盒来进行使用,不需要去打开这个黑盒。

但随着目前程序员行业的发展,我们有必要打开这个黑盒,去探索其中的奥妙。

本期

Spring

源码解析系列文章,将带你领略

Spring

源码的奥秘

本期源码文章吸收了之前

Kafka

源码文章的错误,将不再一行一行的带大家分析源码,我们将一些不重要的部分当做黑盒处理,以便我们更快、更有效的阅读源码。

废话不多说,发车!

本文流程图可关注公众号:爱敲代码的小黄,回复:AOP 获取
贴心的小黄为大家准备的文件格式为 POS文件,方便大家直接导入 ProcessOn 修改使用

二、适合人群

本文采用的

Spring

版本为:4.3.11.RELEASE

本文适合人群:使用过Spring AOP的功能,了解过切面编程

本文前置知识:

  • Spring-IOC源码剖析(必看)
  • Spring-代理源码剖析(必看)

如果你都已经满足,那么跟我一起了解一下抽象的

AOP

吧。

三、Spring AOP配置

首先创建我们的核心类:BeanA

publicclassBeanA{publicvoiddo1(){System.out.println("I am a do1, I start....");}}

我们日志配置类:LogUtil

publicclassLogUtil{privatevoidbefore(JoinPoint joinPoint){//获取方法签名Signature signature = joinPoint.getSignature();//获取参数信息Object[] args = joinPoint.getArgs();System.out.println("log---"+ signature.getName()+"I am before");}privatevoidafter(JoinPoint joinPoint){//获取方法签名Signature signature = joinPoint.getSignature();//获取参数信息Object[] args = joinPoint.getArgs();System.out.println("log---"+ signature.getName()+"I am before");}}

我们

xml

配置:application.xml

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd"><beanid="beanA"class="com.hls.aop.BeanA"/><beanid="logUtil"class="com.hls.aop.LogUtil"/><aop:config><aop:aspectref="logUtil"><aop:pointcutid="myPoint"expression="execution(* com.hls.aop.BeanA.do*(..))"/><aop:beforemethod="before"pointcut-ref="myPoint"/><aop:aftermethod="after"pointcut-ref="myPoint"/></aop:aspect></aop:config></beans>

编写我们的测试类:AOPTest

publicclassAOPTest{publicstaticvoidmain(String[] args){ApplicationContext context =newGenericXmlApplicationContext("application.xml");BeanA beanA = context.getBean(BeanA.class);

        beanA.do1();}}

运行输出结果:

log---do1I am before
I am a do1,I start....
log---do1I am before

四、Spring AOP 组件分析

AOP

的组件在源码中比较重要的有四个:

  • Pointcut:定义切面的匹配点,主要是类和方法
  • Advice:定义切面的行为,即在匹配点执行的操作。
  • Advisor:将 Pointcut 和 Advice 组合成一个对象,表示一个完整的切面。
  • Aspect:使用注解或 XML 配置方式定义切面,通常包含多个 Advisor

我们依次讲解这些组件是什么意思

1、Pointcut

一般情况下我们的

Pointcut

都是以这种形式出现:

<aop:pointcut id="myPoint" expression="execution(* com.mashibing.hls.aop.BeanA.do*(..))"/>

主要就是指明切入点的表达式,比如上述表达式:

* com.hls.aop.BeanA.do*(..)

代表

com.hls.aop

包下的

BeanA

类以

do

开头的方法都可以进行切入

我们看下源码中的形式:

publicinterfacePointcut{/**
     * 在类级别上限定joinpoint的匹配范围
     *
     * Return the ClassFilter for this pointcut.
     * @return the ClassFilter (never {@code null})
     */ClassFiltergetClassFilter();/**
     * 在方法级别上限定joinpoint的匹配范围
     *
     * Return the MethodMatcher for this pointcut.
     * @return the MethodMatcher (never {@code null})
     */MethodMatchergetMethodMatcher();/**
     * 用于匹配上的一个实例,永远返回true
     *
     * Canonical Pointcut instance that always matches.
     */Pointcut TRUE =TruePointcut.INSTANCE;}// Pointcut 的实现publicclassAspectJExpressionPointcutextendsAbstractExpressionPointcutimplementsClassFilter,IntroductionAwareMethodMatcher,BeanFactoryAware{// 类的匹配publicbooleanmatches(Class<?> targetClass){}// 方法的匹配 publicbooleanmatches(Method method,Class<?> targetClass,boolean hasIntroductions){}}

通过源码我们可以发现,这个类提供的功能主要是用来匹配类与方法。

2、Advice

一般情况下,我们的

Advice

以这种情况出现:

<aop:before/><aop:after/>

主要的目的就是定义我们切面的行为,比如:方法前切入、方法后切入、环绕切入等

我们看下源码的形式:

publicinterfaceAdvice{}// 前置切入实现publicinterfaceBeforeAdviceextendsAdvice{}// 后置切入实现publicinterfaceAfterAdviceextendsAdvice{}

一般我们的

Advice

都与

Pointcut

相结合使用

3、Advisor

一般情况下,我们的

Advisor

以这种情况出现:

<aop:before method="before" pointcut-ref="myPoint"/><aop:after method="after" pointcut-ref="myPoint"/>

Advice

Pointcut

结合起来使用

我们看下源码的形式:

publicinterfaceAdvisor{// 返回切面对应的通知AdvicegetAdvice();}

很明显可以看出,这个类主要包装了

Advice

,后续我们会继续讲到

4、Aspect

一般情况下,我们的

Aspect

以这种情况出现:

<aop:aspectref="logUtil"></aop:aspect>

定义一个日志配置类,其方法作为实际的切入业务逻辑

5、总结

从我们上述的描述得知,我们几个组件的关系如下:

在这里插入图片描述

大家这里心里有个大致的了解即可,我们后面会详细讲到。

五、Spring AOP 源码剖析

同样和我们的

IOC

类似,我们从入口开始:

ApplicationContext context =newGenericXmlApplicationContext("application.xml");

1、beanDefinition的注册

同样和我们

IOC

流程一样,先进行

xml

文件的解析

我们直接找到

DefaultBeanDefinitionDocumentReader

类的

parseBeanDefinitions

方法

这里从业务来说,做了两件事:

  • 注册了 AspectJAwareAdvisorAutoProxyCreator 的类,方便之后代理的扩展
  • 解析 xml<aop:config> 的配置,最终生成 AspectJPointcutAdvisorBeanDefinition 注册至 DefaultListableBeanFactoryBeanDefinitionMap

1.1 AspectJAwareAdvisorAutoProxyCreator的注册

protectedvoidparseBeanDefinitions(Element root,BeanDefinitionParserDelegate delegate){if(delegate.isDefaultNamespace(root)){NodeList nl = root.getChildNodes();for(int i =0; i < nl.getLength(); i++){Node node = nl.item(i);if(node instanceofElement){Element ele =(Element) node;if(delegate.isDefaultNamespace(ele)){// 正常的标签,IOC中讲过parseDefaultElement(ele, delegate);}else{// <AOP>、<dubbo> 类的标签
                        delegate.parseCustomElement(ele);}}}}}publicBeanDefinitionparseCustomElement(Element ele){returnparseCustomElement(ele,null);}publicBeanDefinitionparseCustomElement(Element ele,@NullableBeanDefinition containingBd){// 获取对应的命名空间String namespaceUri =getNamespaceURI(ele);// 根据命名空间找到对应的NamespaceHandlerspring//  因为我们这里处理的是 AOP 的标签,所以会有 AopNamespaceHandler 的执行NamespaceHandler handler =this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);// 调用自定义的NamespaceHandler(AopNamespaceHandler)进行解析return handler.parse(ele,newParserContext(this.readerContext,this, containingBd));}publicBeanDefinitionparse(Element element,ParserContext parserContext){// 获取元素的解析器BeanDefinitionParser parser =findParserForElement(element, parserContext);return(parser !=null? parser.parse(element, parserContext):null);}publicBeanDefinitionparse(Element element,ParserContext parserContext){CompositeComponentDefinition compositeDef =newCompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
        parserContext.pushContainingComponent(compositeDef);// 注册自动代理模式创建器,AspectjAwareAdvisorAutoProxyCreatorconfigureAutoProxyCreator(parserContext, element);}privatevoidconfigureAutoProxyCreator(ParserContext parserContext,Element element){AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);}publicstaticvoidregisterAspectJAutoProxyCreatorIfNecessary(ParserContext parserContext,Element sourceElement){// 注册名为org.springframework.aop.config.internalAutoProxyCreator的beanDefinition,其中的class类为`AspectJAwareAdvisorAutoProxyCreator`,其也会被注册到bean工厂中BeanDefinition beanDefinition =AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
                parserContext.getRegistry(), parserContext.extractSource(sourceElement));// 如果指定proxy-target-class=true,则使用CGLIB代理,否则使用JDK代理// 其实其为AspectJAwareAdvisorAutoProxyCreator类的proxyTargetClass属性useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);// 注册到spring的bean工厂中,再次校验是否已注册registerComponentIfNecessary(beanDefinition, parserContext);}publicstaticBeanDefinitionregisterAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,@NullableObject source){returnregisterOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);}privatestaticBeanDefinitionregisterOrEscalateApcAsRequired(Class<?> cls,BeanDefinitionRegistry registry,@NullableObject source){// cls = AspectJAwareAdvisorAutoProxyCreatorRootBeanDefinition beanDefinition =newRootBeanDefinition(cls);
        beanDefinition.setSource(source);
        beanDefinition.getPropertyValues().add("order",Ordered.HIGHEST_PRECEDENCE);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);// key = org.springframework.aop.config.internalAutoProxyCreator// value = org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator// 这里正式注册了 !!!
        registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);return beanDefinition;}

有读者可能疑惑,这里为什么要大费周折注册一个

AspectJAwareAdvisorAutoProxyCreator

这个类,他有什么特殊的地方嘛

这里先卖个关子,后续我们会讲到

1.2、AspectJPointcutAdvisor 的注册

由于这一段的注册逻辑写的很绕、很晦涩难懂,这里博主带大家大致的浏览下流程

不然,直接劝退了大部分人

我们跟着上面这部分源码继续往下看:

publicBeanDefinitionparse(Element element,ParserContext parserContext){CompositeComponentDefinition compositeDef =newCompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
        parserContext.pushContainingComponent(compositeDef);// 注册自动代理模式创建器,AspectjAwareAdvisorAutoProxyCreatorconfigureAutoProxyCreator(parserContext, element);// 解析aop:config子节点下的aop:pointcut/aop:advice/aop:aspectList<Element> childElts =DomUtils.getChildElements(element);for(Element elt: childElts){String localName = parserContext.getDelegate().getLocalName(elt);if(POINTCUT.equals(localName)){parsePointcut(elt, parserContext);}elseif(ADVISOR.equals(localName)){parseAdvisor(elt, parserContext);}// 因为我们上面是 <aop:aspect/> 的标签,会走这个业务逻辑elseif(ASPECT.equals(localName)){parseAspect(elt, parserContext);}}

        parserContext.popAndRegisterContainingComponent();returnnull;}privatevoidparseAspect(Element aspectElement,ParserContext parserContext){// <aop:aspect> id属性String aspectId = aspectElement.getAttribute(ID);// aop ref属性,必须配置。代表切面String aspectName = aspectElement.getAttribute(REF);try{List<BeanDefinition> beanDefinitions =newArrayList<>();List<BeanReference> beanReferences =newArrayList<>();// 解析其下的advice节点NodeList nodeList = aspectElement.getChildNodes();boolean adviceFoundAlready =false;for(int i =0; i < nodeList.getLength(); i++){Node node = nodeList.item(i);// 是否为advice:before/advice:after/advice:after-returning/advice:after-throwing/advice:around节点// 这个就是 advice 的种类,不明白的小伙伴可以百度一下if(isAdviceNode(node, parserContext)){// 校验aop:aspect必须有ref属性,否则无法对切入点进行观察操作if(!adviceFoundAlready){
                        adviceFoundAlready =true;if(!StringUtils.hasText(aspectName)){return;}
                        beanReferences.add(newRuntimeBeanReference(aspectName));}// 解析advice节点并注册到bean工厂中AbstractBeanDefinition advisorDefinition =parseAdvice(
                            aspectName, i, aspectElement,(Element) node, parserContext, beanDefinitions, beanReferences);
                    beanDefinitions.add(advisorDefinition);}}AspectComponentDefinition aspectComponentDefinition =createAspectComponentDefinition(
                    aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
            parserContext.pushContainingComponent(aspectComponentDefinition);// 解析aop:point-cut节点并注册到bean工厂List<Element> pointcuts =DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);for(Element pointcutElement : pointcuts){parsePointcut(pointcutElement, parserContext);}

            parserContext.popAndRegisterContainingComponent();}finally{this.parseState.pop();}}

到这里我们先停一下,从上述源码中我们得到了一些信息,这部分源码的注册基本就三个方面:

  • 解析 Advice 节点并注册到 bean 工厂中
  • 解析 Advisor 节点注册到 bean 工厂中
  • 解析 Pointcut 节点并注册到 bean 工厂
// 整体的注册方法AbstractBeanDefinition advisorDefinition =parseAdvice(aspectName, i, aspectElement,(Element) node, parserContext, beanDefinitions, beanReferences);privateAbstractBeanDefinitionparseAdvice(String aspectName,int order,Element aspectElement,Element adviceElement,ParserContext parserContext,List<BeanDefinition> beanDefinitions,List<BeanReference> beanReferences){// 第一步:解析advice节点中的 method 属性,将其创建成一个 beanDefinition 对象RootBeanDefinition methodDefinition =newRootBeanDefinition(MethodLocatingFactoryBean.class);
    methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
    methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
     methodDefinition.setSynthetic(true);// 第二步:关联aspectName,包装为 SimpleBeanFactoryAwareAspectInstanceFactory 对象RootBeanDefinition aspectFactoryDef =newRootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
     aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
    aspectFactoryDef.setSynthetic(true);// 第三步:涉及point-cut属性的解析,并结合上述的两个bean最终包装为AbstractAspectJAdvice通知对象// 这里面的逻辑也很简单,主要就是通过我们的adviceElement分析是什么类型的 Advice 类型,进行对应的创建AbstractBeanDefinition adviceDef =createAdviceDefinition( adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef, beanDefinitions, beanReferences);// 第四步:最终包装为AspectJPointcutAdvisor对象RootBeanDefinition advisorDefinition =newRootBeanDefinition(AspectJPointcutAdvisor.class);
    advisorDefinition.setSource(parserContext.extractSource(adviceElement));
     advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);// 第五步:注册到我们 DefaultListableBeanFactory 的 BeanDefinitionMap 中
    parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);// 第六步:返回生成好的advisorDefinitionreturn advisorDefinition;}publicStringregisterWithGeneratedName(BeanDefinition beanDefinition){String generatedName =generateBeanName(beanDefinition);getRegistry().registerBeanDefinition(generatedName, beanDefinition);return generatedName;}publicvoidregisterBeanDefinition(String beanName,BeanDefinition beanDefinition){// 老生常谈的注册逻辑了,之前IOC的时候讲过this.beanFactory.registerBeanDefinition(beanName, beanDefinition);}

1.3 组件源码的结构

这里有一个小细节,面试的时候吹牛逼使用,记住了

我们从我们的第五步,拿出我们的

advisorDefinition

,看一下他的构造方法:

在这里插入图片描述

我们看到其构造方法实际是一个

AspectJAfterAdvice

,我们继续看

AspectJAfterAdvice

的构造方法

在这里插入图片描述

其构造方法有三个传参,我们依次看一下什么意思:

  • MethodLocatingFactoryBean:指明了需要插入的类(logUtil)和方法(after在这里插入图片描述
  • BeanReference:存储了切入点的 id,由 myPoint 后续可直接找到 execution(* com.mashibing.hls.aop.BeanA.do*(..))
  • SimpleBeanFactoryAwareAspectInstanceFactory:内有 BeanFactory 的引用

看完这波,我们可以重新梳理一下关系:

在这里插入图片描述

我们最终的组件关系是这种的,但这种的组件关系又会带来一个问题,大家可以先想想

对,就是真正实例化的时候,会特别的复杂

当我们实例化

Advisor

时,需要实例化

Advice

,当我们实例化

Advice

时,需要实例化

Method

Pointcut

Factory

,直接崩溃

通过上述的讲述,我们终于将

AspectJAwareAdvisorAutoProxyCreator

AspectJPointcutAdvisor 

注册到了

BeanDefinitionMap

里面

2、AspectJPointcutAdvisor 的创建

上面注册完成之后,就要到我们的创建逻辑了,这里我们直接跳到

AbstractAutowireCapableBeanFactory

createBean

方法的

Object bean = resolveBeforeInstantiation(beanName, mbdToUse)

这一行

protectedObjectresolveBeforeInstantiation(String beanName,RootBeanDefinition mbd){Object bean =null;// 如果beforeInstantiationResolved值为null或者true,那么表示尚未被处理,进行后续的处理if(!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)){// Make sure bean class is actually resolved at this point.// 确认beanclass确实在此处进行处理// 判断当前mbd是否是合成的,只有在实现aop的时候synthetic的值才为true,并且是否实现了InstantiationAwareBeanPostProcessor接口if(!mbd.isSynthetic()&&hasInstantiationAwareBeanPostProcessors()){// 获取类型Class<?> targetType =determineTargetType(beanName, mbd);if(targetType !=null){// 重点看这里!
                    bean =applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);if(bean !=null){
                        bean =applyBeanPostProcessorsAfterInitialization(bean, beanName);}}}// 是否解析了
            mbd.beforeInstantiationResolved =(bean !=null);}return bean;}protectedObjectapplyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass,String beanName){// 拿到所有的 BeanPostProcessors 的实现类,调用其前置方法// 主要是我们的 AspectJAwareAdvisorAutoProxyCreator 的调用for(BeanPostProcessor bp :getBeanPostProcessors()){if(bp instanceofInstantiationAwareBeanPostProcessor){InstantiationAwareBeanPostProcessor ibp =(InstantiationAwareBeanPostProcessor) bp;Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);if(result !=null){return result;}}}returnnull;}publicObjectpostProcessBeforeInstantiation(Class<?> beanClass,String beanName){Object cacheKey =getCacheKey(beanClass, beanName);if(!StringUtils.hasLength(beanName)||!this.targetSourcedBeans.contains(beanName)){//查缓存,是否有处理过了,不管是不是需要通知增强的,只要处理过了就会放里面// 当前beandeif(this.advisedBeans.containsKey(cacheKey)){returnnull;}if(isInfrastructureClass(beanClass)||shouldSkip(beanClass, beanName)){// 要跳过的直接设置FALSEthis.advisedBeans.put(cacheKey,Boolean.FALSE);returnnull;}}// Advice、Pointcut、Advisor、AopInfrastructureBean直接返回 TrueprotectedbooleanisInfrastructureClass(Class<?> beanClass){boolean retVal =Advice.class.isAssignableFrom(beanClass)||Pointcut.class.isAssignableFrom(beanClass)||Advisor.class.isAssignableFrom(beanClass)||AopInfrastructureBean.class.isAssignableFrom(beanClass);return retVal;}// 重点到了protectedbooleanshouldSkip(Class<?> beanClass,String beanName){// 获取所有的 Advisors 类List<Advisor> candidateAdvisors =findCandidateAdvisors();for(Advisor advisor : candidateAdvisors){if(advisor instanceofAspectJPointcutAdvisor&&((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)){returntrue;}}returnsuper.shouldSkip(beanClass, beanName);}protectedList<Advisor>findCandidateAdvisors(){// 获取所有的增强处理returnthis.advisorRetrievalHelper.findAdvisorBeans();}publicList<Advisor>findAdvisorBeans(){// 第一步:拿到所有的 advisorBeanNamesString[] advisorNames =this.cachedAdvisorBeanNames;// 根据每一个 BeanName 都需要使用 beanFactory 生成实例化List<Advisor> advisors =newArrayList<>();for(String name : advisorNames){
        advisors.add(this.beanFactory.getBean(name,Advisor.class));}// 得到所有的advisors实例化return advisors;}

2.1 Advisor 层级

到这里,我们的

AspectJPointcutAdvisor

已经创建,我们看一下其结构:

在这里插入图片描述
这里又回到了这种层级:
在这里插入图片描述

这个

Advisor

作为

Advice

Pointcut

的对外接口,具体功能还得看

Advice

Pointcut
  • Advice:切入的类、切入的方法、切入的时机
  • Pointcut:是否匹配类、是否匹配方法

2.2 创建实例的问题

如果你亲自 Debug 源码的话,你会发现它跑了好多的

GetBean()

的次数

这就是我之前提到的创建实例化的时候非常的烦人

好在

Spring

自己也做了一些判断,减少一些不必要的程序运行

比如:

if(this.advisedBeans.containsKey(cacheKey)){returnnull;}if(isInfrastructureClass(beanClass)||shouldSkip(beanClass, beanName)){// 要跳过的直接设置FALSEthis.advisedBeans.put(cacheKey,Boolean.FALSE);returnnull;}

3、创建代理对象

我们上面的

Advisor

终于创建完成了,可以开始我们的下一步了

直接跳到

AbstractAutowireCapableBeanFactory

initializeBean

,如果有读者这里不懂为什么要跳到这里,可以先去阅读一下我的

IOC

源码文章

3.1 前置通知

大家可以直接略过,这里没有实现

AOP

接口

wrappedBean =applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

3.2 后置通知

wrappedBean =applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
// 这里只关心一个类的后置通知就可以:AspectJAwareAdvisorAutoProxyCreatorpublicObjectapplyBeanPostProcessorsAfterInitialization(Object existingBean,String beanName){//初始化结果对象为result,默认引用existingBeanObject result = existingBean;//遍历该工厂创建的bean的BeanPostProcessors列表for(BeanPostProcessor processor :getBeanPostProcessors()){//回调BeanPostProcessor#postProcessAfterInitialization来对现有的bean实例进行包装Object current = processor.postProcessAfterInitialization(result, beanName);if(current ==null){//直接返回result,中断其后续的BeanPostProcessor处理return result;}//让result引用processor的返回结果,使其经过所有BeanPostProcess对象的后置处理的层层包装
            result = current;}//返回经过所有BeanPostProcess对象的后置处理的层层包装后的resultreturn result;}publicObjectpostProcessAfterInitialization(@NullableObject bean,String beanName){if(bean !=null){// 获取当前bean的key:如果beanName不为空,则以beanName为key,如果为FactoryBean类型,// 前面还会添加&符号,如果beanName为空,则以当前bean对应的class为keyObject cacheKey =getCacheKey(bean.getClass(), beanName);// 判断当前bean是否正在被代理,如果正在被代理则不进行封装if(this.earlyProxyReferences.remove(cacheKey)!= bean){// 如果它需要被代理,则需要封装指定的beanreturnwrapIfNecessary(bean, beanName, cacheKey);}}return bean;}protectedObjectwrapIfNecessary(Object bean,String beanName,Object cacheKey){// 获取当前bean的Advices和AdvisorsObject[] specificInterceptors =getAdvicesAndAdvisorsForBean(bean.getClass(), beanName,null);// 对当前bean的代理状态进行缓存if(specificInterceptors != DO_NOT_PROXY){// 对当前bean的代理状态进行缓存this.advisedBeans.put(cacheKey,Boolean.TRUE);// 根据获取到的Advices和Advisors为当前bean生成代理对象Object proxy =createProxy(
                    bean.getClass(), beanName, specificInterceptors,newSingletonTargetSource(bean));// 缓存生成的代理bean的类型,并且返回生成的代理beanthis.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}this.advisedBeans.put(cacheKey,Boolean.FALSE);return bean;}

到这里,我们基本上已经找到代理的入口了,主要做了两件事情

第一:

getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null)

校验当前的类、方法是否可以增强

第二:

Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

创建代理类

3.2.1 校验
Object[] specificInterceptors =getAdvicesAndAdvisorsForBean(bean.getClass(), beanName,null);protectedObject[]getAdvicesAndAdvisorsForBean(Class<?> beanClass,String beanName,@NullableTargetSource targetSource){// 找合适的增强器对象List<Advisor> advisors =findEligibleAdvisors(beanClass, beanName);// 若为空表示没找到if(advisors.isEmpty()){return DO_NOT_PROXY;}return advisors.toArray();}protectedList<Advisor>findEligibleAdvisors(Class<?> beanClass,String beanName){// 将当前系统中所有的切面类的切面逻辑进行封装,从而得到目标AdvisorList<Advisor> candidateAdvisors =findCandidateAdvisors();// 对获取到的所有Advisor进行判断,看其切面定义是否可以应用到当前bean,从而得到最终需要应用的AdvisorList<Advisor> eligibleAdvisors =findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);// 提供的hook方法,用于对目标Advisor进行扩展extendAdvisors(eligibleAdvisors);if(!eligibleAdvisors.isEmpty()){// 对需要代理的Advisor按照一定的规则进行排序
            eligibleAdvisors =sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;}

对于怎么判断该

Advisor

可以应用到当前类,也就是

findAdvisorsThatCanApply

这个方法

  • 通过 Advisor 内的 Pointcut 进行判断
  • 首先匹配类是否符合规定,然后匹配方法是否符合规定
  • 最终返回我们符合的 List<Advisor>

这里有一个细节点,大家看这行代码:

eligibleAdvisors = sortAdvisors(eligibleAdvisors)

,这里会有一个排序的动作。

这里面主要是用了拓扑排序,如果你不了解什么是拓扑排序,可以先复习一下这篇文章:拓扑排序在顶尖风控团队的业务落地
在这里插入图片描述

通过

Advice

之间的联系,将其顺序进行重排,如上图所示,我们排序最终的顺序为:

A -> B -> C -> D

或者

A -> C -> B -> D

,两者都可

不过,正常情况下应该也没有哪个面试官会这样问,除非特别刁难你

3.2.2 生成代理
Object proxy =createProxy(bean.getClass(), beanName, specificInterceptors,newSingletonTargetSource(bean));protectedObjectcreateProxy(Class<?> beanClass,@NullableString beanName,@NullableObject[] specificInterceptors,TargetSource targetSource){// 给bean定义设置暴露属性if(this.beanFactory instanceofConfigurableListableBeanFactory){AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory, beanName, beanClass);}// 创建代理工厂ProxyFactory proxyFactory =newProxyFactory();// 获取当前类中相关属性
         proxyFactory.copyFrom(this);// 决定对于给定的bean是否应该使用targetClass而不是他的接口代理,检查proxyTargetClass设置以及preserverTargetClass属性if(!proxyFactory.isProxyTargetClass()){// 判断是 使用jdk动态代理 还是cglib代理if(shouldProxyTargetClass(beanClass, beanName)){
                proxyFactory.setProxyTargetClass(true);}else{// 添加代理接口evaluateProxyInterfaces(beanClass, proxyFactory);}}// 构建增强器Advisor[] advisors =buildAdvisors(beanName, specificInterceptors);
        proxyFactory.addAdvisors(advisors);// 设置到要代理的类
        proxyFactory.setTargetSource(targetSource);// 定制代理customizeProxyFactory(proxyFactory);// 控制代理工程被配置之后,是否还允许修改通知,默认值是false
        proxyFactory.setFrozen(this.freezeProxy);if(advisorsPreFiltered()){
            proxyFactory.setPreFiltered(true);}// 真正创建代理对象return proxyFactory.getProxy(getProxyClassLoader());}publicObjectgetProxy(@NullableClassLoader classLoader){// createAopProxy() 用来创建我们的代理工厂returncreateAopProxy().getProxy(classLoader);}ObjectgetProxy(@NullableClassLoader classLoader);
3.2.3 创建动态代理对象

这里我们以

CglibAopProxy

举例,不懂动态代理的可复习这篇文章:【Spring从成神到升仙系列 一】2023年再不会动态代理,就要被淘汰了

publicObjectgetProxy(@NullableClassLoader classLoader){try{// 从advised中获取ioc容器中配置的target对象Class<?> rootClass =this.advised.getTargetClass();Class<?> proxySuperClass = rootClass;//如果目标对象已经是CGLIB 生成代理对象(就是比较类名称中有 $$ 字符串),那么就取目标对象的父类作为目标对象的类// Validate the class, writing log messages as necessary.// 打印出不能代理的方法名,CGLIB 是使用继承实现的,所以final , static 的方法不能被增强validateClassIfNecessary(proxySuperClass, classLoader);// Configure CGLIB Enhancer...// 创建及配置EnhancerEnhancer enhancer =createEnhancer();if(classLoader !=null){
         enhancer.setClassLoader(classLoader);if(classLoader instanceofSmartClassLoader&&((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)){
            enhancer.setUseCache(false);}}// 配置超类,代理类实现的接口,回调方法等
      enhancer.setSuperclass(proxySuperClass);
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(newClassLoaderAwareGeneratorStrategy(classLoader));// 获取callbacksCallback[] callbacks =getCallbacks(rootClass);Class<?>[] types =newClass<?>[callbacks.length];for(int x =0; x < types.length; x++){
         types[x]= callbacks[x].getClass();}// fixedInterceptorMap only populated at this point, after getCallbacks call above
      enhancer.setCallbackFilter(newProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(),this.fixedInterceptorMap,this.fixedInterceptorOffset));
      enhancer.setCallbackTypes(types);// Generate the proxy class and create a proxy instance.// 通过 Enhancer 生成代理对象,并设置回调returncreateProxyClassAndInstance(enhancer, callbacks);}}

这里也没有什么好讲的,都是一些配置性的东西。

4、回调

我们上述将代理对象存入到

DefaultListableBeanFactory

中,以便我们更好的获取

// 这里拿到的是代理对象,相应的执行的也是代理对象的方法BeanA beanA = context.getBean(BeanA.class);
 beanA.do1();

我们直接跳到

DynamicAdvisedInterceptor

intercept

方法

publicObjectintercept(Object proxy,Method method,Object[] args,MethodProxy methodProxy){// 从advised中获取配置好的AOP通知List<Object> chain =this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);else{// 通过cglibMethodInvocation来启动advice通知
         retVal =newCglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}
      retVal =processReturnType(proxy, target, method, retVal);return retVal;}}

这里一共有三个比较重要的点:

  • getInterceptorsAndDynamicInterceptionAdvice :从 Advisor 得到相对应的 Advice
  • new CglibMethodInvocation():创建责任链
  • proceed():执行责任链

我们重点来看看

proceed()

这个方法:

publicObjectproceed()throwsThrowable{returnsuper.proceed();}publicObjectproceed(){// 从索引为-1的拦截器开始调用,并按序递增,如果拦截器链中的拦截器迭代调用完毕,开始调用target的函数,这个函数是通过反射机制完成的if(this.currentInterceptorIndex ==this.interceptorsAndDynamicMethodMatchers.size()-1){returninvokeJoinpoint();}// 获取下一个要执行的拦截器,沿着定义好的interceptorOrInterceptionAdvice链进行处理Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);// 普通拦截器,直接调用拦截器,将this作为参数传递以保证当前实例中调用链的执行return((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}

这个方法稍微的有点绕,这里先来说下这里的整体思路:递归 + 责任链

在这里插入图片描述

简单的来说一下,首先这个责任链不是我们传统那种上下文连串的责任链,比如

Netty

:【Netty 从成神到升仙系列 五】Netty 的责任链真有这么神奇吗?

因为他的责任链里面的对象各不相同,所以他用了两个对象的

procesd

方法来代替并且使用下标代表当前所处的位置

这里讲一下

AspectJAfterAdvice

AspectJMethodBeforeAdvice

invokeJoinpoint();

对于

AspectJAfterAdvice

来说,它本身属于目标方法之后才去执行的,所以当最开始到达他时,他并不能执行属于自己的方法,因为目标方法还未执行。等执行完之后,再去执行自己的后置方法

invokeAdviceMethod

对于

AspectJMethodBeforeAdvice

来说,它本身属于目标方法执行之前,所以当最开始到达他时,直接执行即可

对于

invokeJoinpoint

来说,他属于目标方法,所有的

Advice

遍历一遍之后,这个时候改

before

执行的都已经执行完了,所以直接执行即可。

这个链式结构设计的非常巧妙,利用递归的思想将各种

Advice

切到目标方法前后。

六:流程图

本文流程图可关注公众号:爱敲代码的小黄,回复:AOP 获取
贴心的小黄为大家准备的文件格式为 POS文件,方便大家直接导入 ProcessOn 修改使用

在这里插入图片描述

七、课后小题-双校验

上面的流程我们已经走完了,这里提一个小细节

我们上面发现进行了两次类和方法的校验

第一次:创建代理对象时,进行了校验

在这里插入图片描述

第二次:回调时候,进行了校验

在这里插入图片描述

那问题来了,为什么要进行两次校验呢?

这个问题留给读者们自己去思考

答案在流程图中!

八、总结

又是一篇大工程的文章结束了

记得校招时候,当时对 Spring 懵懂无知,转眼间也被迫看了源码

更可怕的是,现在面试竟然百分之80都要熟悉IOC、AOP的源码,甚至手写 AOP 的实现

但通过这篇文章,我相信,99% 的人应该都可以理解了 Spring AOP 的来龙去脉

那么如何证明你真的理解了 Spring AOP 呢,我这里出个经典的题目,大家可以想一下:自己实现一个 AOP 的整体逻辑

如果你能看到这,那博主必须要给你一个大大的鼓励,谢谢你的支持!

喜欢的可以点个关注,后续会更新 Spring 源码系列文章

后面至少还有两篇文章,一篇事务的,一篇循环依赖的

我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,Java领域新星创作者,喜欢后端架构和中间件源码。

我们下期再见。

标签: java spring kafka

本文转载自: https://blog.csdn.net/qq_40915439/article/details/129570707
版权归原作者 爱敲代码的小黄 所有, 如有侵权,请联系我们删除。

“【Spring从成神到升仙系列 三】2023年再不会 AOP 源码,就要被淘汰了”的评论:

还没有评论