手写Spring AOP,快来瞧一瞧看一看撒!
上面两篇内容说到了手写IOC和DI,本篇内容就是手写AOP,由于内容中使用到了上面两篇内容写的代码,所以此处贴下链接:
手写Spring IOC容器:点击进入
手写Spring DI依赖注入:点击进入
AOP分析
AOP是什么
Aspect Oriented Programming 面向切面编程,在不改变类的代码的情况下,对类的方法进行功能增强
那么如果要实现一个AOP,需要做的事情就是要向使用的用户提供AOP功能,能够通过AOP技术实现对类的方法进行功能增强
AOP中的元素
- Advice 通知,即增强的功能
- Join points 连接点,可以选择的方法点,即有哪些可以增强的方法供选择
- Pointcut 切入点,选择切入的方法点,即对哪些方法进行增强
- Aspact 切面,选择的多个方法点 + 增强的功能,即Advice和Pointcut的组合
- Introduction 引入,添加新的方法、属性到已经存在的类中,叫做引入
- Weaving 织入,即不改变原来的代码,加入增强的功能
手写AOP的话,上面几个元素有的是需要用户提供的:Advice、Pointcut、Aspact,用户需要提供增强的功能和切入点、切面信息;AOP框架需要提供Join points、Weaving
AOP提供的功能
- 需要功能的增强,即Advice通知
- 对类的方法进行增强,那需要可以选择的要增强的方法点,即Pointcut切入点
- 需要在不改变原类代码的情况下,进行功能的增强,即Weaving织入
图解AOP
假设我们现在玩一局王者荣耀,选择了一个英雄鲁班,这个鲁班需要参与整个游戏直到结束,不管是否挂机;刚刚开始的时候,没有任何装备,假设鲁班身上的各个部位是Join points,即这些部位需要装备去增强它,那每一个装备就是Advice增强的功能,比如增加攻速、生命等,现在买了一个鞋子,那么就是选择在速度上进行增强,即穿在脚上,那么就是Pointcut,那么这个鞋子加上脚就是Aspact切面,上面说的例子可能不够好,见谅啊!
特点分析
- Advice
实现一个Advice,就需要知道Advice有哪些特点,从上面可以知道Advice是用户来提供的,所以有很多不可控的因素
- 用户性:由用户来提供增强的功能,即增强功能的代码是用户来进行编写的
- 多变性:既然用户来提供,那么对于不同的需求,增强的逻辑都是不一样的
- 可选时机:用户可以选择在方法的前面、后面、异常时进行功能增强
- 多重性:同一个切入点,即同一个方法,可以有多重的增强,不止增强一次
- Pointcut
切入点,通过上面知道它也是用户提供的,所以它的特点和Advice基本上差不多
- 用户性:由用户来指定,对哪些方法进行功能增强
- 多变性:用户可以灵活的来指定
- 多点性:用户可以选择在多个方法点上进行功能增强
- Weaving
织入,这部分代码,是需要框架提供的,即是需要我们自己去实现的逻辑,通过这个可以把增强的功能加入到用户指定的方法上面
- 无侵入性:不改变原来类的代码,去实现功能的增强
- 需要我们在AOP框架中自己实现逻辑
通过上面的内容,可以知道,需要做的事情就是Advice、Pointcut、Weaving三个部分,Join points为什么不需要去实现呢,这部分内容在编写代码的过程中就可以知道了
Advice实现
Advice是由用户来实现的,这部分逻辑需要用户写好然后我们实现AOP的时候来进行使用;我们需要认识用户的东西,用户需要使用我们写的框架,而且还需要隔绝用户的多变性
那么需要做的事情是啥呢,可以定义一个标准的接口,用户通过实现接口来提供不同的增强逻辑,这就是应对变化的方式,面向接口编程
定义Advice接口
定义的Advice是一个顶级接口,不需要写任何的方法,然后根据前置、后置、环绕、异常增强,来去实现Advice接口,那么这些增强需要的参数是一样的吗,请往下看
Advice接口:
/**
* @className: Advice
* @description: 通知的标准接口
* @author TR
*/
public interface Advice {
}
首先,我们知道,增强是对方法进行增强,那么使用Advice的时候,需要给的就是方法的一些信息
- 前置增强
在方法执行前进行增强,那么可以知道前置增强是不需要返回值的,需要的参数如下:
- 方法自身 Method
- 方法的参数 Object[]
- 方法所在的类(对象) Object
- 后置增强
在方法执行后进行增强,那需要的参数是不是需要增加一个啊,即需要方法的返回值,因为我如果需要对返回值做一下处理,就需要用到它,而且后置增强也是不需要返回值的,需要的参数如下:
- 方法自身 Method
- 方法的参数 Object[]
- 方法所在的类(对象) Object
- 方法的返回值 Object
- 环绕增强
包裹方法进行增强,它是需要包裹整个方法,即方法由它来执行,那么环绕增强是需要返回值的,这个返回值是需要增加方法的返回值,需要的参数如下:
- 方法自身 Method
- 方法的参数 Object[]
- 方法所在的类(对象) Object
- 异常增强
捕获方法执行时的异常信息,然后进行增强,而且它也是需要包裹方法进行增强的,即它可以在环绕增强中来实现
通过上面知道,需要定义三个方法:前置增强的方法、后置增强的方法、环绕和异常增强的方法,那这三个方法是定义在一个接口里面,还是分三个接口呢,根据单一职责原则,还是分三个接口来实现比较好,而且还可以根据不同的接口类型来区分是哪个Advice
定义前置、后置、环绕和异常增强接口
定义前置增强接口MethodBeforeAdvice:
/**
* @className: MethodBeforeAdvice
* @description: 前置增强接口
* @author TR
*/
public interface MethodBeforeAdvice extends Advice {
/**
* 前置增强方法
* @param method: 将要被执行的方法
* @param target: 执行方法的目标对象
* @param args: 执行方法的参数
* @return: void
**/
void before(Method method, Object target, Object[] args);
}
定义后置增强接口AfterReturningAdvice:
/**
* @className: AfterReturningAdvice
* @description: 后置增强接口
* @author TR
*/
public interface AfterReturningAdvice extends Advice {
/**
* 后置增强方法
* @param method: 将要被执行的方法
* @param target: 执行方法的目标对象
* @param args: 执行方法的参数
* @param returnValue: 执行方法的返回值
* @return: void
**/
void after(Method method, Object target, Object[] args, Object returnValue);
}
定义环绕、异常增强接口MethodInterceptor:
/**
* @className: MethodInterceptor
* @description: 环绕、异常增强接口
* @author TR
*/
public interface MethodInterceptor extends Advice {
/**
* 环绕、异常增强方法,在方法实现中需要调用目标方法
* @param method: 将要被执行的方法
* @param target: 执行方法的目标对象
* @param args: 执行方法的参数
* @return: java.lang.Object 执行方法的返回值
* @throws Throwable
**/
Object invoke(Method method, Object target, Object[] args) throws Throwable;
}
类图如下:
Pointcut实现
Pointcut的特点:
- 用户性:由用户来指定,对哪些方法进行功能增强
- 多变性:用户可以灵活的来指定
- 多点性:用户可以选择在多个方法点上进行功能增强
我们需要给用户提供一个东西,让用户可以灵活的来指定方法点,而且我们获取到的时候又能够知道,用户对哪些方法点进行了指定
指定对哪些方法进行增强,指定的信息是什么,其实就是一个或者多个方法,而且如果有重载存在呢,所以这个指定的东西,就是一个完整的方法签名
那该怎么做到灵活性、多点性呢,这个指定的信息是可以描述一类方法的,比如:
- 某个包下某个类的某个方法
- 某个包下某个类的所有方法
- 某个包下所有类中以get开头的方法
- 某个包下所有类中以get开头以sevice结尾的方法
- 某个包下及其子包下所有类中以get开头以sevice结尾的方法
那么可以定义一个表达式,来去描述上面的信息,这个描述的信息是包名.类名.方法名(参数类型)
每一部分是如何要求的呢?
- 包名:需要有父子特点,可以模糊匹配
- 类名:可以模糊匹配
- 方法名:可以模糊匹配
- 参数类型:可以有多个
定义的表达式是需要来判断:是否需要对某个类的某个方法进行增强,那么需要去匹配类和匹配方法
匹配的表达式:
- 正则表达式
- AspactJ表达式
AspactJ表达式学习
/**
* @className: AspectJTest
* @description: AspectJ测试类
*/
public class AspectJTest {
public static void main(String[] args) throws NoSuchMethodException {
//获得切点解析器
PointcutParser pp = PointcutParser
.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
//切点解析器根据规则,解析出一个类型匹配器
TypePatternMatcher tp = pp.parseTypePattern("di.*");
//根据表达式生成一个切点表达式
PointcutExpression pe = pp.parsePointcutExpression("execution(* demo.beans.BeanFactory.get*(..))");
//匹配MagicGril的getName方法
Class<?> cl = MagicGril.class;
Method method = cl.getMethod("getName", null);
//匹配方法执行
ShadowMatch sm = pe.matchesMethodExecution(method);
System.out.println("是否匹配到方法:" + sm.alwaysMatches());
System.out.println(cl.getName() + ",是否匹配表达式:" + pe.couldMatchJoinPointsInType(cl));
System.out.println(DefaultBeanFactory.class + ",是否匹配表达式:" + pe.couldMatchJoinPointsInType(DefaultBeanFactory.class));
System.out.println(cl.getName() +"类下的所有方法:");
for (Method method1 : cl.getMethods()) {
System.out.println(method1.getName());
}
}
}
输出结果:
通过上面的描述,可以知道,切点该怎么去设计了
切点需要有的属性就是切点表达式,需要提供的功能就是匹配类和匹配方法
而且这里也可以像Advice一样定义一个顶级接口,因为如果以后有其他的表达式更加好用的话,需要扩展,那么只需要继承定义的这个顶级接口就行了,不管它内部如何实现,都要去实现我们定义的匹配类和匹配方法的行为
定义PointCut接口
/**
* @className: PointCut
* @description: 切点匹配接口
* @author: TR
*/
public interface PointCut {
/**
* 匹配类
* @author: jinpeng.sun
* @date: 2021/4/19 13:46
* @param targetClass: 匹配的目标类
* @return: boolean
**/
boolean matchClass(Class<?> targetClass);
/**
* 匹配方法
* @author: jinpeng.sun
* @date: 2021/4/19 13:46
* @param method: 匹配的目标方法
* @param targetClass: 匹配的目标类
* @return: boolean
**/
boolean matchMethod(Method method, Class<?> targetClass);
}
定义正则表达式的实现类:RegExpressionPointcut
此处不实现,主要使用AspactJ的方式
/**
* @className: RegExpressionPointcut
* @description: 正则表达式实现类
* @author: TR
*/
public class RegExpressionPointcut implements PointCut {
@Override
public boolean matchClass(Class<?> targetClass) {
return false;
}
@Override
public boolean matchMethod(Method method, Class<?> targetClass) {
return false;
}
}
定义AspectJ切点表达式的实现类:AspectJExpressionPointcut
/**
* @className: AspectJExpressionPointcut
* @description: AspectJ切点表达式实现类
* @author: TR
*/
public class AspectJExpressionPointcut implements PointCut {
/** 获得切点解析器 */
PointcutParser pp = PointcutParser
.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
/** 切点表达式的字符串形式 */
private String expression;
/** AspectJ中的切点表达式 */
private PointcutExpression pe;
public AspectJExpressionPointcut(String expression) {
super();
this.expression = expression;
pe = pp.parsePointcutExpression(expression);
}
@Override
public boolean matchClass(Class<?> targetClass) {
return pe.couldMatchJoinPointsInType(targetClass);
}
@Override
public boolean matchMethod(Method method, Class<?> targetClass) {
ShadowMatch sm = pe.matchesMethodExecution(method);
return sm.alwaysMatches();
}
public String getExpression() {
return expression;
}
}
实现步骤:
- maven引入Aspectj的jar
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
- 获得切点解析器
PointcutParser pp = PointcutParser
.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
- 解析切点表达式,获得PointcutExpression
PointcutExpression pe = pp.parsePointcutExpression(
"execution(* edu.dongnao.courseware.beans.BeanFactory.get*(..))");
- 使用PointcutExpression匹配类,不可靠
pe.couldMatchJoinPointsInType(targetClass)
- 使用PointcutExpression匹配方法,可靠
ShadowMatch sm = pe.matchesMethodExecution(method);
return sm.alwaysMatches();
类图如下:
Aspact实现
上面实现了Advice和Pointcut,那么用户该如何使用呢?
Advice是用户提供的功能增强实现,是需要继承接口的,那么可以把Advice配置成一个bean,Pointcut是切点表达式,是用来匹配类和方法的,是不需要配置成bean的,只需要提供一个字符串形式的表达式就行了,那么adviceBeanName+expression就组成了切面
下面通过外观模式来实现切面,把Advice和Pointcut组合起来
定义Advisor接口
/**
* @className: Advisor
* @description: 构建切面的接口,组合advice和pointcut
* @author: TR
*/
public interface Advisor {
/**
* 获取通知bean的名称
* @return: java.lang.String
**/
String getAdviceBeanName();
/**
* 获取切点表达式
* @return: java.lang.String
**/
String getExpression();
}
定义PointcutAdvisor接口
/**
* @className: PointcutAdvisor
* @description: 切点通知者,继承自Advisor,扩展了pointcut
* @author: TR
*/
public interface PointcutAdvisor extends Advisor {
/**
* 获取切点
* @return: PointCut
**/
PointCut getPointCut();
}
定义AspectJPointcutAdvisor实现类
/**
* @className: AspectJPointcutAdvisor
* @description: AspectJ切点表达式的通知者实现类
* @author: TR
*/
public class AspectJPointcutAdvisor implements PointcutAdvisor {
/** 通知bean的名称 */
private String adviceBeanName;
/** 表达式 */
private String expression;
/** 切点 */
private PointCut pointCut;
public AspectJPointcutAdvisor(String adviceBeanName, String expression) {
this.adviceBeanName = adviceBeanName;
this.expression = expression;
this.pointCut = new AspectJExpressionPointcut(expression);
}
@Override
public PointCut getPointCut() {
return pointCut;
}
@Override
public String getAdviceBeanName() {
return adviceBeanName;
}
@Override
public String getExpression() {
return expression;
}
}
下面的类不予实现,主要使用AspactJ的形式
/**
* @className: RegPointcutAdvisor
* @description: 正则切点表达式的通知者实现类
* @author: TR
*/
public class RegPointcutAdvisor implements PointcutAdvisor {
/** 通知bean的名称 */
private String adviceBeanName;
/** 表达式 */
private String expression;
/** 切点 */
private PointCut pointCut;
public RegPointcutAdvisor(String adviceBeanName, String expression) {
this.adviceBeanName = adviceBeanName;
this.expression = expression;
this.pointCut = new RegExpressionPointcut();
}
@Override
public PointCut getPointCut() {
return null;
}
@Override
public String getAdviceBeanName() {
return null;
}
@Override
public String getExpression() {
return null;
}
}
类图如下:
从上面的图中看到了,如果再扩展其他的切点表达式,那么在实现类中,可以看到,有很多的重复代码,那么是不是还可以优化一下呢,可以在实现类的上层再加一个抽象类,来继承PointcutAdvisor,然后主要的切点实现继承这个抽象类就行了
Weaving实现
要完成的事情
将用户提供的增强功能加入到指定的方法上,需要我们来实现
什么时候做织入
在创建Bean实例的时候,在Bean初始化完成后,再进行织入
怎么知道Bean要进行增强
需要遍历Bean类及其所有的方法,然后依次的去匹配用户指定的切面,如果存在匹配的切面,就是需要增强了
怎么织入呢,就是需要用到代理了!
用户需要去注册切面,我们还需要实现判断匹配、织入的的逻辑,这部分代码改如何写,需要写到哪里呢
现在要做的事情,就是在Bean创建的过程中加一项处理,后面可能会在Bean的创建过程中加入更多的处理,如果这部分代码都写在BeanFactory中,那么这个类是不是就会有特别多的代码,而且后面不方便扩展
看下Bean的创建过程:
上面图中,每个箭头都是加的扩展点,后面可能存在的是,需要在这些点上加入更多的处理逻辑,那么就需要设计一种方式,在不改变BeanFactory的情况下,能灵活的扩展,那么可以使用观察者模式,有几个扩展点,就是有几个主题,六个观察者
定义观察者接口BeanPostProcessor
/**
* @className: BeanPostProcessor
* @description: 后置处理器,Bean实例化完成后及依赖注入完成后触发
* @author: TR
*/
public interface BeanPostProcessor {
/**
* bean初始化前的处理
* @author: TR
* @param bean: bean实例
* @param beanName: bean名称
* @return: java.lang.Object
**/
default Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
return bean;
}
/**
* bean初始化后的处理
* @author: TR
* @param bean: bean实例
* @param beanName: bean名称
* @return: java.lang.Object
**/
default Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
return bean;
}
}
定义通知接口Aware
/**
* @className: Aware
* @description: 构建通知接口
* @date: 2021/4/19 16:38
* @author: jinpeng.sun
*/
public interface Aware {
}
/**
* @className: BeanFactoryAware
* @description: bean工厂构建通知接口
* @author: TR
*/
public interface BeanFactoryAware extends Aware {
/**
* 接口实现者获得bean工厂方法
* @author: TR
* @param bf:
* @return: void
**/
void setBeanFactory(BeanFactory bf);
}
上面的接口主要用来获得Bean工厂信息
定义通知者注册接口AdvisorRegistry
这个接口主要用来把Advisor注册到增强功能的实现类里面
/**
* @className: AdvisorRegistry
* @description: 通知者注册接口
* @author: TR
*/
public interface AdvisorRegistry {
/**
* 注册通知者
* @author: TR
* @param advisor:
* @return: void
**/
public void registerAdvisor(Advisor advisor);
/**
* 获得通知者列表
* @author: TR
* @date: 2021/4/19 16:45
*
* @return: java.util.List<demo.aop.advisor.Advisor>
**/
public List<Advisor> getAdvisors();
}
定义增强处理的观察者实现类AdvisorAutoProxyCreator
/**
* @className: AdvisorAutoProxyCreator
* @description: 功能增强的实现类,用户和框架交互的核心类
* 用户通过Advisor提供切面,向DefaultBeanFactory注入该实现
* 框架内部:DefaultBeanFactory注入ioc容器
* DefaultBeanFactory调用BeanPostProcessor接口相关方法,进行功能增强
* @author: TR
*/
public class AdvisorAutoProxyCreator implements BeanPostProcessor, BeanFactoryAware, AdvisorRegistry {
/** 通知者列表 */
private List<Advisor> advisors;
/** 当前的bean */
private BeanFactory beanFactory;
public AdvisorAutoProxyCreator() {
this.advisors = new ArrayList<>();
}
@Override
public void registerAdvisor(Advisor advisor) {
this.advisors.add(advisor);
}
@Override
public List<Advisor> getAdvisors() {
return this.advisors;
}
@Override
public void setBeanFactory(BeanFactory bf) {
this.beanFactory = bf;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
return null;
}
}
注册BeanPostProcessor
BeanFactory接口中增加注册BeanPostProcessor的方法
/** 注册BeanPostProcessor */
void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor);
DefaultBeanFactory实现类中增加如下方法:
private List<BeanPostProcessor> postProcessors = Collections.synchronizedList(new ArrayList<>());
@Override
public void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
postProcessors.add(beanPostProcessor);
if (beanPostProcessor instanceof BeanFactoryAware) {
((BeanFactoryAware) beanPostProcessor).setBeanFactory(this);
}
}
getBean方法中增加bean初始化前后的处理:
@Override
public Object getBean(String beanName) throws Exception {
//获取bean定义
BeanDefinition bd = beanDefinitionMap.get(beanName);
Object bean = doGetBean(beanName);
//属性注入
setPropertyDIValues(bd, bean);
//bean初始化前的处理
bean = this.applyPostProcessBeforeInitialization(bean, beanName);
//bean的生命周期
if (StringUtils.isNotBlank(bd.getInitMethodName())) {
doInitMethod(bean,bd);
}
//bean初始化后的处理
bean = this.applyPostProcessAfterInitialization(bean, beanName);
return bean;
}
/**
* bean初始化后的处理
* @author: TR
* @param bean:
* @param beanName:
* @return: java.lang.Object
**/
private Object applyPostProcessAfterInitialization(Object bean, String beanName) throws Exception {
for (BeanPostProcessor bp : this.postProcessors) {
bean = bp.postProcessBeforeInitialization(bean, beanName);
}
return bean;
}
/**
* bean初始化前的处理
* @author: TR
* @param bean:
* @param beanName:
* @return: java.lang.Object
**/
private Object applyPostProcessBeforeInitialization(Object bean, String beanName) throws Exception {
for (BeanPostProcessor bp : this.postProcessors) {
bean = bp.postProcessAfterInitialization(bean, beanName);
}
return bean;
}
下面需要实现的就是判断bean是否需要增强了,那么需要获取到bean的所有方法,然后根据注册进来的advisors去遍历它,然后得到切点去匹配类和方法
修改AdvisorAutoProxyCreator类中的postProcessAfterInitialization方法:
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
//判断是否需要进行切面增强,及获得增强的通知实现
List<Advisor> advisors = getMatchedAdvisors(bean, beanName);
//如果需要增强,返回增强后的对象
if (CollectionUtils.isNotEmpty(advisors)) {
//使用代理模式进行功能增强
}
return bean;
}
下面是获取匹配的方法代码:
/**
* 获取匹配的方法
* @author: TR
* @param bean: bean实例
* @param beanName: bean名称
* @return: void
**/
private List<Advisor> getMatchedAdvisors(Object bean, String beanName) {
if (CollectionUtils.isEmpty(advisors)) {
return null;
}
//得到类
Class<?> beanClass = bean.getClass();
//得到所有的方法
List<Method> allMethods = getAllMethodForClass(beanClass);
//存放匹配的Advisor
List<Advisor> advisors = new ArrayList<>();
for (Advisor ad : this.advisors) {
if (ad instanceof PointcutAdvisor) {
//判断是否匹配
if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods)) {
advisors.add(ad);
}
}
}
return advisors;
}
获取所有的方法,使用的是Spring framework提供的工具类,来找到类下面所有的方法:
/**
* 获取所有的方法
* @author: TR
* @param beanClass:
* @return: void
**/
private List<Method> getAllMethodForClass(Class<?> beanClass) {
List<Method> allMethods = new LinkedList<>();
Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass));
classes.add(beanClass);
for (Class<?> cc : classes) {
//根据工具类获取所有的方法
Method[] methods = ReflectionUtils.getAllDeclaredMethods(cc);
for (Method m : methods) {
allMethods.add(m);
}
}
return allMethods;
}
下面是判断是否有匹配切点规则的方法,使用PointCut中定义的方法来实现:
/**
* 判断是否有匹配切点规则的方法
* @author: TR
* @param ad: 切面
* @param beanClass: 指定的类
* @param allMethods: 指定的类下面所有的方法
* @return: void
**/
private boolean isPointcutMatchBean(PointcutAdvisor ad, Class<?> beanClass, List<Method> allMethods) {
PointCut p = ad.getPointCut();
//匹配类
if (!p.matchClass(beanClass)) {
return false;
}
for (Method m : allMethods) {
//匹配方法
if (p.matchMethod(m, beanClass)) {
return true;
}
}
return false;
}
判断是否增强之后,就是需要进行代理增强了,那么这里的实现逻辑又是啥样的呢
通过上图可以看到,需要判断代理的方式,即使用JDK动态代理还是CGLIB动态代码,那么这里也可以抽象出一个接口。然后不同的代理方式分别去实现
定义AopProxy代理接口
/**
* @className: AopProxy
* @description: AOP代理接口,用来创建和获取代理对象
* @author: TR
*/
public interface AopProxy {
Object getProxy();
Object getProxy(ClassLoader classLoader);
}
JDK动态代理实现
我们知道JDK动态代理是对接口进行的,那么在实现中需要哪些数据呢?
- 要实现的接口
- 目标对象
- 匹配的Advisors
- BeanFactory
需要的参数:
- 需要实现的接口
- InvocationHandler
/**
* @className: JdkDynamicAopProxy
* @description: JDK动态代理实现
* @author: TR
*/
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
private static final Logger logger = LoggerFactory.getLogger(JdkDynamicAopProxy.class);
//bean名称
private String beanName;
//bean对象,需要被代理的对象
private Object target;
//通知列表,需要被增强的功能
private List<Advisor> advisors;
//当前的bean
private BeanFactory beanFactory;
public JdkDynamicAopProxy(String beanName, Object target, List<Advisor> advisors, BeanFactory beanFactory) {
this.beanName = beanName;
this.target = target;
this.advisors = advisors;
this.beanFactory = beanFactory;
}
@Override
public Object getProxy() {
return this.getProxy(target.getClass().getClassLoader());
}
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("为" + target + "创建JDK代理。");
}
return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this);
}
/**
* InvocationHandler接口的实现
* 用来进行代理增强后返回实际的结果
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return AopProxyUtils.applyAdvices(target, method, args, advisors, proxy, beanFactory);
}
}
CGLIB动态代理实现
CGLIB动态代理可以对接口和类进行,那么它需要下面的数据:
- 要继承的类
- 要实现的接口
- 目标对象
- 匹配的Advisors
- BeanFactory
- 构造参数类
- 构造参数
需要的参数:
- 继承的类
- 需要实现的接口
- Callback
- 构造参数类型
- 构造参数
构造参数类型和构造参数在创建实例的时候会有的
/**
* @className: CglibDynamicAopProxy
* @description: Cglib动态代理
* @author: TR
*/
public class CglibDynamicAopProxy implements AopProxy, MethodInterceptor {
private static final Logger logger = LoggerFactory.getLogger(CglibDynamicAopProxy.class);
private static Enhancer enhancer = new Enhancer();
//bean名称
private String beanName;
//bean对象,需要被代理的对象
private Object target;
//通知列表,需要被增强的功能
private List<Advisor> advisors;
//当前的bean
private BeanFactory beanFactory;
public CglibDynamicAopProxy(String beanName, Object target, List<Advisor> advisors, BeanFactory beanFactory) {
this.beanName = beanName;
this.target = target;
this.advisors = advisors;
this.beanFactory = beanFactory;
}
@Override
public Object getProxy() {
return this.getProxy(target.getClass().getClassLoader());
}
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("为" + target + "创建cglib代理。");
}
Class<?> superClass = this.target.getClass();
enhancer.setSuperclass(superClass);
enhancer.setInterfaces(this.getClass().getInterfaces());
enhancer.setCallback(this);
Constructor<?> constructor = null;
try {
constructor = superClass.getConstructor(new Class<?>[] {});
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
if (constructor != null) {
return enhancer.create();
} else {
BeanDefinition bd = ((DefaultBeanFactory)beanFactory).getBeanDefinition(beanName);
return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues());
}
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return AopProxyUtils.applyAdvices(target, method, objects, advisors, o, beanFactory);
}
}
AopProxyUtils它是用来干什么的呢,从上面可以看到,这部分的处理,主要是用来实现增强的,都是使用Advice来增强的,所以增强的逻辑是一样的
/**
* @className: AopProxyUtils
* @description: aop代理工具类
* @author: TR
*/
public class AopProxyUtils {
/**
* 对方法应用advice增强,获得最终返回结果
* @author: TR
* @param target: 需要被增强的对象
* @param method: 需要被增强的方法
* @param args: 增强方法的参数
* @param advisors: 匹配到的切面
* @param proxy: bean对象功能增强后的代理对象
* @param beanFactory: bean工厂
* @return: java.lang.Object
**/
public static Object applyAdvices(Object target, Method method, Object[] args, List<Advisor> advisors,
Object proxy, BeanFactory beanFactory) throws Throwable {
//获取对当前方法进行增强的advices
List<Object> advices = AopProxyUtils.getShouldApplyAdvices(target.getClass(), method, advisors, beanFactory);
if (CollectionUtils.isEmpty(advices)) {
return method.invoke(target, args);
} else {
//使用责任链进行增强
AopAdviceChainInvocation ac = new AopAdviceChainInvocation(proxy, target, method, args, advices);
return ac.invoke();
}
}
/**
* 获得与方法匹配的Advice切面列表
* @author: TR
* @param aClass:
* @param method:
* @param advisors:
* @param beanFactory:
* @return: java.util.List<demo.aop.advice.Advice>
**/
private static List<Object> getShouldApplyAdvices(Class<?> aClass, Method method,
List<Advisor> advisors, BeanFactory beanFactory) throws Exception {
if (CollectionUtils.isEmpty(advisors)) {
return null;
}
List<Object> advices = new ArrayList<>();
for (Advisor advisor : advisors) {
if (advisor instanceof PointcutAdvisor) {
if (((PointcutAdvisor)advisor).getPointCut().matchMethod(method, aClass)) {
advices.add(beanFactory.getBean(advisor.getAdviceBeanName()));
}
}
}
return advices;
}
}
如何来传递创建bean实例时获得的数据到初始化后的Aop中,就是要在BeanDefinition中使用ThreadLocal持有参数值
BeanDefinition增加如下方法:
/** 获取构造参数值 */
public Object[] getConstructorArgumentRealValues();
/** 设置构造参数值 */
public void setConstructorArgumentRealValues(Object[] values);
GeneralBeanDefinition类中相应的实现:
private ThreadLocal<Object[]> realConstructorArgumentValues = new ThreadLocal<>();
@Override
public Object[] getConstructorArgumentRealValues() {
return realConstructorArgumentValues.get();
}
@Override
public void setConstructorArgumentRealValues(Object[] values) {
this.realConstructorArgumentValues.set(values);
}
责任链AopAdviceChainInvocation类
/**
* @className: AopAdviceChainInvocation
* @description: AOP责任链调用类
* @author: TR
*/
public class AopAdviceChainInvocation {
/** AOP责任链执行的方法 */
private static Method invokeMethod;
static {
try {
invokeMethod = AopAdviceChainInvocation.class.getMethod("invoke", null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
//代理类对象
private Object proxy;
//目标类对象
private Object target;
//调用执行的对象方法
private Method method;
//执行方法的参数
private Object[] args;
//方法被增强的功能:通知列表
private List<Object> advices;
public AopAdviceChainInvocation(Object proxy, Object target, Method method, Object[] args, List<Object> advices) {
this.proxy = proxy;
this.target = target;
this.method = method;
this.args = args;
this.advices = advices;
}
//责任链执行记录的索引号
private int i = 0;
public Object invoke() throws Throwable {
if (i < this.advices.size()) {
Object advice = this.advices.get(i++);
if (advice instanceof MethodBeforeAdvice) {
//执行前增强
((MethodBeforeAdvice)advice).before(method, target, args);
} else if (advice instanceof MethodInterceptor) {
//执行环绕增强和异常增强,这里给的method和对象是invoke方法和链对象
((MethodInterceptor)advice).invoke(invokeMethod,this, null);
} else if (advice instanceof AfterReturningAdvice) {
//后置增强,先获得结果,在增强
Object returnValue = this.invoke();
((AfterReturningAdvice)advice).after(method, target, args, returnValue);
return returnValue;
}
//回调
return this.invoke();
} else {
return method.invoke(target, args);
}
}
}
定义AopProxyFactory接口
代理的方式实现完了之后,就需要使用它了,这里使用工厂模式实现:
/**
* @className: AopProxyFactory
* @description: AOP代理的工厂接口
* @author: TR
*/
public interface AopProxyFactory {
/**
* 根据参数获取AOP代理接口的实现
* @param bean: 实例
* @param beanName: bean名称
* @param advisors: advisors列表
* @param beanFactory: bean工厂
* @return: AopProxyFactory
**/
AopProxy createAopProxy(Object bean, String beanName, List<Advisor> advisors, BeanFactory beanFactory);
/**
* 获取默认的AopProxyFactory
* @return: AopProxyFactory
**/
static AopProxyFactory getDefaultAopProxyFactory() {
return new DefaultAopProxyFactory();
}
}
实现类:
/**
* @className: DefaultAopProxyFactory
* @description: 代理工厂实现类
* @author: TR
*/
public class DefaultAopProxyFactory implements AopProxyFactory {
@Override
public AopProxy createAopProxy(Object bean, String beanName, List<Advisor> advisors, BeanFactory beanFactory) {
//是用JDK动态代理还是CGLIB动态代理
if (shouldUseJDKDynamicProxy(bean, beanName)) {
return new JdkDynamicAopProxy(beanName, bean, advisors, beanFactory);
} else {
return new CglibDynamicAopProxy(beanName, bean, advisors, beanFactory);
}
}
private boolean shouldUseJDKDynamicProxy(Object bean, String beanName) {
return false;
}
}
下面需要修改下AdvisorAutoProxyCreator类中的postProcessAfterInitialization方法:
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
//判断是否需要进行切面增强,及获得增强的通知实现
List<Advisor> advisors = getMatchedAdvisors(bean, beanName);
//如果需要增强,返回增强后的对象
if (CollectionUtils.isNotEmpty(advisors)) {
//使用代理模式进行功能增强
bean = this.createProxy(bean, beanName, advisors);
}
return bean;
}
private Object createProxy(Object bean, String beanName, List<Advisor> advisors) {
return AopProxyFactory.getDefaultAopProxyFactory()
.createAopProxy(bean, beanName, advisors, beanFactory)
.getProxy();
}
DefaultBeanFactory类中缓存构造函数的方式需要改变一下
determineConstructor方法中缓存的代码注释掉:
if (ct != null) {
// //对于原型bean,缓存起来
// if (bd.isProtoType()) {
// bd.setConstructor(ct);
// }
return ct;
} else {
throw new Exception("找不到对应的构造方法:" + bd);
}
在createBeanByConstructor中增加代码
/** 通过构造函数构建bean */
private Object createBeanByConstructor(BeanDefinition bd) throws Exception {
Object object = null;
if (CollectionUtils.isEmpty(bd.getConstructorArgumentValues())) {
//获得的构造参数值是空的,就不传参
object = bd.getBeanClass().newInstance();
} else {
//获得的参数值是空的,就不传参
Object[] args = getConstructorArgumentValues(bd);
if (args == null) {
object = bd.getBeanClass().newInstance();
} else {
// 根据参数匹配决定构造方法,然后进行实例化调用
bd.setConstructorArgumentRealValues(args);
Constructor<?> ct = this.determineConstructor(bd, args);
// 缓存构造函数由determineConstructor 中移到了这里,无论原型否都缓存,因为后面AOP需要用
bd.setConstructor(ct);
return ct.newInstance(args);
}
}
return object;
}
测试一下
前置增强实现:
public class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object target, Object[] args) {
System.out.println(this + " 对 " + target + " 进行了前置增强!");
}
}
后置增强实现:
public class MyAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void after(Method method, Object target, Object[] args, Object returnValue) {
System.out.println(this + " 对 " + target + " 做了后置增强,得到的返回值=" + returnValue);
}
}
环绕增强实现:
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(Method method, Object target, Object[] args) throws Throwable {
System.out.println(this + "对 " + target + "进行了环绕--前增强");
Object ret = method.invoke(target, args);
System.out.println(this + "对 " + target + "进行了环绕 --后增强。方法的返回值为:" + ret);
return ret;
}
}
测试类:
public class AopTest {
static PreBuildBeanFactory bf = new PreBuildBeanFactory();
@Test
public void testCirculationDI() throws Throwable {
GeneralBeanDefinition bd = new GeneralBeanDefinition();
bd.setBeanClass(Lad.class);
List<Object> args = new ArrayList<>();
args.add("孙悟空");
args.add(new BeanReference("baigujing"));
bd.setConstructorArgumentValues(args);
bf.registerBeanDefinition("swk", bd);
bd = new GeneralBeanDefinition();
bd.setBeanClass(MagicGirl.class);
args = new ArrayList<>();
args.add("白骨精");
bd.setConstructorArgumentValues(args);
bf.registerBeanDefinition("baigujing", bd);
bd = new GeneralBeanDefinition();
bd.setBeanClass(Renminbi.class);
bf.registerBeanDefinition("renminbi", bd);
// 前置增强advice bean注册
bd = new GeneralBeanDefinition();
bd.setBeanClass(MyBeforeAdvice.class);
bf.registerBeanDefinition("myBeforeAdvice", bd);
// 环绕增强advice bean注册
bd = new GeneralBeanDefinition();
bd.setBeanClass(MyMethodInterceptor.class);
bf.registerBeanDefinition("myMethodInterceptor", bd);
// 后置增强advice bean注册
bd = new GeneralBeanDefinition();
bd.setBeanClass(MyAfterReturningAdvice.class);
bf.registerBeanDefinition("myAfterReturningAdvice", bd);
// 往BeanFactory中注册AOP的BeanPostProcessor
AdvisorAutoProxyCreator aapc = new AdvisorAutoProxyCreator();
bf.registerBeanPostProcessor(aapc);
// 向AdvisorAutoProxyCreator注册Advisor
aapc.registerAdvisor(
new AspectJPointcutAdvisor("myBeforeAdvice", "execution(* demo.di.MagicGirl.*(..))"));
// 向AdvisorAutoProxyCreator注册Advisor
aapc.registerAdvisor(
new AspectJPointcutAdvisor("myMethodInterceptor", "execution(* demo.di.Lad.say*(..))"));
// 向AdvisorAutoProxyCreator注册Advisor
aapc.registerAdvisor(new AspectJPointcutAdvisor("myAfterReturningAdvice",
"execution(* demo.di.Renminbi.*(..))"));
bf.preInstantiateSingletons();
System.out.println("-----------------myBeforeAdvice---------------");
MagicGirl gril = (MagicGirl) bf.getBean("baigujing");
gril.getFriend();
gril.getName();
System.out.println("----------------myMethodInterceptor----------------");
Boy boy = (Boy) bf.getBean("swk");
boy.sayLove();
System.out.println("-----------------myAfterReturningAdvice---------------");
Renminbi rmb = (Renminbi) bf.getBean("renminbi");
rmb.pay();
}
}
运行结果:
本文其他知识点: