「Spring 」「AOP 容器」不看源码就带你认识核心流程以及运作原理

「Spring 」「AOP 容器」不看源码就带你认识核心流程以及运作原理,第1张

前一篇文章主要介绍了 spring 核心特性机制的 IOC 容器机制和核心运作原理,接下来我们去介绍另外一个较为核心的功能,那就是 AOP 容器机制,主要负责承接前一篇代理模式机制中动态代理:JDKProxy 和 CglibProxy 的功能机制之后,我们开始研究一下如何实现一下相关的 AOP 容器代理机制的。

实现的基本实现原理就是后置处理器:BeanPostProcessor 机制,实现动态化植入机制。

bean 在初始化的时候会进行调用对应的 BeanPostProcessor 的对应的方法会进行织入。

主要取决于 wrapIfNecessary 方法:

如果是基础设施类型,则直接回进行返回该 bean 对象,不会进行相关的初始化对应的 aspectj 的动态织入机制。

会进行寻找相关的 Bean 对应的何时的加强通知类。

则会对该 bean 对象,额外进行增强 *** 作生成相关的代理对象,并返回该执行之后的对象,否则会直接返回该对象即可。

getAdvicesAndAdvisorsForBean 方法是我们筛选 Advice 增强类的核心方法,主要用于过滤和筛选对应该 bean 的何时的增强器数组信息。

主要用于调用 AnnotationAwareAspectJAutoProxyCreator 的findCandidateAdvisors()方法,其内部会进行先关的核心构建相关的 Aspectj 的类的相关实现 *** 作

advisorsFactorygetAdvisors 获取通知器

切点类处理 *** 作到此为止,还不完整接下来才是构建动态代理对象的真正执行 *** 作,

扩展相关的筛选出的通知器列表,extendAdvisors 方法,通知器列表首部添加一个 DefaultPointcutAposr 类型的通知器,也就是 ExposeInvocationInterceptorADVISOR 的实现机制。

proxy-target-class 的属性值,代表是否可以支持代理实现类,默认采用的 false 代表着,当 bean 有实现接口的时候,会直接采用 jdk 的动态代理机制生成代理对象,如果是 true,则代表着使用 cglib 进行生成代理对象。

复制代码

前提是必须要配置相关的 expose-proxy 属性配置值为 true,才会进行暴露对应的代理机制。

为了解决目标方法调用同对象中的其他方法,其他方法的切面逻辑是无法实现,因为会涉及到相关的 this *** 作而不是 proxy 对象机制。

可以实现使用 AopContextcurrentProxy()强制转换为当前的代理对象。

获取相关的对应方法的拦截器栈链路,如果没有获取到相关的缓存链路,则会直接调用相关的 getInterceptorsAndDynamicInterceptorAdvice 获取先关的拦截器链。

会进行先关的 PointcutAdvisor 类型通知器,这里会调用相关的通知器所持有的切点(Pointcut)对类和方法进行匹配,匹配冲过这说明相关的向当前的方法进行织入逻辑控制。此外还会通过 geIntercptors()方法对非 MethodIntercptor 类型的通知进行转换。返回相关的拦截器数组,并且随后存入缓存中。

则会直接通过代理机制的反射控制进行调用执行即可。

则例如 jdkDynamicAutoProxy 对象进行调用构建 ReflectiveMethodInvocation 对象,例如它的 process 方法启动拦截器栈的 invoke 方法。

处理返回值,并且返回该值。

访问目标方法最简单的做法是定义增强处理方法时,将第一个参数定义为JoinPoint类型,当该增强处理方法被调用时,该JoinPoint参数就代表了织入增强处理的连接点。JoinPoint里包含了如下几个常用的方法:Object[]getArgs:返回目标方法的参数SignaturegetSignature:返回目标方法的签名ObjectgetTarget:返回被织入增强处理的目标对象ObjectgetThis:返回AOP框架为目标对象生成的代理对象注意:当使用@Around处理时,我们需要将第一个参数定义为ProceedingJoinPoint类型,该类是JoinPoint的子类。

Bean的后置处理器,首先来说,他是Spring中抽象出来的一个顶级的接口,他里面有如下两个方法。

简单点来理解,就是spring会自动从它的所有的bean定义中检测BeanPostProcessor类型的bean定义,然后实例化它们,再将它们应用于随后创建的每一个bean实例,在bean实例的初始化方法回调之前调用BeanPostProcessor的postProcessBeforeInitialization的方法(进行bean实例属性的填充),在bean实例的初始化方法回调之后调用BeanPostProcessor的postProcessAfterInitialization的方法(可以进行bean实例的代理封装)。

大家说它是Spring对外提供的拓展点,也许是因为,通过实现这个接口,程序员可以对Spring管理的bean的生命周期进行插手。

这也体现了AOP的设计思想,就比如在init()方法执行前后做出不同的动作,其实就是对bean的一种增强。

此外BeanPostProcessor可以存在多个。他们会被存储在一个列表中。然后依次被执行。

所谓的注册,只不过是对当前上下文中所有的BeanPostProcessor进行一种集中式管理罢了,为什么非得这么做呢 因为上下文中BeanPostProcessor的数量不是一成不变的,Spring为了启动的正常,需要添加原生的BeanPostProcessor,程序员因为自己的需求也会添加不同数量的bean的后置处理器,因此需要这种策略将上下文中所有的后置处理器进行统一的管理,方便回调。

Spring作为一个优秀的框架,拥有良好的可扩展性。Spring对对象的可扩展性主要就是依靠InstantiationAwareBeanPostProcessor和BeanPostProcessor来实现的。

AnnotationConfigApplicationContext构造方法中调用refresh()方法

refresh() 方法中这里主要关心两个方法:

通过beanFactorygetBeanNamesForType来获取所有BeanPostProcessor。

BeanPostProcessor按优先级分为PriorityOrdered,Ordered和其他的,对他们分别进行 *** 作。

InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法,该方法主要是在bean实例化之后,并且在其被设置上属性值之前去进行调用,用来判断是否继续去执行对属性赋值的流程,方法返回true,即默认情况下都会去执行populateBean方法,对Bean的属性进行赋值。

postProcessAfterInstantiation方法调用是在populateBean方法中,这里从doCreateBean方法进入。

在populateBean方法中,在对bean的属性进行真正赋值之前,会依次调用InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法。

在Spring的AOP模块,一个主要的部分是代理对象的生成,可以通过ProxyFactoryBean来完成,它封装了主要代理对象的生成过程。在这个生成过程中,可以使用JDK的Proxy和CGLIB两种生成情况。

Tip: 什么是匿名类

即没有名称的类,其名称由Java编译器给出,一般为:外部类名称+$+匿名类顺序,名称也就是其他地方不能引用,不能实例化,只用一次,当然也就不能有构造器。

在ProxyFactoryBean中,需要为target目标对象生成Proxy代理对象,从而为AOP横切面的编织做好准备。从FactoryBean中获取对象,是以getObject()方法作为入口完成的。在该方法中,首先对通知器链进行初始化,封装了一系列的拦截器,这些拦截器都要从配置中读取,然后为代理对象的生成做好准备。在生成代理对象时,因为Spring中有singleton类型和prototype类型这两种不同的Bean,所以要对代理对象的生成做一个区分。

首先为Proxy代理对象配置Advisor链,在initializeAdvisorChain()方法中执行。

在该方法中它会首先通过thisadvisorChainInitialized来判断通知器链是否已经初始化了,如果已经初始化了,就直接返回。其他情况下,通过 thisinterceptorNames 来要添加的通知器名,然后通过该名从IOC容器中取得的通知器加入到拦截器链中。

生成singleton的代理对象在getSingletonInstance()中完成

如果它还没有被创建,则lazily creating

在Spring代理目标target时,其实并不是直接创建一个目标target的对象实例的,而是通过一个TargetSource类型的对象对目标target进行封装,Spring Aop获取目标对象始终是通过 TargetSourcegetTarget() 方法进行的。

proxy(代理对象)代理的不是target,而是TargetSource

那么问题来了:为什么SpringAOP代理不直接代理target,而需要通过代理TargetSource(target的来源,其内部持有target),间接代理target呢

通常情况下,一个proxy(代理对象)只能代理一个target,每次方法调用的目标也是唯一固定的target。但是,如果让proxy代理TargetSource,可以使得每次方法调用的target实例都不同(当然也可以相同,这取决于TargetSource实现)。这种机制使得方法调用变得灵活,可以扩展出很多高级功能,如:target pool(目标对象池)、hot swap(运行时目标对象热替换),等等。

Spring内置了多种TargetSource

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

具体的代理对象的生成,是在ProxyFactoryBean的基类AdvisedSupport的实现中借助AopProxyFactory完成的,这个代理对象要么从JDK中生成,要么借助CGLIB获得。

这个AopProxyFactory是在初始化函数中定义的,使用的是DefaultAopProxyFactor。

如果targetClass是接口类,使用JDK来生成Proxy

如果不是接口类要生成Proxy,那么使用CGLIB来生成。

接下来分别介绍两种不同的方式来产生AopProxy代理对象

首先从advised对象中取得代理对象的代理接口配置,然后调用Proxy的newProxyInstance方法,得到最终的Proxy代理对象。

在生成代理对象时,需要指明三个参数,类加载器,代理接口和Proxy回调方法所在的对象。

在回调方法所在对象中,需要实现InvocationHandler接口,它定义了invoke方法,

对于JdkDynamimcAopProxy,它本身实现了InvocationHandler接口和invoke方法,这个invoke方法是Proxy代理对象的回调方法。

在该篇文章中就不讲解了,感兴趣的可以百度搜索。

注:本文大多数是对《Spring技术内幕》的阅读整理。

C#的AOP实现主要是参考了这篇 博客 ,并对实现过程中遇到的问题进行分析和修改)。

定义一个AOP上下文特性,用于标注需要支持AOP方法的类,通过 ContextAttribute, IContributeObjectSink 来获取类的上下文环境,这是通过 Attribute 拦截参数和获取返回值的前提。

实现一个继承自 ContextBoundObject 的类,并标注 [AOPContext] 特性,两者配合,使得这个类下的方法可以被成功拦截。需要支持AOP的类,继承这个类即可。

用于标注需要拦截参数的方法,和指出对应的处理函数。

TestMethod1为将被拦截处理的方法,before方法和after方法可抽取到其他类中。

被拦截的类需要继承AOPContext,并且该类中调用的第一个方法会被拦截,如果存在嵌套方法,不会拦截到第二个方法。

目前的实现无法改值参,可以考虑将数值类型封装到model中,传model对象。

IOC的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。Spring容器负责将这些联系在一起。也就是说,Spring的IOC负责管理各种对象的创建、清除以及它们之间的联系。AOP是指面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。一般用于事务管理、权限控制、错误处理等各种业务中共同性的东西。

以上就是关于「Spring 」「AOP 容器」不看源码就带你认识核心流程以及运作原理全部的内容,包括:「Spring 」「AOP 容器」不看源码就带你认识核心流程以及运作原理、spring aop后置通知怎么获取目标方法返回值,及通知的信息、Spring5AOP——BeanPostProcessor等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/9394137.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-27
下一篇 2023-04-27

发表评论

登录后才能评论

评论列表(0条)

保存