Spring-aop技术
创始人
2024-02-19 16:41:32
0

前言

        spring-aop技术是对oop(面向对象)的一个补充,其底层其实就是使用aspect+动态代理进行实现的,本篇文章将大概讨论下aop的核心实现流程

相关的核心概念

        刚开始,先介绍下aop中比较核心的一些对象和概念,只要理解了这些,后面就可以很容易理解aop是怎么工作的了

Advisor接口

        Advisor接口包含了Advice和Pointcut的定义,而这两个对象是我们进行代理的核心对象,举个例子你就明白了,如下

public class UserService{public void a(){}    public void b(){}
}假设有上面的类对象,现在有一段切入逻辑如下public void method(){"插入方法逻辑";
}这是一段要插入到UserService类的a和b方法的逻辑,那么在这个场景下Advice: 指的就是method这段逻辑
PonitCut: 假设只想要在a方法切入,不想在b方法切入,那么这个校验过程就是PonitCut的代表

ProxyFactory对象

        因为spring对于aop实现底层就是用代理实现的,所以底层会有这个对象进行管理代理对象,代理方法目前有两种,JdkDynamicAopProxy和ObjenesisCglibAopProxy(cglib)

        这个对象主要就是用来获取代理对象的,其他的似乎没啥说的,因为核心逻辑还是在代理对象中进行实现的,下面以两个的其中一个来进行说明

JdkDynamicAopProxy

        基于jdk的动态代理,Java基础扎实的人肯定都会知道他会实现一个InvocationHandler接口,该接口是代理对象最终会执行的入口,我们就来看看他的invoke接口具体干了啥,如下

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;//这里简单理解就是获取被代理的类,比如这里就是UserServiceTargetSource targetSource = this.advised.targetSource;Object target = null;try {//判断如果是以下这些方法时不需要进行代理if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {return equals(args[0]);}else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {return hashCode();}else if (method.getDeclaringClass() == DecoratingProxy.class) {return AopProxyUtils.ultimateTargetClass(this.advised);}else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);}//下面真正进行代理逻辑的处理Object retVal;//是否把当前的代理对象绑定到当前线程,就是放到ThreadLocal中if (this.advised.exposeProxy) {oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}target = targetSource.getTarget();Class targetClass = (target != null ? target.getClass() : null);// 获取所有关联的advisor对象,也就是上面刚说的很核心的对象List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);if (chain.isEmpty()) {//如果为空表示没有任何的代理逻辑,直接返回即可Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);}else {//如果不为空把数据封装为ReflectiveMethodInvocation对象,然后调用proceed方法,开始处理代理逻辑MethodInvocation invocation =new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);// Proceed to the joinpoint through the interceptor chain.retVal = invocation.proceed();}// 处理返回值,不是核心方法,,Class returnType = method.getReturnType();if (retVal != null && retVal == target &&returnType != Object.class && returnType.isInstance(proxy) &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {retVal = proxy;}else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);}return retVal;}finally {if (target != null && !targetSource.isStatic()) {// Must have come from TargetSource.targetSource.releaseTarget(target);}if (setProxyContext) {// 移除绑定关系AopContext.setCurrentProxy(oldProxy);}}} 

以上可以看到核心的处理流程,那么问题就来到了两个细节方法了,接下来分析下下面的问题

首先,来看下 this.advised.getInterceptorsAndDynamicInterceptionAdvice这个方法,这个方法是来获取所有的Advisor对象的,也可以理解为获取Advice对象,因为最终的代理逻辑就是在Advice里面的

public List getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class targetClass) {AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();//获取所有的Advisor,config其实就是ProxyFactory对象,在生成的时候已经绑定进来了Advisor[] advisors = config.getAdvisors();List interceptorList = new ArrayList<>(advisors.length);Class actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());Boolean hasIntroductions = null;//循环处理for (Advisor advisor : advisors) {if (advisor instanceof PointcutAdvisor) {// Add it conditionally.PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;//粗筛,可以理解为对UserService类的筛选if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {//方法匹配器,进一步对UserService类下面的方法进行筛选MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();boolean match;if (mm instanceof IntroductionAwareMethodMatcher) {if (hasIntroductions == null) {hasIntroductions = hasMatchingIntroductions(advisors, actualClass);}match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);}else {//是否匹配match = mm.matches(method, actualClass);}if (match) {//如果是的话会加入到这个逻辑里//把数据包装为MethodInterceptor对象返回MethodInterceptor[] interceptors = registry.getInterceptors(advisor);if (mm.isRuntime()) {// MethodMatcher是否配置了运行时还要校验,如果是的话到时具体执行的时候还会根据实时参数进一步筛选for (MethodInterceptor interceptor : interceptors) {interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));}}else {interceptorList.addAll(Arrays.asList(interceptors));}}}}else if (advisor instanceof IntroductionAdvisor) {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));}}return interceptorList;} 

以上就是筛选匹配的Advisor对象,最终会统一封装为MethodInterceptor列表返回,可以进一步来看看这个,这里用了一个适配器模式,还是比较值得看看的

DefaultAdvisorAdapterRegistry#getInterceptors方法中进行处理的

 @Overridepublic MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {List interceptors = new ArrayList<>(3);//获取对应的AdviceAdvice advice = advisor.getAdvice();//如果是MethodInterceptor直接加入即可if (advice instanceof MethodInterceptor) {interceptors.add((MethodInterceptor) advice);}//如果不是会使用AdvisorAdapter对象进行对比并且加入for (AdvisorAdapter adapter : this.adapters) {if (adapter.supportsAdvice(advice)) {interceptors.add(adapter.getInterceptor(advisor));}}if (interceptors.isEmpty()) {throw new UnknownAdviceTypeException(advisor.getAdvice());}return interceptors.toArray(new MethodInterceptor[0]);}

AdvisorAdapter是一种适配器,用于对原来不是MethodInterceptor的对象进行适配为MethodInterceptor对象,可以看看它的接口定义

/*** Interface allowing extension to the Spring AOP framework to allow* handling of new Advisors and Advice types.** 

Implementing objects can create AOP Alliance Interceptors from* custom advice types, enabling these advice types to be used* in the Spring AOP framework, which uses interception under the covers.**

There is no need for most Spring users to implement this interface;* do so only if you need to introduce more Advisor or Advice types to Spring.** @author Rod Johnson*/ public interface AdvisorAdapter {/*** Does this adapter understand this advice object? Is it valid to* invoke the {@code getInterceptors} method with an Advisor that* contains this advice as an argument?* @param advice an Advice such as a BeforeAdvice* @return whether this adapter understands the given advice object* @see #getInterceptor(org.springframework.aop.Advisor)* @see org.springframework.aop.BeforeAdvice*/boolean supportsAdvice(Advice advice);/*** Return an AOP Alliance MethodInterceptor exposing the behavior of* the given advice to an interception-based AOP framework.*

Don't worry about any Pointcut contained in the Advisor;* the AOP framework will take care of checking the pointcut.* @param advisor the Advisor. The supportsAdvice() method must have* returned true on this object* @return an AOP Alliance interceptor for this Advisor. There's* no need to cache instances for efficiency, as the AOP framework* caches advice chains.*/MethodInterceptor getInterceptor(Advisor advisor);}

只有两个方法,一个是否支持,一个获取到MethodInterceptor对象,接下来可以看看其中一个实现类,如下

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {@Overridepublic boolean supportsAdvice(Advice advice) {//是否为MethodBeforeAdvice类型的return (advice instanceof MethodBeforeAdvice);}@Overridepublic MethodInterceptor getInterceptor(Advisor advisor) {MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();//把advice适配为 MethodInterceptor 对象return new MethodBeforeAdviceInterceptor(advice);}}

这种设计模式很巧妙,很多地方都会用到这个技术,比如gateway也会有相应的使用

AnnotationAwareAspectJAutoProxyCreator

        自动处理aop逻辑的处理器,它实现了BeanPostProcessor接口,也就是说,所有bean的创建都会经过这个类,它的大概逻辑如下

1 .bean创建的时候经常这个处理器

2. 查找当前bean匹配的Advisor对象,分为两种方式,一种是直接从ioc容器中获取Advisor对象,一种是通过对@Aspect进行解析里面的方法

3. 如果没有配置的就直接返回

4. 如果有一个及以上匹配的Advisor就使用ProxyFactory进行创建代理对象

5. 最终整个流程就被串起来了

到此,aop的核心流程就已经说完了,最后的问题,AnnotationAwareAspectJAutoProxyCreator这个类是如何加载到ioc容器的,其实很简单,我们在使用aop的时候,会标记以下的注解以启用aop功能 @EnableAspectJAutoProxy,这个注解会往容器中导入AspectJAutoProxyRegistrar对象,而AspectJAutoProxyRegistrar对象又会注册AnnotationAwareAspectJAutoProxyCreator对象,最终,整个流程就串起来了

结语

        整个流程其实并不复杂,只要理解了核心的几个对象,加上些许的Ioc容器工作过程,就可以理解了,源码的入口其实就是在AnnotationAwareAspectJAutoProxyCreator这个对象的后置处理方法中进行的,主要是这个方法AbstractAutoProxyCreator#wrapIfNecessary进行了包装,当然了这个还会涉及到循环依赖问题,这里就不说了

        最后再放一张我自己画的简单流程图,如下。。。

 

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...