您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
源码学习之Spring AOP
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
源码学习之Spring AOP
自猿其说Tech
2022-06-02
IP归属:未知
18000浏览
计算机编程
### 1 前言 实际工作中,为了满足业务需要,或者为多个不具有继承关系的对象引入一个公共行为,例如日志、监控、事务等,如果我们直接修改源码,会破坏原有的代码逻辑,可能产生大量重复的代码,也可能会造成一定的风险,这时候我们就会想到借助Spring AOP来做到这一点。 UserService是一个普通的bean ```java @Component public class UserService { public void test() { System.out.println("test..."); } } ``` 定义切面 ```java @Aspect @Component public class MyAspect { @Before("execution(public void com.jd.service.UserService.test())") public void myBefore(JoinPoint joinPoint) { System.out.println("before..."); } @After("execution(public void com.jd.service.UserService.test())") public void myAfter(JoinPoint joinPoint) { System.out.println("after..."); } @Around("execution(public void com.jd.service.UserService.test())") public Object cwAround(ProceedingJoinPoint point) { Object obj = null; System.out.println("around before..."); try { obj = point.proceed(); }catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println("around after..."); return obj; } } ``` 开启对Spring AOP支持的配置 ```java @ComponentScan("com.jd.service") @EnableAspectJAutoProxy public class AppConfig { } ``` 测试 ```java public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = (UserService) applicationContext.getBean("userService"); userService.test(); } } ``` 不出意外,我们会看到控制台中打印了如下的结果: ![](//img1.jcloudcs.com/developer.jdcloud.com/1ac1db81-fa64-419d-a3f7-4cbf2454573c20220602150620.png) 那么,Spring究竟是如何实现AOP的呢?下面通过本文来进行详细分析(文章有些长,可以先收藏待有时间后再慢慢看)。 ### 2 Spring AOP原理介绍 AOP: aspect object programming 面向切面编程,其功能就是让关注点代码与业务代码分离。关于AOP中的概念本身是比较难理解的,Spring官网上是这么说的: Let us begin by defining some central AOP concepts and terminology. These terms are not Spring-specific. Unfortunately, AOP terminology is not particularly intuitive.However, it would be even more confusing if Spring used its own terminology. 意思是,AOP中的这些概念不是Spring特有的,不幸的是,AOP中的概念不是特别直观的,但是,如果Spring重新定义自己的那可能会导致更加混乱。 我们先看下AOP中主要的概念定义: - Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、Advice等等; - Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行。 - Advice:表示通知,表示在一个特定连接点上所采取的动作。Advice分为不同的类型,后面详细讨论,在很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,并且在连接点周围维护一个Interceptor链; - Pointcut:表示切点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的,Advice将会执行在和切点表达式所匹配的连接点上; - Introduction:可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现; - Target object:目标对象,被代理对象; - AOP proxy:表示代理工厂,用来创建代理对象的,在Spring Framework中,要么是JDK动态代理,要么是CGLIB代理; - Weaving:表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如Aspejctj),或者运行时,比如Spring AOP。 下面用一个图描述下这些概念之间关系: ![](//img1.jcloudcs.com/developer.jdcloud.com/a8c23f2c-a5a3-4cfc-b524-786e2e70818720220602150704.png) 下面是Spring创建代理类的时序图: ![](//img1.jcloudcs.com/developer.jdcloud.com/e9c22c18-4a79-457b-ac7e-8be91f25f6d220220602150744.jpg) 下面是代理对象调用的时序图,以JDK动态代理为例: ![](//img1.jcloudcs.com/developer.jdcloud.com/12263da3-d68d-4841-98f1-8d76738e275420220602152434.jpg) ### 3 Spring AOP底层源码解析 #### 3.1 AOP基于注解的入口 在前言中的示例中,开启Sping AOP最重要的一个注解就是@EnableAspectJAutoProxy,这个注解主要就是往Spring容器中添加了一个AspectJAutoProxyRegistrar类型的Bean。 ![](//img1.jcloudcs.com/developer.jdcloud.com/16d22d69-f82e-466c-a9fa-ee5d5dfdf0d120220602161757.png) 具体看看注解里面的 AspectJAutoProxyRegistrar 这个类: ![](//img1.jcloudcs.com/developer.jdcloud.com/bcf8057c-0940-4529-a2bc-d9e6cdea874220220602161807.png) 再往下看: ![](//img1.jcloudcs.com/developer.jdcloud.com/d8f54873-01c7-4c17-accf-5962289978d420220602161820.png) 其实,注解@EnableAspectJAutoProxy的作用就是通过@Import注册 AOP 入口类AnnotationAwareAspectJAutoProxyCreator,从而开启了AOP,并设置了两个属性proxyTargetClass和exposeProxy。 ##### proxyTargetClass: true - 目标对象实现了接口– 使用 CGLIB 代理机制 - 目标对象没有接口(只有实现类) – 使用 CGLIB 代理机制 false - 目标对象实现了接口– 使用 JDK 动态代理机制(代理所有实现了的接口) - 目标对象没有接口(只有实现类) – 使用 CGLIB 代理机制 ##### exposeProxy: 用于设置代理对象是否需要暴露,说白了就是确认是否需要把代理对象设置到ThreadLocal中。 #### 3.2 是否生成代理 我们再来看AnnotationAwareAspectJAutoProxyCreator这个类: ![](//img1.jcloudcs.com/developer.jdcloud.com/7af9647f-dfc6-40af-b95b-304e67d717f520220602161901.png) 在类的层级中,我们看到AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor接口,当Spring在创建某个Bean时,就会进入到它对应的生命周期方法中,在某个Bean初始化之后,会调用wrapIfNecessary方法判断该 bean 是否生成代理。 在父类AbstractAutoProxyCreator的postProcessAfterInitialization中代码如下: ![](//img1.jcloudcs.com/developer.jdcloud.com/e56ce20f-4aba-4600-a867-80c67710d43520220602161913.png) 重点看一下这个方法 wrapIfNecessary,如果有切面就会生成对应的代理: ![](//img1.jcloudcs.com/developer.jdcloud.com/966ace3b-910a-403e-bb27-5be87f5c525520220602161924.png) getAdvicesAndAdvisorsForBean方法就是判断当前 bean 是否有切面 advisor,如果有切面就会走到 createProxy 方法,生成代理对象然后返回。 我们继续分析,看下Spring是如何寻找切面Advisor的。先看找合格切面的逻辑: ![](//img1.jcloudcs.com/developer.jdcloud.com/07db8e71-cb5d-48d1-bc25-0c99ce653ba020220602161942.png) 在findEligibleAdvisors方法中会寻找合格的切面,主要包含两个过程:找到候选的切面和判断候选的切面是否适用于当前Bean上面,对应的方法就是findCandidateAdvisors和findAdvisorsThatCanApply。 ![](//img1.jcloudcs.com/developer.jdcloud.com/331b81b7-e276-4b61-a579-32548cd6421120220602161955.png) ##### 3.2.1 获取Advisor集合 下面重点看一下 findCandidateAdvisors()寻找合格切面的过程: ![](//img1.jcloudcs.com/developer.jdcloud.com/dc0b04cc-93af-49dd-988f-d236b6f1e77d20220602162009.png) 重点看一下这个 buildAspectJAdvisors()方法,此方法的处理逻辑如下: 首先,获取 spring 容器中的所有 bean 的名称 BeanName,组成 BeanNames 数组。 ![](//img1.jcloudcs.com/developer.jdcloud.com/c364d1cb-dccf-4bb1-bd26-e7cbd3cc76ef20220602162023.png) 然后,循环遍历 BeanNames 数组,判断该类上面是否有@Aspect 注解,如果有则是我们要找的,添加到 aspectNames。 ![](//img1.jcloudcs.com/developer.jdcloud.com/9b5e870d-f41e-457d-91e0-28029237d65220220602162051.png) 再把带@Aspect 注解的单个 beanName 包装成 MetadataAwareAspectInstanceFactory 工厂,用于获取 getAdvisors 创建切面 advisor集合使用(注:一个 bean 当中可能有多个 advisor,单个 advisor 是由 poincut 和 advice 组成,因此每个 bean对应的都是一个 advisors 集合) ![](//img1.jcloudcs.com/developer.jdcloud.com/91e5fce5-f3b1-4980-bdde-b9e282f4dc5820220602162108.png) 然后,创建切面 advisor 对象,下面是 getAdvisors 过程,循环单个 Bean 里面的除了@PointCut 注解的方法,判断当前方法上面的注解是否在@Around、@Before、@After、 @AfterReturning、 @AfterThrowing 中间,如果包含在这些注解之中, 就把注解里面的信息,比如表达式,argNames,注解类型等信息封装成对象 AspectJAnnotation。 ![](//img1.jcloudcs.com/developer.jdcloud.com/0ceed9e0-d9a5-4f47-a5b5-71a22b58246f20220602162122.png) getAdvisors方法中又包含了以下几步: - 从工厂中获取有@Aspect 注解的类 Class; - 从工厂中获取有@Aspect 注解的类的名称; - 创建工厂的装饰类; - 从上面包装的工厂中获取对应的带@Aspect 注解的单个 Class 对象,遍历这个 aspectClass 对象中的所有没有被 @Pointcut 标注的方法,然后把收集到的方法进行过滤; ![](//img1.jcloudcs.com/developer.jdcloud.com/f770e0a9-feb8-45ea-a902-910b665f9a2420220602162139.png) 再看一下getAdvisor这个方法,循环遍历没有@Pointcut 注解的方法,并且把每个方法组装成 Advisor 对象。 ![](//img1.jcloudcs.com/developer.jdcloud.com/43d04d6a-9db9-454f-8fd2-819056f9e6da20220602162510.png) ##### PointCut对象创建 我们先看一下如何获取 PointCut 对象的,看一下 getPointcut()这个方法,把注解信息封装成 AspectJAnnotation 对象,封装一个 PointCut 类,并且把前面从注解里面解析的表达式设置进去 ![](//img1.jcloudcs.com/developer.jdcloud.com/44e9754b-5cd7-4923-b35e-60c49c2400b120220602162540.png) 重点看一下,怎么封装成AspectJAnnotaition 对象的,看一下这个方法 findAspectJAnnotationOnMethod,如下所示: ![](//img1.jcloudcs.com/developer.jdcloud.com/57a149ea-0aa4-44a8-bd6d-18f3f94805e820220602162552.png) 遍历所有注解(Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),找到对应的@Around@Before等注解封装成AspectJAnnotation对象。 ![](//img1.jcloudcs.com/developer.jdcloud.com/9e0dd4c6-bffd-44fe-ae94-f931c7970c5320220602162615.png) 下面再看一下 new AspectJAnnotation<>(result)这里面对应的内容。 ![](//img1.jcloudcs.com/developer.jdcloud.com/79efbdbd-7799-4497-be87-d51cd4f7962f20220602162632.png) 以上,我们拥有了AspectJExpressionPointcut,有了 pointcut 对象。 ##### Advice对象创建 下面我们再看一下advice 对象的创建过程 ![](//img1.jcloudcs.com/developer.jdcloud.com/97de04e5-3a54-4924-af39-d864aa15917920220602162654.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/f2c79aed-8492-4639-9f5f-7b29f6c09ff620220602162709.png) 看一下 instantiateAdvice 这个方法,如下图所示: ![](//img1.jcloudcs.com/developer.jdcloud.com/5baedadb-290c-4df6-8096-2227b942dbf320220602162725.png) 继续看 getAdvice 这个方法,这个方法是获取有@Aspect 注解的类,然后把方法上面的注解包装成 AspectJAnnotation 对象: ![](//img1.jcloudcs.com/developer.jdcloud.com/bfb9bcd4-40c1-490f-a24b-5b4bbd07ecc820220602162738.png) 看下对应的 AspectJAnnotation 这个类, 这个类中包括 6 种注解类型: ![](//img1.jcloudcs.com/developer.jdcloud.com/52cbd092-a847-4355-a9b4-190b84da1abb20220602162753.png) 我们再往下看 getAdvice 这个方法: ![](//img1.jcloudcs.com/developer.jdcloud.com/2e75d711-eb6c-4c0b-b4f2-41cdd1a2b57d20220602162839.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/3ce4ed2a-6f06-41ce-b8c2-85d43bc5052e20220602162854.png) 不同的Advice对象如下:AspectJAroundAdvice,AspectJAfterAdvice,AspectJAfterThrowingAdvice,AspectJMethodBeforeAdvice,AspectJAfterReturningAdvice,最终把注解对应的Advice对象和 pointCut对象封装成Advisor对象。 ##### 小结 上面的一大堆方法就是为了给一个类中的某一个方法包装成对应的 Advisor,一个类中可能有多个不同的方法,每个方法都包装成对应的 Advisor 对象,这样对应的一个类中就会有一个List<Advisor>集合,返回给前面。 ##### 3.2.2 寻找匹配的Advisor集合 上述我们分析了Spring如何把工程中所有有@Aspect这个注解的类封装成List<Advisor>返回,接下来,我们再继续分析Spring是如何判断当前bean是否能匹配到集合中的切面,即集合中是否包含当前bean的切面信息。 我们从findAdvisorsThatCanApply方法看下匹配合格切面的过程: ![](//img1.jcloudcs.com/developer.jdcloud.com/cfc07454-17b5-480b-af23-ddf10ec7d5b720220602162931.png) 继续看AopUtils.findAdvisorsThatCanApply这个方法,主要是寻找所有Advisor中适用于当前class的Advisor,引介切面和普通的处理不太一样,所以进行了分开处理。 ![](//img1.jcloudcs.com/developer.jdcloud.com/d9736718-9ec7-4e08-9dfc-291c225ae18620220602162948.png) 真正的匹配是在canApply方法中 ![](//img1.jcloudcs.com/developer.jdcloud.com/d12ed9c4-7b48-4523-9266-42f0cb27947520220602163003.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/9545f3db-9625-49cf-9433-9685f294a4c720220602163015.png) #### 3.3 代理类的创建 当某个Bean有切面Advisor匹配时,便进入到了创建代理对象的过程中,我们回到AbstractAutoProxyCreator类的postProcessBeforeInstantiation方法中。 ![](//img1.jcloudcs.com/developer.jdcloud.com/f6ca3d3a-6a62-45b4-8f36-beac95a11ff320220602163109.png) 看下创建代理的过程: 对于代理类的创建及处理,Spring委托给了ProxyFactory去处理,在createProxy方法中,主要就是对ProxyFactory的初始化操作,进而对真正的创建代理做准备,这些初始化操作包括如下内容。 - 获取当前类的属性; - 添加代理接口; - 封装Advisor并加入到ProxyFactory中; - 设置要代理的类; - 提供了定制代理的方法customizeProxyFactory; - 获取代理对象; ![](//img1.jcloudcs.com/developer.jdcloud.com/e605cac1-b9f1-4f10-aca5-b22f0ec4b5eb20220602163530.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/f38f344e-9a80-49bb-a4d2-b6033f1c58e620220602163542.png) 其中,封装Advisor并加入到ProxyFactory中和创建代理是两个相对繁琐的过程。 buildAdvisors方法将切面对象重新包装,把自定义的MethodInterceptor类型的类包装成Advisor切面类并加入到代理工厂中。 ![](//img1.jcloudcs.com/developer.jdcloud.com/250ca234-ecf1-48c1-affc-56a1706fdaef20220602163555.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/6620303d-ec2d-473e-8916-c61be722e47620220602163608.png) proxyFactory.getProxy方法会根据proxyTargetClass参数和是否实现接口,来判断是采用JDK代理还是CGLIB代理。 ![](//img1.jcloudcs.com/developer.jdcloud.com/97552ec6-cc33-4c48-93b3-9acbb1ad543b20220602163620.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/97fafe7b-65be-49e0-af73-83825afe086c20220602163632.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/5c6d877d-ec9f-45e9-95bc-39b4586c71ab20220602163640.png) JdkDynamicAopProxy 1. 在构造JdkDynamicAopProxy对象时,会先拿到被代理对象自己所实现的接口,并且额外的增加SpringProxy、Advised、DecoratingProxy三个接口,组合成一个Class[],并赋值给proxiedInterfaces属性; 2. 并且检查这些接口中是否定义了equals()、hashcode()方法; 3. 执行 Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this) ,得到代理对象,JdkDynamicAopProxy作为InvocationHandler,代理对象在执行某个方法时,会进入到JdkDynamicAopProxy的**invoke()**方法中。 ObjenesisCglibAopProxy 1. 创建Enhancer对象; 2. 设置Enhancer的superClass为通过ProxyFactory.setTarget()所设置的对象的类; 3. 设置Enhancer的interfaces为通过ProxyFactory.addInterface()所添加的接口,以及SpringProxy、Advised、DecoratingProxy接口; 4. 设置Enhancer的Callbacks为DynamicAdvisedInterceptor; 5. 最后创建一个代理对象,代理对象在执行某个方法时,会进入到DynamicAdvisedInterceptor的intercept()方法中。 #### 3.4 代理类的调用 上面Spring已经帮我们创建出来代理对象了,现在我们继续分析拿到代理对象后的调用,以JDK动态代理为例,CGLIB调用逻辑类似。 代理对象创建完成后,当发生代理对象调用时,肯定会调用到实现了invocationHandler接口的类,这个类就是JdkDynamicAopProxy,必定会调用到该类的invoke 方法。 Ok,我们看看invoke方法: ![](//img1.jcloudcs.com/developer.jdcloud.com/fc930f1f-7898-451a-a3b1-6ae0180bf2a220220602163737.png) 从代理工厂中拿到切面,并且跟当前被代理类和当前被调用方法匹配,如果匹配就返回切面中的advice 对象,这就是advice 执行链,其实就是对应的 interceptor List 列表: ![](//img1.jcloudcs.com/developer.jdcloud.com/9e747167-3896-400b-8cbb-323fa71c0d1f20220602163755.png) 我们看下getInterceptorsAndDynamicInterceptionAdvice这个方法的逻辑: ![](//img1.jcloudcs.com/developer.jdcloud.com/38208f42-425d-4817-b916-f66b0b22cffe20220602163808.png) 继续往下跟,从代理工厂中获得该被代理类的所有切面advisor,config就是代理工厂对象 ![](//img1.jcloudcs.com/developer.jdcloud.com/b33e20cc-f0ef-473c-a60b-ef696468633420220602163820.png) 然后再开始一轮遍历,如果切面的pointCut和被代理对象Class类是匹配的,说明被代理对象这个Class类是切面要拦截的对象。 ![](//img1.jcloudcs.com/developer.jdcloud.com/4fbd5c63-5130-48ff-a768-946813d6aba520220602163834.png) 判断匹配的被代理对象中的方法是否是切面pointcut需要拦截的方法 ![](//img1.jcloudcs.com/developer.jdcloud.com/27031752-7a29-46e9-abde-a30a8d94d3b620220602163851.png) 如上图所示,获取到切面advisor中的advice,并且包装成MethodInterceptor类型的对象,接下来我们看一下getInterceptors方法,此步骤最关键的是对不同类型的advice进行了统一包装,方便后续进行统一的代码调用。 如下图所示: ![](//img1.jcloudcs.com/developer.jdcloud.com/98ea51eb-4e88-45e2-a8de-417cd1b1c10520220602163905.png) 如果是MethodInterceptor类型的,如:AspectJAroundAdvice、AspectJAfterAdvice、AspectJAfterThrowingAdvice,则直接添加到拦截器;如果是MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter,则需要advice包装成MethodInterceptor类型的advice。 我们再返回到invoke方法中,继续往下看,从代理工厂中拿过滤器链,如果该方法没有执行链,则说明这个方法不需要被拦截,则直接反射调用 ![](//img1.jcloudcs.com/developer.jdcloud.com/c46c537b-58b8-451e-ad52-3fc6a9c630d220220602163917.png) 如果有执行连,即chain不为空,我们继续分析下proceed这个方法的调用逻辑。 我们以具体的示例来分析一下AOP调用的过程,此例中共有一个 before、一个after和一个aroud切面,通过调试发现拦截器数组中的顺序为AspectJAfterAdvice、AspectJAroundAdvice、MethodBeforeAdviceInterceptor,因此接下来先执行AspectJAfterAdvice内部的invoke方法。 ![](//img1.jcloudcs.com/developer.jdcloud.com/e9001739-e998-462f-b885-d2f71de2ef3c20220602163932.png) 此时AspectJAfterAdvice这里有一个mi.proceed()这个方法又会回到ReflectiveMethodInvocation这个类中,这时候索引又会+1,然后从链中后去下一个advice,这时候就会找到AspectJAroundAdvice中的invoke,如下图所示: ![](//img1.jcloudcs.com/developer.jdcloud.com/b3e4291d-8425-4a42-8f2e-3df8e4f198bf20220602163945.png) 此方法会调用到MyAspect中cwAround方法 ![](//img1.jcloudcs.com/developer.jdcloud.com/96537d47-3297-4dbb-967d-d5862e47603b20220602164000.png) 通过调用point.proceed()这个方法又会回到ReflectiveMethodInvocation这个类中,这时候索引又会+1,然后从链中后去下一个advice,然后又开始调用MethodBeforeAdviceInterceptor中的invoke ,如下图所示: ![](//img1.jcloudcs.com/developer.jdcloud.com/cc24c186-315f-444d-8b10-9c2e859818a120220602164032.png) 此时先调用Aspect中的before方法,然后调完以后,这里有一个mi.proceed()这个方法又会回到 ReflectiveMethodInvocation这个类中,这时候索引又会+1,此时的索引值已经等于数组大小-1了。直到数组链中全部调用完后会调用到具体的invokeJoinpoint方法: ![](//img1.jcloudcs.com/developer.jdcloud.com/6fc23292-68a9-4297-ae5e-bcb695ae9a5620220602164047.png) 此时就会调用被代理的方法了,调用完毕后,返回的值通过MethodBeforeAdviceInterceptor返回到AspectJAroundAdvice,最终返回到MyAspect中的cwAround方法,继续执行后面的逻辑。cwAround方法执行完毕后,把obj返回给上一个执行的advice->AspectJAfterAdvice中的invoke完成值的返回,最终会执行finally方法调用,完成@After方法调用,至此,代理对象的调用就全部结束了。 ### 4 总结 OOP表示面向对象编程,是一种编程思想,AOP表示面向切面编程,也是一种编程思想。Spring AOP就是Spring为了让程序员更加方便的做到面向切面编程所提供的技术支持,换句话说,就是Spring提供了一套机制,可以让我们更加容易的来进行AOP。 使用注解的方式来定义Pointcut和Advice,Spring并不是首创,首创是AspectJ,而且也不仅仅只有Spring提供了一套机制来支持AOP,还有比如 JBoss 4.0、aspectwerkz等技术都提供了对于AOP的支持。而刚刚说的注解的方式,Spring是依赖了AspectJ的,或者说,Spring是直接把AspectJ中所定义的那些注解直接拿过来用,自己没有再重复定义了,不过也仅仅只是把注解的定义赋值过来了,每个注解具体底层是怎么解析的,还是Spring自己做的,所以我们在用Spring时,如果你要用@Before、@Around等注解,是需要单独引入aspecj相关jar包的,比如: ``` <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.5</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> ``` AspectJ是在编译时对字节码进行了修改,是直接在类对应的字节码中进行增强的,也就是可以理解为是在编译时就会去解析@Before这些注解,然后得到代理逻辑,加入到被代理的类中的字节码中去的,所以如果想用AspectJ技术来生成代理对象 ,是需要用单独的AspectJ编译器的。我们在项目中很少这么用,我们仅仅只是用了@Before这些注解,而我们在启动Spring的过程中,Spring会去解析这些注解,然后利用动态代理机制生成代理对象的。 ### 5 参考资料 - Spring 官方文档 https://docs.spring.io/spring/docs/5.0.6.RELEASE/spring-framework-reference/core.html#spring-core - Spring【AOP模块】就这么简单 - 《Spring源码深度解析 第2版》 ------------ ###### 自猿其说Tech-JDL京东物流技术与数据智能部 ###### 作者:于朔
原创文章,需联系作者,授权转载
上一篇:测试用例设计方法六脉神剑——第五剑:化气为型,场景用例破云
下一篇:测试用例设计方法六脉神剑——第四剑:石破天惊,功能图法攻阵
相关文章
Taro小程序跨端开发入门实战
Flutter For Web实践
配运基础数据缓存瘦身实践
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号