【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

有目标就不怕路远。年轻人.无论你现在身在何方.重要的是你将要向何处去。只有明确的目标才能助你成功。没有目标的航船.任何方向的风对他来说都是逆风。因此,再遥远的旅程,只要有目标.就不怕路远。没有目标,哪来的劲头?一车尔尼雷夫斯基

导读:本篇文章讲解 【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

【Spring源码系列- IOC】

1

【Spring源码】0.安装Gradle环境

2

【Spring源码】1.下载与编译_pom relocation to an other version number is not f

3

【Spring源码】2.试个水先~Debug找到传说中的三级缓存(图解向,堆图预警)

4

【Spring源码】3. xml文件如何转换成BeanDefinition(主要涉及prepareRefresh()+ obtainFreshBeanFactory()两个函数,图解向,堆图预警)_spring xml转bean

5

【Spring源码】4. 自己搞个标签?~自定义标签保姆级全过程(图解向,堆图预警)

6

【Spring源码】5.spring的bean工厂准备工作(prepareBeanFactory(beanFactory)

7

【Spring源码】6. Spring扩展自定义属性编辑器保姆级教程

8

【Spring源码】7. 如何添加自定义的BeanFactoryPostProcessor

9

【Spring源码】8. 捋下invokeBeanFactoryPostProcessors()主要处理流程

10

【Spring源码】9. 超级重要的ConfigurationClassPostProcessor

11

【Spring源码】10. 递归调用的processConfigurationClass()方法

12

【Spring源码】11. 我是注解类不?checkConfigurationClassCandidate()注解类判断方法详解

13

【Spring源码】12. 注册bean处理器registerBeanPostProcessors()

14

【Spring源码】13. 国际化处理initMessageSource()源码解析

【补充内容】【保姆级】SpringBoot项目中的i18n国际化

15

【Spring源码】14. 消息多播器(观察者模式)

【补充内容】【保姆级示例向】观察者模式

16

【Spring源码】15. Bean的创建过程(1.概述篇)

17

【Spring源码】16. Bean的创建过程(2)

18

【Spring源码】17.创建Bean这篇认真的@(・●・)@

【补充内容】

【保姆级·创建对象】如何通过Supplier创建对象

【保姆级·创建对象】如何通过factory-method创建对象

【保姆级·创建对象】如何利用resolveBeforeInstantiation()在预处理阶段返回一个Bean的实例对象

19

【Spring源码】18. factory-method创建对象关键函数详解:instantiateUsingFactoryMethod()

20

【Spring源码】19. 没合适的构造器?找determineCandidateConstructors()!

21

【Spring源码】20. MergedBeanDefinitionPostProcessor修改/合并bean定义

【补充内容】

【保姆级】@PostConstruct & @PreDestroy使用示例

【Spring源码】AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition()详解

【Spring源码】CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition()详解

22

【Spring源码】21. 初探循环依赖

【补充内容】

【保姆级】手把手Debug循环依赖的整体流程

【实践向】当移除了三级缓存……

【分析向】没有三级缓存会导致什么?

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

23

【Spring源码】22. 属性填充populateBean()详解

【补充内容】

【Spring源码】自动注入·名称:autowireByName()详解

【Spring源码】自动注入·类型:autowireByType()详解

【Spring源码】属性值的解析与赋值:populateBean().applyPropertyValues()

【保姆级】超超超简单的自定义注解实现@Autowired同款功能

24

【Spring源码】23. 执行初始化逻辑:initializeBean()

在文章【分析向】没有三级缓存会导致什么? 中,提到过一个方法——wrapIfNecessary(),就是在这个方法中为Bean创建的代理对象,介于篇幅原因,当时并咩有详细🔎分析这个方法,这篇文章我们进去wrapIfNecessary()这个方法中瞅瞅(。・ω・。)ノ

wrapIfNecessary()

wrapIfNecessary()方法一开始会依次进行一系列的判断,如果满足了任意的一个,就会直接返回传入的bean对象,都不满足才会开始代理对象的创建

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

一开始会先判断当前传入的bean对象是否已经处理过(从集合targetSourcedBeans中查询,能查询到说明当前bean已经被处理过了),如果处理过了,就直接返回

对于已经创建过代理对象也会直接返回(从集合advisedBeans中查询📖,如果查得到,就说明缓存中存在当前bean的代理对象,即当前bean已经创建过代理对象了)

接下来的两个方法也是继续判断是否需要创建代理的

isInfrastructureClass()

用于判断当前bean是否是Spring自带的bean(自带的bean无需进行代理)

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

这里对4个类进行了判断:

  • Advice.class

  • Pointcut.class

  • Advisor.class

  • AopInfrastructureBean.class

isAssignableFrom()

通过调用上面4个类的isAssignableFrom()方法来判断传入对象所代表的类或者接口是否与他们相同,或者是其超类或者超接口,如果是的话返回true,否则返回false

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

shouldSkip()

判断当前bean是否需要被略过

以上两个条件,满足任意一个,则将当前bean进行缓存,并返回

当前(。・ω・。)ノ

如果上面👆的条件都不满足,则要开始为当前bean创建代理对象了

getAdvicesAndAdvisorsForBean()

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

先通过调用getAdvicesAndAdvisorsForBean()方法获取当前bean的Advices和Advisors

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

findEligibleAdvisors()

在findEligibleAdvisors()方法中,从候选的通知器中找到合适正在创建的实例对象的通知器获取到的Advisors(获取当前系统中所有切面类的切面逻辑,封装成一个List对象)进行判断,看其切面定义是否能应用到当前Bean,在进行相关的拓展、排序操作,最终得到最终需要应用的Adviser

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

findCandidateAdvisors()

将当前系统中的切面类的切面逻辑进行封装,从而得到目标Advisors

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

进入其实现类

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

findAdvisorsThatCanApply()

对获取到的所有Advisor进行判断,看其切面定义是否可以应用到当前bean,从而得到最终需要应用的Advisors

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

通过同名方法findAdvisorsThatCanApply()从候选的通知器中找到合适正在创建的实例对象

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

在findAdvisorsThatCanApply()的一开始定义一个合适的增强器集合对象eligibleAdvisors,接着循环我们候选的增强器对象,判断我们的增强器对象是不是实现了IntroductionAdvisor(是否有引介增强),最后调用canApply()方法判断增强器是否适用于当前类型

canApply()

这里对Advisor的类型进行了判断(见末尾补充内容)

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

  • 如果是IntroductionAdvisor的话,则调用IntroductionAdvisor类型的实例进行类的过滤

  • 如果是PointcutAdvisor类型(我们用到的一般都是这个类型),转为PointcutAdvisor类型

再次进入同名方法,方法中先进行classFilter的natches方法校验,类型不匹配直接返回false

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

进行切点表达式的匹配判断最重要的就是ClassFilter和MethodMatcher这两个方法的实现。

在当前方法中,只有在类型匹配得上以后,才会再进行MethodMatcher方法级别的校验

这里顺便介绍下MethodMatcher中有两个matches方法:

  • 参数:Method、targetClass

  • 参数:Method、targetClass、Method的方法参数

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

他们两个的区别是:两个参数的matches是用于静态的方法,匹配三个参数的matches是在运行期动态的进行方法匹配的。

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

判断匹配器是不是IntroductionAwareMethodMatcher,创建一个集合用于保存targetClass的class对象,接着判断当前class是不是代理的class对象,如果不是就加入到集合中去;接着获取到targetclass所实现的接口的class对象并加入到集合中

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

接下来就是循环所有的class对象,通过class获取到所有的方法,循坏遍历获取到的方法,只要有一个方法能配到就会返回true

回到方法wrapIfNecessary()中,获取了当前bean的Advices和Advisors后,判断下获取到的集合advisors是否为🈳(🈳就是没找到),是就返回DO_NOT_PROXY,不为空则返回集合advisor s的数组对象

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

只要getAdvicesAndAdvisorsForBean()方法的返回值不是DO_NOT_PROXY,就会进入if条件语句的逻辑块,缓存当前bean的代理状态,然后调用createProxy()实现代理对象的创建,创建完成后,缓存生成的代理对象,随后一路返回,就获取到了的一个当前bean的代理对象

无论当前对象是否需要创建代理对象,都会先创建它的普通实例对象,只不过在后续的流程中会把这个普通的实例对象进行替换

ps:其实DO_NOT_PROXY的值就是null ⁄(⁄ ⁄ ⁄ω⁄ ⁄ ⁄)⁄

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

extendAdvisors()

提供hook方法,用于对目标Advisor进行扩展

sortAdvisors()

最后对需要代理的Advisors们按照一定的顺序进行排序

结束后将排好序的Advisors们返回

回到getAdvicesAndAdvisorsForBean()中,判断排好序的Advisors集合是否为空,空则返回“DO_NOT_PROXY”

如果排好序的Advisors的集合不等于“DO_NOT_PROXY”(不为空),就会开始为当前对象创建代理

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

createProxy()

createProxy()方法会创建代理对象并将该代理对象缓存并返回

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

首先,给bean定义设置暴露属性

exposeTargetClass()

主要功能就是设置ORIGINAL_TARGET_CLASS_ATTRIBUTEH的属性值

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

接着创建代理工厂、获取单当前类中相关属性,决定对于给定的bean是否应该使用targetClass而不是他的接口代理,检查proxyTargetClass设置以及preserverTargetClass属性

shouldProxyTargetClass()

如果proxyFactory.isProxyTargetClass()的值为false则会调用方法shouldProxyTargetClass()继续判断是使用JDK动态代理,还是CGLIB动态代理

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

再次进入同名方法

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

ORIGINAL_TARGET_CLASS_ATTRIBUTE这个变量表示的是Bean的定义属性,它可以表明一个给定的Bean是否应该被其目标类代理(在它首先被代理的情况下)。其值是TRUE或FALSE。如果代理工厂为一个特定的Bean建立了一个目标类代理,并希望强制Bean总是可以被投递到它的目标类(即使AOP建议通过自动代理被应用),那么代理工厂可以设置这个属性

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

evaluateProxyInterfaces()

evaluateProxyInterfaces()方法用于添加代理接口

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

一开始会初始化参数hasReasonableProxyInterface为false,然后判断目标类有没有实现的接口,如果有接口并且以下条件同时为true,则使用JDK动态代理,并将参数hasReasonableProxyInterface设为true

  • !isConfigurationCallbackInterface(ifc)

  • !isInternalLanguageInterface(ifc)

  • ifc.getMethods().length > 0

接下来如果确定使用JDK动态代理就会把所有目标类实现的接口都添加进代理工厂,如果不是就会把代理工厂的·屏proxyTargetClass属性设为true

添加完代理接口后,会开始构建增强器,设置要代理的目标类,随后进行代理的定制化(目前默认为空,就是啥都没干)

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

通过设置frozen属性控制代理工厂被配置后,是否还允许修改通知,默认值是false,最后调用getProxy()真正开始构建代理对象

getProxy()

此处会根据工厂的设置,创建一个可以反复调用的新的代理对象

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

createAopProxy()

创建代理工厂

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

添加或删除了接口,效果会有所不同。可以添加和删除拦截器。使用给定的类加载器(如果对代理的创建有必要)。

监听调用AdvisedSupportListener实现类的activated()方法

通过AopProxyFactory获取到AopProxy,这个AopProxyFactory是在初始化函数中定义的(DefaultAopProxyFactory)

再次进入同名方法中

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

这里首先选择使用哪种方式创建代理对象,如果满足以下3个条件中的任意一个,就会进入if条件代码块(不进入则使用JDK的提供的代理方式生成代理对象)

  • config.isOptimize() 是否对代理类的生成使用策略优化(默认为false)

  • config.isProxyTargetClass() 是否使用Cglib的方式创建代理对象(默认为false)

  • hasNoUserSuppliedProxyInterfaces(config) 目标类是否有接口存在且只有一个接口的时候接口类型不是SpringProxy类型(如下图)

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

接着从AdvisedSupport中获取目标类类对象,对目标类进行判断如果目标类是是接口或者Proxy类型的类则使用JDK的方式生成代理对象,否则使用CGLIB 进行动态代理

getProxy()
【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

这个方法有两个实现

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

CglibAopProxy

我们先来看看CglibAopProxy的实现

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

从advised中获取ioc容器中配置的target对象,如果对象已经是CGLIB生成的,就取目标对象的父类作为目标对象的类,然后获取无法被代理的方法名(CGLIB的实现方式是继承,所以final、static修饰的方法不能被增强)

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

创建、配置增强器,配置其他信息(超类、代理类实现的接口、回调方法等)

这里有必要插播介绍下advisor、advice、adviced各自的含义

  • advisor 通知器包含advice和pointcut

  • advice 具体的某一个消息通知

  • adviced 用来配置代理(proxyFactory)

这部分代码中有个方法需要注意下AopProxyUtils中的completeProxiedInterfaces(this.advised)

此处的this.advised表示的是具体的代理工厂类也就是proxyFactory

completeProxiedInterfaces()

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

再次进入当前方法内的同名方法

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

根据AdvisedSupport类型中目标类的接口,如果目标类没有实现接口,就尝试获取目标类,如果目标类是接口,则添加到AdvisedSupport的接口中,如果是Proxy类型,也会尝试获取接口

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

最后将获取到的接口返回,回到方法getProxy()中,配置完属性后,回获取回调函数

getCallbacks()

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

首先对expose-proxy、isFrozen、isStatic这几个属性进行处理,接着将拦截器封装在DynamicAdvisedInterceptor中

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

new一个Callback类型的数组,将拦截器(回调函数)添加进数组,返回并赋值给变量callbacks

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

最后调用方法createProxyClassAndInstance() 通过Enhancer生成代理对象并设置返回

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

进入createClass()方法

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

详细看下createHelper()

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

preValidate()是校验callbackTypes、filter是否为空,以及为空时的处理

【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

接着是一行特别长的代码,截图没截全,下面👇贴下代码

Object key = KEY_FACTORY.newInstance(
             this.superclass != null ? this.superclass.getName() : null,
             ReflectUtils.getNames(this.interfaces), 
             this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), 
             this.callbackTypes, 
             this.useFactory, 
             this.interceptDuringConstruction, 
             this.serialVersionUID);

这段代码是通过newInstance()方法来为创建的EnhancerKey对象的属性赋值

接着设置当前enhancer的代理类的key标识,然后调用父类即AbstractClassGenerator的创建代理类,生成代理对象后返回

啊啊啊~这篇长湿俺叻、搞定( ̄∇ ̄)/

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/135369.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!