极简Spring之AOP
简简单单实现个AOP
书接上回,这里接着介绍AOP
🍍什么是AOP?
假设有一个方法foo,现在有个需求想要记录下foo方法执行的耗时,于是就可以在foo执行之前记录一次当前时间T1,在执行之后再记录一次当前时间T2,两个时间相减就是foo方法执行耗时
以上场景就体现了AOP思想:在目标方法(foo)执行前后增加一些额外的逻辑
,从而达到增强目标方法的目的
AOP最经典的实现方式就是动态代理
,同时也有其他实现方法比如AJC代理和Agent代理,不过用的不多
Spring中AOP的核心就是:站在使用者角度来看,他们调用的方法看上去还是原来的foo方法,但是这个foo方法除了会执行原本的任务外,还额外增加了记录耗时
的逻辑,这是怎么做到的呢?
举个栗子,Spring中有一个Bean,长这样
//目标类
public class Target {
//目标方法
public void foo(){
System.out.println("执行目标方法...");
}
}
Spring在实例化这个Bean时,发现foo方法需要被增强,于是动态生成一个代理类对象$Proxy0
,大概长下面这样
//动态生成的代理类
public class $Proxy0 extend Target {
Target target = new Target();
//动态代理增强的方法
public void foo(){
long t1 = System.currentTimeMillis();
//调用目标方法
target.foo();
long t2 = System.currentTimeMillis();
System.out.println("foo方法耗时:" + (t2 - t1));
}
}
然后用这个代理类对象顶替掉原始Bean并返回,于是Spring的IOC容器中持有的这个Target类型的Bean,本质就变成了$Proxy0
这个代理类型
但是使用者并不知道,使用者看到的还是Target类型,调用这个Target类型Bean的foo方法,惊讶地发现在原本方法的基础上,这个方法还多了个计算耗时的增强逻辑
🍈要实现上面描述的功能,需要解决下面几个问题
- 在实例化Bean时,Spring怎么知道这个Bean应该被增强?
- 增强的逻辑放在哪的?怎么知道这些增强的代码分别属于哪些目标方法?
- 该怎么把增强逻辑与目标方法结合起来并以此产生一个代理类?
- 实例化Bean的哪个步骤进行上面的操作?
- 平时都是提前把类和方法写好,然后再编译运行,这里怎么在运行时动态生成类?
对于最后一个问题,Java有一个第三方库ASM,就是专门用来在程序运行时动态生成字节码,动态代理的两种实现方式,JDK自带的代理和CGLIB代理底层都是使用了ASM技术产生字节码,并加载进虚拟机
对前面几个问题,Spring的AOP定义了几个术语
-
连接点JoinPoint
也就是目标方法,需要被增强的方法
-
切点Pointcut
一个表达式,用来描述目标方法的
特征
,Spring可以通过切点来判断Bean中有没有符合切点条件的方法 -
通知Advice
也就是增强的逻辑,Spring可以通过某些注解得到所有通知
-
切面Advisor
切点和通知结合到一起,形成一个实体,也就是切面
-
织入
表示何时、何地以及如何将增强的逻辑应用到目标方法上
可以将这些术语理解为一个个抽象的组件,Spring一旦拥有了这些组件,在Bean后处理器
中合理运用这些组件,不就可以达到用代理对象顶替掉原始Bean的目的了吗?
这就是AnnotationAwareAspectJAutoProxyCreator,如果没有循环依赖就在后初始化完成,如果有循环依赖就在依赖注入前完成并暂存在二级缓存
再好的理念,必然要有落地的代码实现,本篇AOP文档就是介绍如何使用代码实现上面的几个组件
Spring中切面分为下面两大类
- Aspect高级切面:也就是使用@Aspect注解修饰的类,包含了多个
通知 + 切点
- Advisor低级切面:更细粒度的切面,只有一个
通知 + 切点
其实Aspect只是为了方便程序员开发
才出现的,而Advisor则是为了方便程序处理
,将来解析时会先将Aspect解析成若干个Advisor
1 Pointcut
Pointcut——切点,也叫切点表达式,用来描述目标方法长啥样
比如* org.springframework.bean.Person.*(..))
这个切点,描述了Person这个类中的所有方法
切点有了,如何判断某个方法是否匹配该切点呢?这里使用第三方库aspectJ
来判断
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
封装一下这个库,得到一个用来工具类,该工具类用于判断某个方法是否匹配指定切点
首先定义两个组件,表示切点是否匹配某个类、是否匹配某个方法,抽取成如下接口
public interface ClassFilter {
//判断指定类是否匹配切点,也就是判断类中有没有存在匹配切点的方法
boolean matches(Class<?> clazz);
}
public interface MethodMatcher {
//判断指定类中的指定方法是否匹配切点表达式
boolean matches(Method method, Class<?> targetClass);
}
再添加一个接口用于管理上面的两个组件
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
添加一个类AspectJExpressionPointcut
,实现上面三个接口
public class AspectJExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher {
private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES;
static {
SUPPORTED_PRIMITIVES = new HashSet<PointcutPrimitive>();
//添加一个用于解析execution(* com.example.service.*.*(..))这种切点的解析器
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
}
//切点表达式对象
private final PointcutExpression pointcutExpression;
//通过构造器传入具体的切点表达式,然后根据这个表达式生成PointcutExpression
public AspectJExpressionPointcut(String expression) {
//aspectJ的一个名称巨长的方法
PointcutParser pointcutParser = PointcutParser
.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, this.getClass().getClassLoader());
pointcutExpression = pointcutParser.parsePointcutExpression(expression);
}
/**
* 上面代码使用了aspectJ的api,用于根据指定切点表达式生成一个切点对象
* 下面是实现自接口的方法,就用到了这个切点对象
*/
@Override
public boolean matches(Class<?> clazz) {
//判断指定类是否匹配切点
return pointcutExpression.couldMatchJoinPointsInType(clazz);
}
@Override
public boolean matches(Method method, Class<?> targetClass) {
return pointcutExpression.matchesMethodExecution(method).alwaysMatches();
}
@Override
public ClassFilter getClassFilter() {return this;}
@Override
public MethodMatcher getMethodMatcher() {return this;}
}
该类封装了aspectJ提供的api,用来判断某个方法或类是否匹配指定的切点
测试一下
//准备一个切点
AspectJExpressionPointcut pointcut =
new AspectJExpressionPointcut("execution(* org.springframework.entity.Person.*(..))");
Class<Person> clazz = Person.class;
Method method = clazz.getDeclaredMethod("sayHello");
//判断切点是否匹配Person类
assertThat(pointcut.matches(clazz)).isTrue();
//判断切点是否匹配Person类的sayHello方法
assertThat(pointcut.matches(method, clazz)).isTrue();
2 动态代理
在运行时动态生成代理对象,有两种方式,JDK动态代理和CGLIB动态代理
下面介绍下这两种动态代理的使用方式
2.1 JDK动态代理
基于JDK的动态代理,生成的代理类和目标类会实现同样的接口,代理类和目标类属于兄弟关系
准备一个接口Foo
public interface Foo {
void foo();
long bar(int n);
}
准备一个实现了该接口的目标类Target
public class Target implements Foo{
private String field;
public void setField(String field) {this.field = field;}
@Override
public void foo() {
System.out.println("正在调用目标类的foo方法...");
}
@Override
public long bar(int n) {
int r = n * n;
System.out.println("正在调用目标类的bar方法,field是[" + field + "],并返回:" + r);
return r;
}
}
使用JDK代理为Target生成一个代理类,代码如下
//要生成一个代理类,需要准备四个组件:目标类、类加载器、实现的接口、回调函数
//1.首先准备一个目标类对象
Target target = new Target();
//2.将要动态生成的代理类要使用类加载器加载进JVM,所以需要提供一个类加载器
ClassLoader classLoader = this.getClass().getClassLoader();
//3.代理类要实现的接口
Class[] interfaces = {Foo.class};
/**
* 4.代理类要使用的回调函数
* 由于知道了要实现哪些接口,也就知道了将要生成的代理类里有哪些方法,以及方法长什么样子
* 唯一不知道的是代理类中实现方法的代码体长啥样,对于这种不明确的地方需要我们来指定
* 将代码体封装进函数式对象中,代理类中的实现方法内部就会调用到这里提供的函数式对象,函数对象需要几个参数:
* 1.proxy:代理类对象本身 2.method:正在调用的代理类方法 3.args:正在调用的方法参数
* 由于代理类和目标类实现了同样的接口,所以代理类中有的方法目标类中也有,因此可以调用到目标类中对应的方法
*/
InvocationHandler handler = (proxy, method, args) -> {
//在调用目标方法前执行增强逻辑
System.out.println("前置增强...");
//调用目标类对应的方法
Object result = method.invoke(target, args);
//在调用目标方法后执行增强逻辑
System.out.println("前置增强...");
//如果目标方法有返回值,代理方法肯定也会返回值
return result;
};
//使用下面代码生成代理类对象,由于代理类是实现自Foo接口,所以可以直接强转成Foo类型
Foo foo = (Foo)Proxy.newProxyInstance(classLoader, interfaces, handler);
//调用foo方法,发现target的foo方法就被增强了
foo.foo();
调用代理类的方法,本质都会调用到提供的回调函数
,然后回调函数内部根据传入的不同方法对象再调用目标
JDK代理底层使用了ASM生成字节码并加载进JVM,生成字节码翻译成Java类大概长下面这样,了解即可
/**
* 这里模拟了JDK生成的代理类,不过这里的代理类是写死的
* 而JDK生成代理类是在运行时使用字节码技术ASM动态生成的
* 有兴趣可以去了解下ASM如何生成代理类
*/
public class $Proxy0 implements Foo {
InvocationHandler invocationHandler;
//使用JDK代理时传入的回调函数,就是赋值给了invocationHandler
//将来调用代理类的方法时,就是调用InvocationHandler接口的invoke方法
public $Proxy0(InvocationHandler invocationHandler) {
this.invocationHandler = invocationHandler;
}
/**
* 这里定义了方法对象(方法来自目标类),这里为了模拟简单,方法对象是写死的
* 运行时会分析接口方法的签名,返回值,然后生成几个方法对象的静态变量,以及实现类的代码
*/
static Method foo;
static Method bar;
static {
try {
foo = Foo.class.getMethod("foo");
bar = Foo.class.getMethod("bar");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
@Override
public void foo() {
try {
//回调调用方定义的方法
invocationHandler.invoke(this, foo, new Object[0]);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
//将编译时异常变成运行时异常
throw new UndeclaredThrowableException(e);
}
}
@Override
public long bar() {
try {
Object result = invocationHandler.invoke(this, bar, new Object[0]);
return (long)result;
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
2.2 CGLIB动态代理
基于CGLIB的动态代理,其生成的代理类既可以是目标类的子类,属于父子关系,又可以跟目标类实现同样接口
CGLIB代理属于第三方库
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.3.0</version>
</dependency>
CGLIB代理相对于JDK代理,有三种方式调用目标方法,下面演示生成目标类子类的代理对象
//高版本的JDK要想成功运行下面程序,要加JVM参数:--add-opens java.base/java.lang=ALL-UNNAMED
Target t = new Target();
//这里的MethodInterceptor是CGLIB提供的函数式接口,意思跟前面我们定义的MethodInterceptor差不多
MethodInterceptor interceptor = (MethodInterceptor) (p, method, arg, methodProxy) -> {
System.out.println("before...");
//通过反射调用,跟JDK代理一样
method.invoke(t, arg);
//methodProxy方法调用,不走反射
methodProxy.invoke(t, arg);
//methodProxy方法调用,不需要额外定义一个目标类对象,使用代理类对象本身即可,同样不走反射
methodProxy.invokeSuper(p, arg);
System.out.println("after...");
return null;
};
Target target = (Target)Enhancer.create(Target.class, interceptor);
target.foo();
CGLIB生成的代理类结构比jdk代理类复杂,主要复杂在:CGLIB可以不通过反射调用到目标类方法
其原理就是:CGLIB内部会额外生成两个代理类(FastClass的子类),这两个代理类一个结合目标类使用,一个结合代理类使用,了解即可
3 MethodInterceptor
MethodInterceptor表示方法拦截器,拦截目标方法并在其调用前后执行增强逻辑
而Spring中具体的通知Advice就是实现了MethodInterceptor
将Adivce抽象成接口
//Advice表示增强的逻辑,也就是通知
public interface Advice {}
增强逻辑,本质在调用目标方法的前后执行代理行为
//拦截器,拦截目标方法对其进行增强
public interface Interceptor extends Advice {}
在目标方法执行前后执行代理行为,这一系列过程又可以抽取成一个接口
public interface MethodInterceptor extends Interceptor {
//参数method表示被拦截的目标方法
Object invoke(MethodInvocation method) throws Throwable;
}
实际上,实现MethodInterceptor接口的
4 Joinpoint与调用链Invocation
Joinpoint代表被增强的目标方法
public interface Joinpoint {
//调用目标方法
Object proceed() throws Throwable;
//得到目标类
Object getThis();
//得到连接点,也就是目标方法,AccessibleObject是方法对象Method的父类
AccessibleObject getStaticPart();
}
将增强的逻辑应用到Joinpoint的过程称为织入,织入后面介绍
织入成功并生成代理类之后,代理类中的代理方法大概长这样:
| -> before1 --------------------------------
| -> before2 ------------------ |
| -> target -------> 目标 |advice2 advice1
| -> after2 ------------------ |
| -> after1 --------------------------------
可以看出,这本质就是一条链,可以将这条调用链
封装成一个组件Invocation
调用Invocation组件,就会沿着这条调用链从上到下执行,站在使用者的角度,看起来就像在执行一个增强方法,下面实现一条Invocation调用链
首先将Invocation抽象成接口
//调用链里面肯定会调用到目标方法,所以继承Joinpoint
public interface Invocation extends Joinpoint {
//得到调用方法传入的参数
Object[] getArguments();
}
在定义一个接口MethodInvocation,继承自Invocation,表示目标方法的调用链
public interface MethodInvocation extends Invocation {
//目标方法
Method getMethod();
}
其实现类ReflectiveMethodInvocation负责实现调用链,负责执行所有增强以及调用目标方法
想要通过反射调用方法,必备三要素:目标类对象、目标方法对象、方法的参数值
而要想执行增强,就必须持有所有的MethodInterceptor
于是,ReflectiveMethodInvocation长下面这样
public class ReflectiveMethodInvocation implements MethodInvocation {
protected final Object target;
protected final Method method;
protected final Object[] arguments;
protected final List<MethodInterceptor> interceptors;
//表示当前调用到了第几个MethodInterceptor
private int count = -1;
public ReflectiveMethodInvocation(Object target, Method method, Object[] arguments, List<MethodInterceptor> interceptors) {
this.target = target;
this.method = method;
this.arguments = arguments;
this.interceptors = interceptors;
}
//调用目标方法
@Override
public Object proceed() throws Throwable {
//如果所有MethodInterceptor的前置增强都执行完毕了,则开始调用目标方法
if (this.count == this.interceptors.size() - 1) {
return method.invoke(target, arguments);
}
//间接递归调用MethodInterceptor
MethodInterceptor interceptor = interceptors.get(++count);
return interceptor.invoke(this);
}
@Override
public Method getMethod() {return method;}
@Override
public Object[] getArguments() {return arguments;}
@Override
public Object getThis() {return target; }
@Override
public AccessibleObject getStaticPart() {return method;}
}
Invocation的proceed方法里会调用MethodInterceptor的invoke方法,然后invoke里又调用proceed方法,所以是间接递归,通过这种方法,就可以实现一条调用链
测试一下,先准备两个MethodInterceptor
class MyInterceptor1 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("前置增强1...");
//调用目标方法
Object proceed = invocation.proceed();
System.out.println("后置增强1...");
return proceed;
}
}
class MyInterceptor2 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("前置增强2...");
//调用目标方法
Object proceed = invocation.proceed();
System.out.println("后置增强2...");
return proceed;
}
}
然后执行这条调用链
//准备目标类
Target target = new Target();
//准备目标方法
Method bar = target.getClass().getDeclaredMethod("bar", int.class);
//准备方法参数值
Object[] args = {233};
//准备MethodInterceptor集合
List<MethodInterceptor> interceptors = List.of(new MyInterceptor1(), new MyInterceptor2());
//将目标方法和interceptors封装成调用链对象
ReflectiveMethodInvocation invocation =
new ReflectiveMethodInvocation(target, bar, args, interceptors);
/**
* 执行的打印结果如下:
* 前置增强1...
* 前置增强2...
* 正在调用目标类的bar方法,field是[null],并返回:54289
* 后置增强2...
* 后置增强1...
*/
invocation.proceed();
MethodInterceptor里面一定要调用到目标方法,否则这条链就会断掉
5 AdvisedSupport
AdvisedSupport承载了 AOP 织入所需的关键配置信息和管理功能,为实际的织入操作提供了基础和支持
要执行织入操作,首先需要知道目标类,将目标类封装一下
这里将目标类封装成TargetSource
public class TargetSource {
//持有目标类对象
private final Object target;
//通过构造参数传入目标类对象
public TargetSource(Object target) {
this.target = target;
}
//得到目标类实现的接口列表
public Class<?>[] getTargetClass() {
return this.target.getClass().getInterfaces();
}
//得到目标类对象本身
public Object getTarget() {
return this.target;
}
}
此外,还需要知道所有增强的逻辑,以及增强逻辑需要应用到哪个方法上
于是可以推断出AdvisedSupport长下面这样
public class AdvisedSupport {
//表示目标类
private TargetSource targetSource;
//表示增强的逻辑
private List<MethodInterceptor> interceptors;
//匹配方法的组件
private MethodMatcher methodMatcher;
//省略了上面三个组件的get、set方法
//.....
}
6 AopProxy
AopProxy是一个接口,抽象了获取代理对象
的这个动作
public interface AopProxy {
//得到代理类
Object getProxy();
}
其有两个实现类,分别表示获取JDK代理对象和获取CGLIB的代理对象
JdkDynamicAopProxy表示使用获取JDK代理对象,织入的操作就封装在里面
//实现了InvocationHandler接口,以此给代理对象传回调函数时传this就行了
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
//AdvisedSupport用于给织入操作提供支持
private final AdvisedSupport advised;
public JdkDynamicAopProxy(AdvisedSupport advised) {
this.advised = advised;
}
/**
* 生成的代理类中的方法会调用到这的invoke方法
* invoke方法中,将增强的逻辑应用到目标方法上,这就是织入
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class<?> targetClass = advised.getTargetSource().getTarget().getClass();
//判断切点是否匹配目标类的目标方法
if (advised.getMethodMatcher().matches(method, targetClass)) {
//如果匹配,说明目标方法需要被增强,methodInterceptor表示增强的逻辑
List<MethodInterceptor> interceptors = advised.getInterceptors();
//invocation表示目标方法的调用
ReflectiveMethodInvocation invocation = new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args, interceptors);
//执行调用链
return invocation.proceed();
}
//不匹配就直接执行目标方法,无需增强
return method.invoke(advised.getTargetSource().getTarget(), args);
}
//创建代理类并返回
@Override
public Object getProxy() {
ClassLoader classLoader = getClass().getClassLoader();
Class<?>[] interfaces = advised.getTargetSource().getTargetClass();
return Proxy.newProxyInstance(classLoader, interfaces, this);
}
}
测试一下JdkDynamicAopProxy
TargetSource targetSource = new TargetSource(new Target()); //封装目标类
List<org.springframework.aop.MethodInterceptor> interceptors = List.of(new MyInterceptor1(), new MyInterceptor2()); //封装通知
MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* org.springframework.test.bean.Foo.*(..))").getMethodMatcher(); //封装切点
//封装AdvisedSupport
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTargetSource(targetSource);
advisedSupport.setInterceptors(interceptors);
advisedSupport.setMethodMatcher(methodMatcher);
//创建代理类
Foo foo = (Foo)new JdkDynamicAopProxy(advisedSupport).getProxy();
foo.foo();
AopProxy的另一个实现类CglibAopProxy用于获取CGLIB代理对象,同样封装了织入
该类中持有两个内部类
- DynamicAdvisedInterceptorAdapter:将CGLIB提供的MethodInterceptor适配成我们定义的AdvisedSupport
- CglibMethodInvocation:CGLIB提供其他方式调用目标,所有封装一个适用于CGLIB的Invocation类
代码如下
public class CglibAopProxy implements AopProxy {
//织入支持
private final AdvisedSupport support;
public CglibAopProxy(AdvisedSupport support) {
this.support = support;
}
@Override
public Object getProxy() {
//创建代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(support.getTargetSource().getTarget().getClass());
enhancer.setInterfaces(support.getTargetSource().getTargetClass());
//该方法需要的MethodInterceptor类型参数是CGLIB中提供的MethodInterceptor
enhancer.setCallback(new DynamicAdvisedInterceptorAdapter(support));
return enhancer.create();
}
/**
* 这是一个典型的适配器模式,将我们上面定义的MethodInterceptor转为CGLIB提供的MethodInterceptor
*/
private record DynamicAdvisedInterceptorAdapter(AdvisedSupport advised)
implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
CglibMethodInvocation invocation =
new CglibMethodInvocation(advised.getTargetSource().getTarget(),
method, args, methodProxy, advised.getInterceptors());
if (advised.getMethodMatcher().matches(method, advised.getTargetSource()
.getTarget().getClass())) {
//代理方法
return invocation.proceed();
}
return method.invoke(o, args);
}
}
/**
* CGLIB可以通过其他方式调用目标,所有增加一个适用于CGLIB的Invocation
*/
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
//使用该对象可以无需反射调用到目标方法
private final MethodProxy methodProxy;
public CglibMethodInvocation(Object target, Method method,
Object[] arguments, MethodProxy methodProxy,
//这的MethodInterceptor是我们上面定义的MethodInterceptor
List<org.springframework.aop.MethodInterceptor> interceptors) {
super(target, method, arguments, interceptors);
this.methodProxy = methodProxy;
}
@Override
public Object proceed() throws Throwable {
if (this.count == this.interceptors.size() - 1) {
//通过CGLIB提供的方法调用目标
return methodProxy.invoke(target, args);
}
org.springframework.aop.MethodInterceptor interceptor = interceptors.get(++count);
return interceptor.invoke(this);
}
}
}
测试一下
TargetSource targetSource = new TargetSource(new Target()); //封装目标类
List<org.springframework.aop.MethodInterceptor> interceptors = List.of(new MyInterceptor1(), new MyInterceptor2());//封装通知
MethodMatcher methodMatcher = new AspectJExpressionPointcut(
"execution(* org.springframework.test.bean.Foo.*(..))").getMethodMatcher(); //封装切点
//封装AdvisedSupport
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTargetSource(targetSource);
advisedSupport.setInterceptors(interceptors);
advisedSupport.setMethodMatcher(methodMatcher);
//创建代理类
Foo foo = (Foo)new CglibAopProxy(advisedSupport).getProxy();
foo.foo();
AopProxy的两个实现类,分别表示基于JDK代理和基于CGLIB代理,关系如下
«interface»
AopProxy
+Object getProxy()
«interface»
InvocationHandler
+Object invoke(Object proxy, Method method, Object[] args)
JdkDynamicAopProxy
+ final AdvisedSupport advised
+Object getProxy()
+Object invoke(Object proxy, Method method, Object[] args)
CglibAopProxy
+ final AdvisedSupport advised
+Object getProxy()
7 代理工厂
织入时希望可以选择代理类是基于JDK代理还是CGLIB代理,于是给AdvisedSupport增加一个属性proxyTargetClass
public class AdvisedSupport {
//是否使用cglib代理,默认false,也就是默认使用JDK代理
private boolean proxyTargetClass = false;
private TargetSource targetSource;
private List<MethodInterceptor> interceptors;
private MethodMatcher methodMatcher;
//省略了get、set、is方法
}
再封装一层ProxyFactory,依赖了AopProxy的两个实现类,并根据proxyTargetClass自动选择一个实现类创建代理对象
//ProxyFactory表示代理工厂
public class ProxyFactory {
//织入
private AdvisedSupport advisedSupport;
public ProxyFactory(AdvisedSupport advisedSupport) {
this.advisedSupport = advisedSupport;
}
//获取代理对象
public Object getProxy() {
return createAopProxy().getProxy();
}
//根据proxyTargetClass选择一个AopProxy实现类
private AopProxy createAopProxy() {
if (advisedSupport.isProxyTargetClass()) {
return new CglibAopProxy(advisedSupport);
}
return new JdkDynamicAopProxy(advisedSupport);
}
}
测试一下
//设置切点
MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* org.springframework.test.bean.Foo.*(..))").getMethodMatcher();
//封装织入
AdvisedSupport support = new AdvisedSupport();
support.setTargetSource(new TargetSource(new Target()));
support.setMethodInterceptor(new MyInterceptor());
support.setMethodMatcher(methodMatcher);
/**
* 测试使用CGLIB代理
*/
support.setProxyTargetClass(true);
//生成的代理类是目标类Target的子类
Target target = (Target)new ProxyFactory(support).getProxy();
target.foo();
/**
* 测试使用JDK代理
*/
support.setProxyTargetClass(false);
//生成的代理类与目标类实现了同样的接口Foo
Foo foo = (Foo)new ProxyFactory(support).getProxy();
foo.bar();
8 几种Advice
Adivce表示通知,也就是增强的逻辑
Spring中定义了五种通知
- 前置通知:目标方法执行前执行的增强逻辑
- 后置通知:目标方法执行后执行的增强逻辑
- 环绕通知:目标方法执行前后执行的增强逻辑
- 异常通知:执行目标方法发生异常时的增强逻辑
- 最终通知:目标方法执行后无论是否发生异常都会执行的增强逻辑,相当于
finally
块
前面讲过,通知会和目标一起被封装成一个调用链对象,为了方便形成调用链,这些通知应该实现同一个接口,这个接口就是MethodInterceptor,环绕、异常、最终通知都实现了该接口
但由于历史原因,前置、后置通知并没有实现这个接口,Spring会使用适配器来进行转换
定义前置通知
//前置通知接口
public interface BeforeAdvice extends Advice {}
//实现该接口即可认为是一个前置通知
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method method, Object[] args, Object target) throws Throwable;
}
定义后置通知
//后置通知接口
public interface AfterAdvice extends Advice {}
//实现该接口即可认为是一个后置通知
public interface AfterReturningAdvice extends AfterAdvice {
void afterReturning(Object returnValue, Method method, Object[] args,
Object target) throws Throwable;
}
将前置、后置通知转化为环绕通知的适配器类
class MethodBeforeAdviceInterceptor implements MethodInterceptor {
private MethodBeforeAdvice advice;
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
//在执行目标方法之前,先执行before advice操作
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
}
class AfterReturningAdviceInterceptor implements MethodInterceptor {
private AfterReturningAdvice advice;
public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
//在执行目标方法之后,在执行after advice操作
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
}
测试,在业务代码中定义前置、后置通知的实现类
class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("这是一个前置通知...");
}
}
class MyAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("这是一个后置通知...");
}
}
测试代码
//设置切点
MethodMatcher methodMatcher = new AspectJExpressionPointcut(
"execution(* org.springframework.test.bean.Foo.*(..))").getMethodMatcher();
//设置增强
List<MethodInterceptor> interceptors = List.of(
new MethodBeforeAdviceInterceptor(new MyBeforeAdvice()),
new AfterReturningAdviceInterceptor(new MyAfterReturningAdvice())
);
AdvisedSupport support = new AdvisedSupport();
support.setTargetSource(new TargetSource(new Target()));
support.setInterceptors(interceptors);
support.setMethodMatcher(methodMatcher);
support.setProxyTargetClass(true);
//生成的代理类是目标类Target的子类
Target target = (Target)new ProxyFactory(support).getProxy();
target.foo();
9 Advisor
Advisor表示切面,包含了一个Pointcut和一个Advice
- Pointcut用于匹配和捕获JoinPoint
- Advice决定对JoinPoint进行何种增强
首先定义一个Advisor接口
//该接口依赖了Advice
public interface Advisor {
Advice getAdvice();
}
还差一个JoinPoint,再定义一个子接口
public interface PointcutAdvisor extends Advisor {
Pointcut getPointcut();
}
其实现类长这样
public class AspectJExpressionPointcutAdvisor implements PointcutAdvisor {
private AspectJExpressionPointcut pointcut;
private Advice advice;
//切点表达式
private String expression;
//根据表达式生成AspectJExpressionPointcut
public void setExpression(String expression) {
this.expression = expression;
pointcut = new AspectJExpressionPointcut(expression);
}
//pointcut和advice的get、set方法
//.....
}
简单测试一下
//目标类
Target target = new Target();
//切点表达式
String express = "execution(* org.springframework.test.bean.Foo.*(..))";
//创建两个切面
AspectJExpressionPointcutAdvisor advisor1 = new AspectJExpressionPointcutAdvisor();
advisor1.setExpression(express);
advisor1.setAdvice(new AfterReturningAdviceInterceptor(new MyAfterReturningAdvice()));
AspectJExpressionPointcutAdvisor advisor2 = new AspectJExpressionPointcutAdvisor();
advisor2.setExpression(express);
advisor2.setAdvice(new MethodBeforeAdviceInterceptor(new MyBeforeAdvice()));
//假设这是容器内所有的切面
List<Advisor> advisors = List.of(advisor1, advisor2);
//收集符合目标类的所有通知
List<Advisor> suitableAdvisors = advisors.stream()
.filter(advisor -> ((PointcutAdvisor) advisor).getPointcut()
.getClassFilter().matches(target.getClass())
).collect(Collectors.toList());
//如果存在一个切面能够匹配目标类,就进行增强
if (suitableAdvisors.size() > 0) {
List<MethodInterceptor> interceptors = suitableAdvisors.stream().map(
advisor -> (MethodInterceptor)advisor.getAdvice()
).collect(Collectors.toList());
AdvisedSupport support = new AdvisedSupport();
support.setTargetSource(new TargetSource(target));
support.setInterceptors(interceptors);
support.setMethodMatcher(
((PointcutAdvisor)suitableAdvisors.get(0)).getPointcut().getMethodMatcher()
);
//使用JDK代理
Foo foo = (Foo)new ProxyFactory(support).getProxy();
foo.bar(1);
}
10 与Spring结合
前面都是动态代理的独角戏,而与Spring结合才叫AOP,怎么结合呢?自然是融入到Bean的生命周期,所以需要用到Bean后处理器
BeanPostProcessor只能扩展前后初始化阶段,扩展点太少
引入一个新的Bean后处理器,负责为Bean构造前、Bean构造后依赖注入前、依赖注入阶段提供扩展
这就是InstantiationAwareBeanPostProcessor
,继承BeanPostProcessor
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
//Bean构造前回调
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
//Bean构造后依赖注入前回调
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;
//依赖注入时回调
PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean,
String beanName) throws BeansException;
}
依赖注入和属性填充有区别
- 依赖注入是通过@Value、@Autowired或者构造器注入为属性赋值
- 属性填充是根据BeanDefinition的PropertyValues为属性赋值
这里了解下,后面会介绍依赖注入
DefaultAdvisorAutoProxyCreator
是其实现类,在后初始化时判断是否需要生成代理并进行织入
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
private DefaultListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
}
//构造前,如果返回对象就以该对象作为成品Bean而不走后面实例化流程,返回null则跟以前一样走流程
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
//构造后,返回false则介绍实例化流程立即以当前Bean作为成品Bean返回
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return true;
}
//依赖注入
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
return pvs;
}
//前初始化,以返回的对象顶替调原始Bean
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
//后初始化,以返回的对象顶替调原始Bean,在这里创建并返回代理类
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//避免死循环
if (isInfrastructureClass(bean.getClass())) {
return bean;
}
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
//如果需要被代理,则返回代理类,否则返回当前bean
return wrapIfNecessary(bean, beanName);
}
//根据目标类找到有资格的所有Advisor
private List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//从容器得到所有Advisor类型的Bean
Map<String, Advisor> advisorMap = beanFactory.getBeansOfType(Advisor.class);
//找到匹配当前Bean的Advisor
List<Advisor> advisors = advisorMap.values().stream().filter(
advisor -> ((PointcutAdvisor) advisor).getPointcut()
.getClassFilter().matches(beanClass)
).toList();
return advisors;
}
//如果至少有一个Advisor匹配当前Bean,就创建一个代理类,返回null就说明无需代理类
private Object wrapIfNecessary(Object bean, String beanName) {
try {
List<Advisor> advisors = findEligibleAdvisors(bean.getClass(), beanName);
if (!advisors.isEmpty()) {
List<MethodInterceptor> interceptors = advisors.stream().map(advisor ->
(MethodInterceptor)advisor.getAdvice()).toList();
AdvisedSupport advisedSupport = new AdvisedSupport();
TargetSource targetSource = new TargetSource(bean);
advisedSupport.setTargetSource(targetSource);
advisedSupport.setInterceptors(interceptors);
advisedSupport.setMethodMatcher(
((PointcutAdvisor)advisors.get(0)).getPointcut().getMethodMatcher()
);
//使用JDK代理
return new ProxyFactory(advisedSupport).getProxy();
}
} catch (Exception e) {
throw new BeansException("创建代理对象失败: " + beanName, e);
}
return bean;
}
private boolean isInfrastructureClass(Class<?> beanClass) {
return Advice.class.isAssignableFrom(beanClass)
|| Pointcut.class.isAssignableFrom(beanClass)
|| Advisor.class.isAssignableFrom(beanClass);
}
}
织入的逻辑放在后初始化中,前面该咋样咋样,属性填充,初始化什么的都没影响,在实例化的最后一部后初始化中返回代理对象,AbstractAutowireCapableBeanFactory增加如下代码
public abstract class AbstractAutowireCapableBeanFactory
extends AbstractBeanFactory implements AutowireCapableBeanFactory {
//修改createBean的方法
@Override
protected Object createBean(String beanName,
BeanDefinition beanDefinition) throws BeansException {
//调用bean后处理器执行构造前的逻辑,如果的bean不为空就不走后面的实例化流程了
Object bean = resolveBeforeInstantiation(beanName, beanDefinition);
if (bean != null) {
return bean;
}
//构造前的后处理器返回null,说明还是要走实例化流程的
return doCreateBean(beanName, beanDefinition);
}
//Bean构造前的逻辑
protected Object resolveBeforeInstantiation(String beanName,
BeanDefinition beanDefinition) {
Object bean = applyBeanPostProcessorsBeforeInstantiation(
beanDefinition.getBeanClass(), beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
return bean;
}
//Bean实例化前的解析
protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass,
String beanName) {
for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor bpp) {
Object result = bpp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
//修改doCreateBean
protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
Object bean = null;
try {
//构造Bean
bean = createBeanInstance(beanDefinition);
//调用bean后处理器执行构造后的逻辑,如果返回false,就不走后面实例化流程
boolean continueWithPropertyPopulation
= applyBeanPostProcessorsAfterInstantiation(beanName, bean);
if (!continueWithPropertyPopulation) {
return bean;
}
//依赖注入
applyBeanPostprocessorsBeforeApplyingPropertyValues(
beanName, bean, beanDefinition
);
//属性填充
applyPropertyValues(beanName, bean, beanDefinition);
//前初始化、初始化、后初始化
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("实例化bean失败", e);
}
//将成品Bean注册到单例Bean注册表之前,先注册拥有销毁方法的Bean
registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
//实例化完成后,将Bean注册进单例Bean注册表
//只有Scope为singleton的Bean才保存起来
if (beanDefinition.isSingleton()) {
addSingleton(beanName, bean);
}
return bean;
}
//构造后的逻辑
private boolean applyBeanPostProcessorsAfterInstantiation(String beanName, Object bean) {
boolean continueWithPropertyPopulation = true;
for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
if (!((InstantiationAwareBeanPostProcessor) beanPostProcessor)
.postProcessAfterInstantiation(bean, beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
return continueWithPropertyPopulation;
}
//TODO 依赖注入,此时还没有实现功能
protected void applyBeanPostprocessorsBeforeApplyingPropertyValues(
String beanName, Object bean, BeanDefinition beanDefinition) {
for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
//如果是InstantiationAwareBeanPostProcessor类型,就回调postProcessPropertyValues
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
PropertyValues pvs = ((InstantiationAwareBeanPostProcessor) beanPostProcessor)
.postProcessPropertyValues(beanDefinition.getPropertyValues(),
bean, beanName);
//设置新的PropertyValue
if (pvs != null) {
for (PropertyValue propertyValue : pvs.getPropertyValues()) {
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
}
}
}
}
//为了符合Spring,属性填充时调用set方法为属性赋值,而不是直接给属性赋值
protected void applyPropertyValues(String beanName, Object bean,
BeanDefinition beanDefinition) {
PropertyValues pvs = beanDefinition.getPropertyValues();
try {
for (PropertyValue pv : pvs.getPropertyValues()) {
String name = pv.getName();
Object value = pv.getValue();
if (value instanceof BeanReference beanReference) {
value = getBean(beanReference.getBeanName());
}
//通过反射调用属性对应的set方法
String setMethod = "set" +
name.substring(0, 1).toUpperCase() + name.substring(1);
ReflectUtil.invoke(bean, setMethod, value);
}
} catch (Exception e) {
throw new BeansException("属性填充发生异常,在Bean:" + beanName, e);
}
}
}
注意,依赖注入没有实现,因为此时没有一个Bean后处理器能够进行依赖注入,在后面会引入
测试阶段,先添加一个XML文件
<!--aop.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!--目标类-->
<bean id="target" class="org.springframework.test.bean.Target">
<property name="field" ref="属性填充" />
</bean>
<!--Bean后处理器-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<!--将前置、后置增强适配为MethodInterceptor类型-->
<bean id="beforeAdviceMethodInterceptor" class="org.springframework.aop.framework.advice.MethodBeforeAdviceInterceptor">
<property name="advice" ref="beforeAdvice" />
</bean>
<bean id="afterAdviceMethodInterceptor" class="org.springframework.aop.framework.advice.AfterReturningAdviceInterceptor">
<property name="advice" ref="afterAdvice" />
</bean>
<!--单纯的前置和后置增强-->
<bean id="beforeAdvice" class="org.springframework.test.service.MyBeforeAdvice"/>
<bean id="afterAdvice" class="org.springframework.test.service.MyAfterReturningAdvice"/>
</beans>
实例化策略是通过无参构造创建对象,但是MethodBeforeAdviceInterceptor和AfterReturningAdviceInterceptor并没有无参构造,为了测试通过,需要给这两个类添加一个无参构造哦
测试代码如下
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:aop.xml");
//得到的foo就是一个代理类
Foo foo = context.getBean("target", Foo.class);
/**
* 调用下面方法,发现是个增强方法,并且属性填充功能也是正常的,打印如下
* 这是一个前置通知...
* 正在调用目标类的bar方法,field是[属性填充],并返回:1
* 这是一个后置通知...
*/
foo.bar(1);
11 使用注解开发
使用AOP时,我们更多的是使用注解@Aspect
作为切面
@Component
@Aspect
class MyAspect {
@Before("execution(* foo())")
public void before() {
System.out.println("前置通知");
}
@AfterReturning("execution(* foo())")
public void afterReturning() {
System.out.println("后置通知");
}
@Around("execution(* foo())")
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕前置");
point.proceed();
System.out.println("环绕后置");
}
@After("execution(* foo())")
public void after() {
System.out.println("最终通知");
}
}
这就跟IOC篇的注解监听器一样,最终会被解析成Advisor并被注册进容器,解析方法就像下面一样
//aspect就是加了@Aspect注解的Bean
//下面演示的类和方法是Spring中提供的
public List<Advisor> convert(Object aspect) {
//切面工厂,使用这个工厂可以拿到aspect这个切面对象
AspectInstanceFactory factory = new SingletonAspectInstanceFactory(aspect);
//收集低级切面的集合
List<Advisor> list = new ArrayList<>();
//判断aspect有没有加@Aspect注解
if (aspect.getClass().isAnnotationPresent(Aspect.class)) {
//循环结束,就将aspect转成了若干个Advisor
for (Method method : aspect.getClass().getDeclaredMethods()) {
//下面演示了四种通知注解的解析
if (method.isAnnotationPresent(Before.class)) {
//解析切点
String value = method.getAnnotation(Before.class).value();
AspectJExpressionPointcut pt = new AspectJExpressionPointcut();
pt.setExpression(value);
AspectJMethodBeforeAdvice advice =
new AspectJMethodBeforeAdvice(method, pt, factory);
//将方法转为Advisor
Advisor advisor = new DefaultPointcutAdvisor(pt, advice);
list.add(advisor);
}
else if (method.isAnnotationPresent(AfterReturning.class)) {
String value = method.getAnnotation(AfterReturning.class).value();
AspectJExpressionPointcut pt = new AspectJExpressionPointcut();
pt.setExpression(value);
AspectJAfterReturningAdvice advice =
new AspectJAfterReturningAdvice(method, pt, factory);
Advisor advisor = new DefaultPointcutAdvisor(pt, advice);
list.add(advisor);
}
else if (method.isAnnotationPresent(Around.class)) {
String value = method.getAnnotation(Around.class).value();
AspectJExpressionPointcut pt = new AspectJExpressionPointcut();
pt.setExpression(value);
AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pt, factory);
Advisor advisor = new DefaultPointcutAdvisor(pt, advice);
list.add(advisor);
}
else if (method.isAnnotationPresent(After.class)) {
String value = method.getAnnotation(After.class).value();
AspectJExpressionPointcut pt = new AspectJExpressionPointcut();
pt.setExpression(value);
AspectJAfterAdvice advice = new AspectJAfterAdvice(method, pt, factory);
Advisor advisor = new DefaultPointcutAdvisor(pt, advice);
list.add(advisor);
}
}
}
return list;
}
将解析出来的Advisor注册到Spring中很简单,就跟IOC篇的注解监听器一样,这里就不演示了
转载自:https://juejin.cn/post/7368355894708584488