【Spring源码】Spring中的AOP底层原理分析
AOP中的几个概念
Advisor 和 Advice
Advice,我们通常都会把他翻译为通知,其实很不好理解,其实他还有另外一个意思,就是“建议”,我觉得把Advice理解为“建议”会更好。
比如,我们已经完成了一个功能,这时客户跟我们说,我建议在这个功能之前可以再增加一些逻辑,再之后再增加一些逻辑。
在Spring中,Advice分为:
前置Advice:MethodBeforeAdvice
后置Advice:AfterReturningAdvice
环绕Advice:MethodInterceptor
异常Advice:ThrowsAdvice
在利用Spring AOP去生成一个代理对象时,我们可以设置这个代理对象的Advice。
而对于Advice来说,它只表示了“建议”,它没有表示这个“建议”可以用在哪些方面。
就好比,我们已经完成了一个功能,客户给这个功能提了一个建议,但是这个建议也许也能用到其他功能上。
这时,就出现了Advisor,表示一个Advice可以应用在哪些地方,而“哪些地方”就是Pointcut(切点)。
Pointcut
切点,表示我想让哪些地方加上我的代理逻辑。
比如某个方法,
比如某些方法,
比如某些方法名前缀为“find”的方法,
比如某个类下的所有方法,等等。
在Pointcut中,有一个MethodMatcher,表示方法匹配器。
使用ProxyFactory通过编程创建AOP代理
定义一个MyAdvisor
public class MyAdvisor implements PointcutAdvisor {
@Override
public Pointcut getPointcut() {
NameMatchMethodPointcut methodPointcut = new NameMatchMethodPointcut();
methodPointcut.addMethodName("test");
return methodPointcut;
}
@Override
public Advice getAdvice() {
MethodBeforeAdvice methodBeforeAdvice = new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行方法前"+method.getName());
}
};
return methodBeforeAdvice;
}
@Override
public boolean isPerInstance() {
return false;
}
}
定义一个UserService
public class UserService {
public void test() {
System.out.println("111");
}
}
ProxyFactory factory = new ProxyFactory();
factory.setTarget(new UserService());
factory.addAdvisor(new MyAdvisor());
UserService userService = (UserService) factory.getProxy();
userService.test();
ProxyFactory的工作原理
ProxyFactory就是一个代理对象生产工厂,在生成代理对象之前需要对代理工厂进行配置。
ProxyFactory在生成代理对象之前需要决定到底是使用JDK动态代理还是CGLIB技术:
// config就是ProxyFactory对象
// optimize为true,或proxyTargetClass为true,或用户没有给ProxyFactory对象添加interface
if (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.");
}
// targetClass是接口,直接使用Jdk动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 使用Cglib
return new ObjenesisCglibAopProxy(config);
}
else {
// 使用Jdk动态代理
return new JdkDynamicAopProxy(config);
}
JdkDynamicAopProxy创建代理对象过程
获取生成代理对象所需要实现的接口集合
获取通过ProxyFactory.addInterface()所添加的接口,如果没有通过ProxyFactory.addInterface()添加接口,那么则看ProxyFactory.setTargetClass()所设置的targetClass是不是一个接口,把接口添加到结果集合中
同时把SpringProxy、Advised、DecoratingProxy这几个接口也添加到结果集合中去
确定好要代理的集合之后,就利用Proxy.newProxyInstance()生成一个代理对象
JdkDynamicAopProxy创建的代理对象执行过程
如果通过ProxyFactory.setExposeProxy()把exposeProxy设置为了true,那么则把代理对象设置到一个ThreadLocal(currentProxy)中去。
获取通过ProxyFactory所设置的target,如果设置的是targetClass,那么target将为null
根据当前所调用的方法对象寻找ProxyFactory中所添加的并匹配的Advisor,并且把Advisor封装为MethodInterceptor返回,得到MethodInterceptor链叫做chain
如果chain为空,则直接执行target对应的当前方法,如果target为null会报错
如果chain不为空,则会依次执行chain中的MethodInterceptor
如果当前MethodInterceptor是MethodBeforeAdviceInterceptor,那么则先执行Advisor中所advice的before()方法,然后执行下一个MethodInterceptor
如果当前MethodInterceptor是AfterReturningAdviceInterceptor,那么则先执行下一个MethodInterceptor,拿到返回值之后,再执行Advisor中所advice的afterReturning()方法
ObjenesisCglibAopProxy创建代理对象过程
创建Enhancer
设置Enhancer的superClass为通过ProxyFactory.setTarget()所设置的对象的类
设置Enhancer的interfaces为通过ProxyFactory.addInterface()所添加的接口,以及SpringProxy、Advised接口
设置Enhancer的Callbacks为DynamicAdvisedInterceptor
最后通过Enhancer创建一个代理对象
ObjenesisCglibAopProxy创建的代理对象执行过程
执行过程主要就看DynamicAdvisedInterceptor中的实现,执行逻辑和JdkDynamicAopProxy中是一样的。
使用“自动代理(autoproxy)”功能
"自动代理"表示,只需要在Spring中添加某个Bean,这个Bean是一个BeanPostProcessor,那么Spring在每创建一个Bean时,都会经过这个BeanPostProcessor的判断,去判断当前正在创建的这个Bean是不是需要进行AOP。
我们可以在项目中定义很多个Advisor,定义方式有两种:
通过实现PointcutAdvisor接口
通过@Aspect、@Pointcut、@Before等注解
在创建某个Bean时,会根据当前这个Bean的信息,比如对应的类,以及当前Bean中的方法信息,去和定义的所有Advisor进行匹配,如果匹配到了其中某些Advisor,那么就会把这些Advisor给找出来,并且添加到ProxyFactory中去,在利用ProxyFactory去生成代理对象
BeanNameAutoProxyCreator
@Bean
public BeanNameAutoProxyCreator creator(){
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
beanNameAutoProxyCreator.setBeanNames("userService");
beanNameAutoProxyCreator.setInterceptorNames("myAdvisor");
return beanNameAutoProxyCreator;
}
定义的这个bean,相当于一个“自动代理”器,有了这个Bean之后,可以自动的对setBeanNames中所对应的bean进行代理,代理逻辑为所设置的interceptorNames
DefaultAdvisorAutoProxyCreator
DefaultAdvisorAutoProxyCreator这个更加强大,只要添加了这个Bean,它就会自动识别所有的Advisor中的PointCut进行代理
AbstractAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessor接口,是一个BeanPostProcessor
在某个Bean实例化之前,查看该AbstractAutoProxyCreator中是不是设置了CustomTargetSource,如果设置了就查看当前Bean是不是需要创建一个TargetSource,如果需要就会创建一个TargetSource对象,然后进行AOP创建一个代理对象,并返回该代理对象
如果某个Bean出现了循环依赖,那么会利用getEarlyBeanReference()方法提前进行AOP
在某个Bean初始化之后,会调用wrapIfNecessary()方法进行AOP
在这个类中提供了一个抽象方法:getAdvicesAndAdvisorsForBean(),表示对于某个Bean匹配了哪些Advices和Advisors
AbstractAdvisorAutoProxyCreator继承了AbstractAutoProxyCreator,AbstractAdvisorAutoProxyCreator中实现了getAdvicesAndAdvisorsForBean()方法,实现逻辑为:
调用findEligibleAdvisors()
调用findCandidateAdvisors,得到所有Advisor类型的Bean
按当前正在进行Bean的生命周期的Bean进行过滤
@EnableAspectJAutoProxy
这个注解主要是添加了一个AnnotationAwareAspectJAutoProxyCreator类型的BeanDefinition
AspectJAwareAdvisorAutoProxyCreator继承了AbstractAdvisorAutoProxyCreator,重写了shouldSkip(Class<?> beanClass, String beanName)方法,表示某个bean需不需要进行AOP,在shouldSkip()方法中:
拿到所有的Advisor
遍历所有的Advisor,如果当前bean是AspectJPointcutAdvisor,那么则跳过
AnnotationAwareAspectJAutoProxyCreator继承了AspectJAwareAdvisorAutoProxyCreator,重写了findCandidateAdvisors()方法,它即可以找到Advisor类型的bean,也能把所有@Aspect注解标注的类扫描出来并生成Advisor
注解和源码对应关系
@Before对应的是AspectJMethodBeforeAdvice,直接实现MethodBeforeAdvice,在进行动态代理时会把AspectJMethodBeforeAdvice转成MethodBeforeAdviceInterceptor,也就转变成了MethodBeforeAdviceInterceptor
先执行advice对应的方法
再执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
@After对应的是AspectJAfterAdvice,直接实现了MethodInterceptor
先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
再执行advice对应的方法
@Around对应的是AspectJAroundAdvice,直接实现了MethodInterceptor
直接执行advice对应的方法
@AfterThrowing对应的是AspectJAfterThrowingAdvice,直接实现了MethodInterceptor
先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
如果上面抛了Throwable,那么则会执行advice对应的方法
@AfterReturning对应的是AspectJAfterReturningAdvice,实现了AfterReturningAdvice,在进行动态代理时会把AspectJAfterReturningAdvice转成AfterReturningAdviceInterceptor,也就转变成了MethodInterceptor
先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
执行上面的方法后得到最终的方法的返回值
再执行Advice对应的方法
Spring中AOP原理流程图
Introduction
Spring的文档上对Introduction这个概念和相关的注解@DeclareParents作了如下介绍:
Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare that advised objects implement a given interface, and to provide an implementation of that interface on behalf of those objects.
An introduction is made using the @DeclareParents annotation. This annotation is used to declare that matching types have a new parent (hence the name).
在这段介绍之后还给出了一个例子,对于初学者要理解这段话以及后面的例子还是蛮困难的,因此下面用一个简单的例子告诉大家什么是Introduction以及如何使用@DeclareParents注解。
对于Introduction这个词,个人认为理解成引入是最合适的,其目标是对于一个已有的类引入新的接口(有人可能会问:有什么用呢?简单的说,你可以把当前对象转型成另一个对象,那么很显然,你就可以调用另一个对象的方法了),看一个例子就全明白了。
假设已经有一个UserService类提供了保存User对象的服务,但是现在想增加对User进行验证的功能,只对通过验证的User提供保存服务,在不修改UserService类代码的前提下就可以通过Introduction来解决。
首先定义一个Verifier接口,里面定义了进行验证的方法validate(),如下所示:
package com.jackfrued.aop;
import com.jackfrued.models.User;
public interface Verifier {
public boolean validate(User user);
}
接下来给出该接口的一个实现类BasicVerifier,如下所示:
package com.jackfrued.aop;
import com.jackfrued.models.User;
public class BasicVerifier implements Verifier {
@Override
public boolean validate(User user) {
if(user.getUsername().equals("jack") && user.getPassword().equals("1234")) {
return true;
}
return false;
}
}
如何才能为UserService类增加验证User的功能呢,如下所示定义Aspect:
package com.jackfrued.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
@DeclareParents(value="com.tsinghuait.services.UserService",
defaultImpl=com.tsinghuait.aop.BasicVerifier.class)
public Verifier verifer;
}
接下来就可以将UserService对象转型为Verifier对象并对用户进行验证了,如下所示:
package com.jackfrued.main;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.jackfrued.aop.Verifier;
import com.jackfrued.models.User;
import com.jackfrued.services.Service;
class Test {
public static void main(String[] args) {
User user1 = new User();
user1.setUsername("abc");
user1.setPassword("def");
ApplicationContext factory = new ClassPathXmlApplicationContext("config.xml");
Service s = (Service) factory.getBean("service");
Verifier v = (Verifier) s;
if(v.validate(user1) {
System.out.println("验证成功");
s.serve(user1);
}
}
}
这样,上面代码中的user1是不会被服务的,当然是因为没有通过验证啦!
这样一说,是不是大概明白什么是Introduction了呢,其实@DeclareParents用起来也很简单吧!
【Spring源码】Spring中的AOP底层原理分析相关推荐
- spring源码深度解析---创建AOP代理之获取增强器
spring源码深度解析-创建AOP代理之获取增强器 在上一篇的博文中我们讲解了通过自定义配置完成了对AnnotationAwareAspectJAutoProxyCreator类型的自动注册,那么这 ...
- Spring源码——Spring MVC
前言 内容主要参考自<Spring源码深度解析>一书,算是读书笔记或是原书的补充.进入正文后可能会引来各种不适,毕竟阅读源码是件极其痛苦的事情. 本文主要涉及书中第十一章的部分,依照书中内 ...
- 4、Eureka 源码解析 之 Eureka Client 启动原理分析
在前面的一篇文章 3.Eureka 源码解析 之 Eureka Server 启动原理分析当中我们分析了一下 Eureka Server 的启动.在集群环境下 Eureka Server 相互之前需要 ...
- Spring源码学习(三)-- 底层架构核心概念解析
前面,我们大概了解了Spring中的一些概念和底层工作流程,这篇是后续看Spring源码所必备的,防止后续看源码的过程中,遇到不会的概念得单独跳出来学习. BeanDefinition BeanDe ...
- Spring源码解析 - AbstractBeanFactory 实现接口与父类分析
2019独角兽企业重金招聘Python工程师标准>>> 我们先来看类图吧: 除了BeanFactory这一支的接口,AbstractBeanFactory主要实现了AliasRegi ...
- c++ map 获取key列表_好未来Golang源码系列一:Map实现原理分析
分享老师:学而思网校 郭雨田 一.map的结构与设计原理 golang中map是一个kv对集合.底层使用hash table,用链表来解决冲突 ,出现冲突时,不是每一个key都申请一个结构通过链表串起 ...
- Spring中的Aop底层原理
Aop底层 aop底层使用的是动态代理,分为两种情况: 第一种:要增强的类实现了接口,使用JDK 动态代理 第二种: 没有接口情况,使用 CGLIB 动态代理 2.下面具体介绍下JDK动态代理 jdk ...
- spring源码分析之Aop
今天读spring源码,读到aop相关内容,在此记录一下,以便以后复习和查阅. 一.spring如何实现Aop 这里简单的说下原理,spring实例化bean要经历一套完整的生命周期,在这个过程中会对 ...
- 【spring源码】源码分析
[spring源码]源码分析 (一)mac版idea引入spring源码 (二)spring的学习流程 (三)spring源码分析 [1]refresh()方法概览(AbstractApplicati ...
最新文章
- 宏基因组分析技术研讨会-2019年最后一期
- Android Studio 3.0 安装注意点
- 源码安装zabbix3.2.7时PHP ldap Warning
- 软件项目管理的75条建议
- 【编译打包】haproxy 1.4.23
- LeetCode 1239. 串联字符串的最大长度(回溯/动态规划)
- eclipse中为了format的代码更加好看,少换行,可以设置java、xml、jsp的代码line width。
- 通过系统表获取SQL,Access,Oracle数据库的元数据信息
- .net Thrift 之旅 (二) TServer
- 众善之源 or 万恶之源?详析微服务的好与坏
- Android与iPhone的对比
- padstack editor制作过孔
- 爱了爱了,这样的文字动画让你爱不释手
- 十大排序算法(C++)(时间复杂度O(nlogn)篇:希尔排序、堆排序、快速排序、归并排序)
- 阿里系-淘宝接口抓取
- Python处理PDF神器:PyMuPDF的安装与使用
- hong书网页版x-s、x-t
- android------之高德地图实现定位和3D地图显示
- peek在c语言中的作用,聚醚醚酮(PEEK)材料用于颅骨修补优点有哪些
- Localization of Classified Objects in SLAM using NonparametricStatistics and Clustering(2018,IROS)
热门文章
- 【 笔记 】定位算法性能分析
- Java程序员的日常—— 《编程思想》关于类的使用常识
- vtigercrm学习(二)
- 剑走偏锋——用css制作一个三角形箭头
- linux日志显示too many open files解决
- Silverlight Toolkit DataGrid - 单元格内容对齐样式
- 嵌入式linux webkit,嵌入式平台arm linux 之qt 4.5交叉编译到webkit时出错解决方法
- 无线通信信号自由空间损耗
- 关于示波器地线的问题!
- [译] Lenses:可组合函数式编程的 Getter 和 Setter(第十九部分)