Spring源码-AOP具体源码

ops/2024/10/10 15:00:17/

1.类ProxyFactory

核心方法:getProxy

1.DefaultAopProxyFactory#createAopProxy

判断使用JDK还是CGLIB动态代理的代码如下:

java">@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {// 如果ProxyFactory的isOptimize为true,Spring认为cglib比jdk动态代理要快// 或者isProxyTargetClass为true,// 或者被代理对象没有实现接口,// 或者只实现了SpringProxy这个接口// 那么则利用Cglib进行动态代理,但如果被代理类是接口,或者被代理类已经是进行过JDK动态代理而生成的代理类了则只能进行JDK动态代理// 其他情况都会进行JDK动态代理,比如被代理类实现了除SpringProxy接口之外的其他接口// 是不是在GraalVM虚拟机上运行if (!NativeDetector.inNativeImage() &&(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {//被代理的类 是接口return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}
}

判断该使用哪种技术生成代理对象

config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config),判断是否优化、是否代理目标类、是否没有接口。满足这个条件,返回CGLIB代理对象;不满足,就返回JDK代理对象。

同时还会判断,判断:targetClass.isInterface() || Proxy.isProxyClass(targetClass),如果被代理的类是接口,那就返回JDK代理对象;如果类是被JDK代理的类,那继续使用JDK代理对象。

(isOptimize()表示Spring启动优化,那就使用CGLIB;isProxyTargetClass 表示代理的是类,JDK只能代理接口;hasNoUserSuppliedProxyInterfaces表示用户有没有addInterface)

2.getProxy

核心:通过不同的技术,产生代理对象(有两种实现方法,CGLIB和JDK动态代理)

2.Jdk生成的代理对象的执行流程

核心方法:JdkDynamicAopProxy#invoke

1.targetSource = this.advised.targetSource

拿到被代理对象

2.如果接口中的是equals方法或者hasCode方法,那它们不用走代理

3.if (this.advised.exposeProxy),true-->把代理对象放到ThredLocal中

如果ProxyFactory的exposeProxy为true,则将代理对象设置到currentProxy这个ThreadLocal中

4.target = targetSource.getTarget();Class<?> targetClass

拿到被代理对象和代理类

5.chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(2.1详细讲

代理对象在执行某个方法时,根据方法筛选出匹配的 Advisor ,并适配成 Interceptor生成chain链。(生成MethodIntercept链)

6.如果chain.isEmpty()(没有代理逻辑),则调用invokeJoinpointUsingReflection(直接执行 被代理对象的方法)

7.如果有代理逻辑,invocation =new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain),将chain 组合成invocation (2.2详解ReflectiveMethodInvocation)

retVal = invocation.proceed();// 执行MethodIntercept逻辑

java">@Override@Nullablepublic Object proceed() throws Throwable {// We start with an index of -1 and increment early.// currentInterceptorIndex初始值为-1,每调用一个interceptor就会加1// 当调用完了 最后一个interceptor后 就会执行被代理方法if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}// currentInterceptorIndex初始值为-1Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);// 当前interceptor是InterceptorAndDynamicMethodMatcher,则先进行匹配,匹配成功后再调用该interceptor// 如果没有匹配则递归调用proceed()方法,调用下一个interceptorif (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {// Evaluate dynamic method matcher here: static part will already have// been evaluated and found to match.InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());// 动态匹配,根据方法参数匹配if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {return dm.interceptor.invoke(this);}else {// Dynamic matching failed.// Skip this interceptor and invoke the next in the chain.// 不匹配则执行下一个MethodInterceptorreturn proceed();}}else {// It's an interceptor, so we just invoke it: The pointcut will have// been evaluated statically before this object was constructed.// 直接调用MethodInterceptor,传入this,在内部会再次调用proceed()方法进行递归// 比如MethodBeforeAdviceInterceptorreturn ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}}

invoke->proceed->invoke->proceed,链路调用实现AOP逻辑

8.结束

2.1getInterceptorsAndDynamicInterceptionAdvice的筛选逻辑

代理对象在执行某个方法时,根据方法筛选出匹配的 Advisor ,并适配成 Interceptor,生成chain链。(生成MethodIntercept链)

核心:所有Advice都会封装成Advisor,最终都会转化为MethodInterceptor

1.advisors = config.getAdvisors()

从ProxyFactory中拿到所设置的Advice(config就是ProxyFactory)

2.遍历每一个Advisor,筛选出符合条件的Advisor

3.先看class是否匹配class匹配通过,再看方法是否匹配(现在只拿MethodMatcher中的matches进行匹配)

4.如果都匹配,执行interceptors = registry.getInterceptors,将Advisor封装成为MethodInterceptor

在这里使用到设配器模式,将advisor中的advice适配成MethodInterceptor(适配的代码如下)

java">@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {List<MethodInterceptor> interceptors = new ArrayList<>(3);Advice advice = advisor.getAdvice();// 取出Adviceif (advice instanceof MethodInterceptor) {interceptors.add((MethodInterceptor) advice);}// 将Advice适配成MethodInterceptorfor (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]);
}

三个适配器

  • MethodBeforeAdviceAdapter():before
  • AfterReturningAdviceAdapter(): AfterRurening
  • ThrowsAdviceAdapter(): Throws

注意:afterThrowing限制方法的参数是1个或者4个。部分核心代码如下:

java">class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {@Overridepublic boolean supportsAdvice(Advice advice) {return (advice instanceof AfterReturningAdvice);}@Overridepublic MethodInterceptor getInterceptor(Advisor advisor) {AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();return new AfterReturningAdviceInterceptor(advice);}}
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {private final AfterReturningAdvice advice;/*** Create a new AfterReturningAdviceInterceptor for the given advice.* @param advice the AfterReturningAdvice to wrap*/public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {Assert.notNull(advice, "Advice must not be null");this.advice = advice;}@Override@Nullablepublic Object invoke(MethodInvocation mi) throws Throwable {Object retVal = mi.proceed();this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());return retVal;}}

4.1.判断isRuntime(),如果runtime为true,再包装成InterceptorAndDynamicMethodMatcher(runtime为true->进行入参匹配),还需要看MethodMatcher中的matches是否可以匹配,如果匹配,才可以执行代理逻辑。

java">if (match) {// 如果匹配则将Advisor封装成为Interceptor,当前Advisor中的Advice可能即是MethodBeforeAdvice,也是ThrowsAdviceMethodInterceptor[] interceptors = registry.getInterceptors(advisor);if (mm.isRuntime()) {// Creating a new object instance in the getInterceptors() method// isn't a problem as we normally cache created chains.for (MethodInterceptor interceptor : interceptors) {interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));}}else {interceptorList.addAll(Arrays.asList(interceptors));}
}

2.2 ReflectiveMethodInvocation的执行逻辑

1.invocation.proceed

执行 MethodIntercept逻辑

2.invokeJoinpoint

当Interceptor执行完,就执行被代理的方法

3.满足方法的匹配(runtime为true),根据方法参数匹配

如果匹配,调用interceptor.invoke,执行被包装的代理逻辑

如果不匹配,则执行下一个MethodInterceptor,调用proceed()

3.SpringAOP的实现逻辑

核心注解:@EnableAspectJAutoProxy-->@Import(AspectJAutoProxyRegistrar.class)-->registerBeanDefinitions--> registerAspectJAnnotationAutoProxyCreatorIfNecessary-->forceAutoProxyCreatorToUseClassProxying

解释:注册一个 AnnotationAwareAspectJAutoProxyCreator 类型的Bean, beanName为AUTO_PROXY_CREATOR_BEAN_NAME(AnnotationAwareAspectJAutoProxyCreator就是BeanPostProcessor)

forceAutoProxyCreatorToUseClassProxying是将注解中的属性注册到BeanDefinition中,包含proxyTargetClass和exposeProxy。

3.1AOP代理对象的产生时机

当生成Bean的时候,调用doCreateBean-->initializeBean-->applyBeanPostProcessorsAfterInitialization(进入初始化后逻辑),

进行如下判断:拿到所有的BeanPostProcessor,包括AnnotationAwareAspectJAutoProxyCreator,如果存在这个BeanPostProcessor,则走代理逻辑,生成代理对象。

进入postProcessAfterInitialization生成代理对象的逻辑(类AbstractAutoProxyCreator)

(注意:AnnotationAwareAspectJAutoProxyCreator的父类就是AbstractAutoProxyCreator)

AbstractAutoProxyCreator#wrapIfNecessary的代码如下:

java">protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}// advisedBeans表示已经判断过了的bean,false表示此bean不需要进行Aopif (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}// 当前正在创建的Bean不用进行AOP,比如切面Beanif (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// Create proxy if we have advice.// 判断当前bean是否存在匹配的advice,如果存在则要生成一个代理对象// 此处根据类以及类中的方法去匹配到Interceptor(也就是Advice),然后生成代理对象,代理对象在执行的时候,还会根据当前执行的方法去匹配Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {// advisedBeans记录了某个Bean已经进行过AOP了this.advisedBeans.put(cacheKey, Boolean.TRUE);Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;
}

1.调用wrapIfNecessary

2.判断FALSE.equals(this.advisedBeans.get(cacheKey))

表示已经判断过了的bean,false表示此bean不需要进行AOP

3.判断isInfrastructureClass

表示当前正在创建的Bean不用进行AOP,比如切面Bean、Advice、Pointcut、Advisor、AopInfrastructureBean,同时调用advisedBeans.put(cacheKey, Boolean.FALSE):把不进行AOP的bean放到缓存

4.判断shouldSkip(模版方法)

留给子类扩展,子类可以定义哪些Bean不需要AOP

5.Object[] specificInterceptors = getAdvicesAndAdvisorsForBean

找到所有bean中的Advisor,内容很多,在3.2。

6.如果specificInterceptors!=null,调用

createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)),生成代理对象,调用addAdvisors-->setTargetSource-->proxyFactory.getProxy(classLoader)

在执行的时候还会对Advisor筛选,执行符合不同方法的Advisor(对于一个bean,并不是所有的Adivisor都生效

3.2getAdvicesAndAdvisorsForBean的实现逻辑

判断某个bean是否需要进行AOP,匹配到bean的所有Advisor(第一次匹配)。bean在执行方法的时候还会进行一次匹配,细粒度的匹配(pointcut匹配)(第二次匹配)。(一共两次)

getAdvicesAndAdvisorsForBean(类AbstractAdvisorAutoProxyCreator)-->findEligibleAdvisors(找到合格的Advisors)

1.调用findCandidateAdvisors,找到所有的Advisor

1.1findAdvisorBeans

先找到 所有Advisor类型 的Bean对象.findCandidateAdvisors

具体是:从beanFactory拿到所有Advisor的beanName,找到Advisor的bean添加到list中,然后return

BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors:本方法会被多次调用,因为一个Bean在判断要不要进行AOP时,都会调用这个方法

获取切面类中没有加@Pointcut的方法,进行遍历生成Advisor

ReflectiveAspectJAdvisorFactory#getAdvisorMethods:

java">private List<Method> getAdvisorMethods(Class<?> aspectClass) {List<Method> methods = new ArrayList<>();// 拿到切面类中所有没有加@Pointcut的方法ReflectionUtils.doWithMethods(aspectClass, methods::add, adviceMethodFilter);// 对方法进行排序,按注解和方法名字进行排序if (methods.size() > 1) {methods.sort(adviceMethodComparator);}return methods;
}

1.2advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()

从所有切面中解析,得到Advisor对象,也就是解析加了@Aspect注解的bean

具体是:找出来Factory中所有bean,然后判断有无@Aspect注解,拿到加了@Aspect注解的bean,factory =new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName),把这个bean封装成BeanFactoryAspectInstanceFactory对象,利用BeanFactoryAspectInstanceFactory来解析Aspect类。

调用getAdvisors,拿到Bean的类型和name,getAdvisorMethods(获取切面类中所有没有加Pointcut注解的方法),methods.sort,先根据Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class的顺序排序,然后根据方法的名字排序。

调用getAdvisor,将方法生成Advisor(拿到所有带Pointcut内容的Advisor),他会调用expressionPointcut = getPointcut,拿到当前方法所对应的Pointcut对象,但是注意:如果当前方法上是这么写的@After("pointcut()"),那么此时得到的Pointcut并没有去解析pointcut()得到对应的表达式,然后调用InstantiationModelAwarePointcutAdvisorImpl,拿到所有带Pointcut内容的Advisor,之后核心调用getAdvice(解析method上面的注解),根据不同的注解生成Advice对象。

2.调用eligibleAdvisors = findAdvisorsThatCanApply,进行筛选,将找到的所有Advisor和userService的方法进行匹配,找到符合userServicce方法的Advisor(利用方法过滤器)

3.sortAdvisors,对Advisor进行排序,按 Ordered接口、@Order 注解进行排序

3.3 AOP的执行逻辑

Advisor在执行的时候,就会进到这个方法getAdvice(类InstantiationModelAwarePointcutAdvisorImpl

getAdvice-->instantiateAdvice-->advice =getAdvice(this.aspectJAdviceMethod,...)

1.getAspectClass

拿到切面类

2.aspectJAnnotation =findAspectJAnnotationOnMethod

拿到当前candidateAdviceMethod方法上的注解信息

3.根据不同的注解生成不同的Advice,然后执行。代码如下:

java">@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();validate(candidateAspectClass);// 拿到当前candidateAdviceMethod方法上的注解信息AspectJAnnotation<?> aspectJAnnotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {return null;}// If we get here, we know we have an AspectJ method.// Check that it's an AspectJ-annotated classif (!isAspect(candidateAspectClass)) {throw new AopConfigException("Advice must be declared inside an aspect type: " +"Offending method '" + candidateAdviceMethod + "' in class [" +candidateAspectClass.getName() + "]");}if (logger.isDebugEnabled()) {logger.debug("Found AspectJ method: " + candidateAdviceMethod);}AbstractAspectJAdvice springAdvice;// 按不同的注解类型得到不同的Adviceswitch (aspectJAnnotation.getAnnotationType()) {case AtPointcut:if (logger.isDebugEnabled()) {logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");}return null;case AtAround:// @AroundspringAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtBefore:springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtAfter:springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtAfterReturning:springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();if (StringUtils.hasText(afterReturningAnnotation.returning())) {springAdvice.setReturningName(afterReturningAnnotation.returning());}break;case AtAfterThrowing:springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {springAdvice.setThrowingName(afterThrowingAnnotation.throwing());}break;default:throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);}// Now to configure the advice...springAdvice.setAspectName(aspectName);springAdvice.setDeclarationOrder(declarationOrder);String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);if (argNames != null) {springAdvice.setArgumentNamesFromStringArray(argNames);}springAdvice.calculateArgumentBindings();return springAdvice;
}

示例,如果使用After注解,After类的代码如下:核心是invoke方法

java">public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable {public AspectJAfterAdvice(Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {super(aspectJBeforeAdviceMethod, pointcut, aif);}@Override@Nullablepublic Object invoke(MethodInvocation mi) throws Throwable {try {return mi.proceed();}finally {invokeAdviceMethod(getJoinPointMatch(), null, null);}}@Overridepublic boolean isBeforeAdvice() {return false;}@Overridepublic boolean isAfterAdvice() {return true;}}

补充:@Around中使用ProceedJoinPoint,其他的注解都是使用JoinPoint。


http://www.ppmy.cn/ops/123542.html

相关文章

【VUE】虚拟DOM真的比真实DOM性能好吗

首次渲染大量DOM时&#xff0c;由于多了一层虚拟DOM的计算&#xff0c;会比innerHTML插入慢。它能保证性能下限&#xff0c;在真实DOM操作的时候进行针对性的优化时&#xff0c;还是更快的。 虚拟DOM&#xff08;Virtual DOM&#xff09;相比真实DOM&#xff08;Real DOM&…

golang包管理

package 在工程化的Go语言开发项目中&#xff0c;Go语言的源码复用是建立在包&#xff08;package&#xff09;基础之上的。本文介绍了Go语言中如何定义包、如何导出包的内容及如何导入其他包。 包与依赖管理 本章学习目标 掌握包的定义和使用掌握init初始化函数的使用掌握…

前端 + Nginx + 后端架构的无感升级方案

一、前端无感升级 构建新的前端包 使用 Webpack、Vite 等工具进行打包&#xff0c;生成带有版本号或哈希值的静态文件名。确保 index.html 引用最新的静态资源文件&#xff08;例如 app.js?versionabc123&#xff09;。 上传静态资源到服务器 将打包后的前端静态资源上传到 Ng…

昇思MindSpore进阶教程--数据处理性能优化(中)

大家好&#xff0c;我是刘明&#xff0c;明志科技创始人&#xff0c;华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享&#xff0c;如果你也喜欢我的文章&#xff0c;就点个关注吧 shuffle性能优化 shuffle操作主要是对有…

Web3与传统互联网的比较:机遇与挑战

随着科技的不断进步&#xff0c;Web3作为新一代互联网的概念逐渐浮出水面&#xff0c;改变了我们对网络的认知。相较于传统互联网&#xff0c;Web3在许多方面展现出不同的特征与潜力。本文将对Web3与传统互联网进行比较&#xff0c;探讨其带来的机遇与挑战。 一、核心概念的差异…

ubuntu下载gitee库源码

在Ubuntu系统中&#xff0c;你可以通过以下步骤从Gitee&#xff08;码云&#xff09;下载源码&#xff1a; 1. 安装Git 首先&#xff0c;确保你的系统上已经安装了Git。你可以使用以下命令安装Git&#xff1a; sudo apt-get update sudo apt-get install git2. 配置Git 在使…

git 相关问题解决一一记录

文章目录 gitssh.github.com: Permission denied (publickey)1. 检查 SSH 密钥生成新的 SSH 密钥添加 SSH 密钥到 GitHub 2. 配置 SSH 代理启动 SSH 代理添加私钥到 SSH 代理 3. 检查 SSH 配置文件4. 测试 SSH 连接5. 检查防火墙和网络设置6. 检查 GitHub 账户设置详细步骤 更新…

深度学习常见问题

1.YOLOV5和YOLOV8的区别 YOLOv5 和 YOLOv8 是两个版本的 YOLO&#xff08;You Only Look Once&#xff09;目标检测算法&#xff0c;它们在网络架构、性能优化、功能扩展等方面有显著的区别。YOLOv5 是 YOLO 系列的重要改进版本&#xff0c;而 YOLOv8 是最新的一次重大升级&am…