手写 Spring 09 - 核心增强,AOP实现
这个文档讨论了如何使用 Spring AOP 实现动态代理。它包括了 AspectJExpressionPointcut、TargetSource、AdvisedSupport、ReflectiveMethodInvocation、AopProxy、JdkDynamicAopProxy 和 Cglib2AopProxy 等类的实现。
此外,文档还提供了一个 TigerInterceptor 类,该类演示了如何在代理对象上拦截方法调用并执行增强逻辑。最后,文档提供了一个测试类,该类演示了如何使用 Spring AOP 创建动态代理对象。
# 解析
AOP 的实现,最主要的核心就是切面表达式和动态代理。切面表达式匹配到指定的切面,再交由不用的代理(jdk 或 cglib)来实现。而他们的实现已经由 aspectj 、jdk 、 cglib 实现了,我们所作的就是封装他们的复杂性,将一个简单的类或功能暴露出来。
切面表达式:使用 aspectj 的 org.aspectj.weaver.tools.PointcutExpression
动态代理:将 jdk 和 cglib 的代理统一由 aopalliance
的 org.aopalliance.intercept.MethodInterceptor
和 org.aopalliance.intercept.MethodInvocation
实现(换句话说,aopalliance 将方法的调用转发到不同的代理实现中)。
这样对用户来说,只需 ① 自己定义一个切面表达式和 ② 自己实现 MethodInvocation
来实现切面,简单了用户操作,屏蔽了底层实现。
# 实现切面表达式
切面表达式就一个作用:过滤出指定的类和方法。
# 定义一些职责和能力
Pointcut
Pointcut 接口是 Spring 框架 AOP(面向切面编程)的一个核心接口,它用于描述一组需要被拦截的方法或类。
在 Spring 中,Pointcut 可以被看作是一个逻辑切面,它包含了需要被增强的方法和类的信息,并可以根据这些信息来决定哪些方法需要被拦截和增强。
public interface Pointcut {
/**
* 获取类过滤器
* 类过滤器用于对目标类进行筛选和匹配,以便确定哪些类需要被代理和增强;
*
* @return {@link ClassFilter}
*/
ClassFilter getClassFilter();
/**
* 获取方法匹配器
* 方法匹配器用于对目标类的方法进行筛选和匹配,以便确定哪些方法需要被拦截和增强;
*
* @return {@link MethodMatcher}
*/
MethodMatcher getMethodMatcher();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ClassFilter
在 AOP 中,ClassFilter 用于在 BeanFactory 和 ApplicationContext 中过滤出需要被代理的类,主要作用是对目标类进行筛选和匹配,以便确定哪些类需要被代理和增强。
例如,在 Spring AOP 中,可以使用切点(Pointcut)来定义一个需要拦截的切点,然后使用 ClassFilter 对目标类进行筛选和匹配。
在匹配过程中,Spring 将会遍历所有的类,将每个类传递给 ClassFilter 的 matches 方法进行匹配。
如果这个类符合条件,则将其加入到代理列表中,否则跳过不处理。
public interface ClassFilter {
/**
* 用于判断指定的 Class 对象是否符合条件
*
* @param clazz clazz
* @return boolean
*/
boolean matches(Class<?> clazz);
}
2
3
4
5
6
7
8
9
10
11
MethodMatcher
在 AOP 中,MethodMatcher 主要作用是对目标类的方法进行筛选和匹配,以便确定哪些方法需要被拦截和增强。通常情况下,MethodMatcher 总是和 ClassFilter 一起使用,以实现更加细粒度的 AOP 控制。
例如,在 Spring AOP 中,可以通过 Pointcut 来定义一个切点,然后使用 MethodMatcher 进行方法匹配。
- 在匹配过程中,Spring 将会遍历所有的方法,将每个方法传递给 MethodMatcher 的 matches 方法进行匹配。
- 如果这个方法符合条件,则将其加入到代理列表中,否则跳过不处理。
public interface MethodMatcher {
/**
* 用于在代理对象上进行目标方法匹配,判断是否符合条件
*
* @param method 方法
* @param targetClass 目标类
* @return boolean
*/
boolean matches(Method method, Class<?> targetClass);
}
2
3
4
5
6
7
8
9
10
11
12
# 具体的实现
AspectJExpressionPointcut
AspectJExpressionPointcut 是 Spring 框架中实现 Pointcut 接口的一个具体实现类,用于定义一个基于 AspectJ 表达式的静态切点。
AspectJ 是一种基于注解的 AOP 框架,它提供了一组强大的注解和表达式来进行方法拦截和增强。
public class AspectJExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher {
private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
static {
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
}
private final PointcutExpression pointcutExpression;
public AspectJExpressionPointcut(String expression) {
PointcutParser pointcutParser =
PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
SUPPORTED_PRIMITIVES, this.getClass().getClassLoader());
pointcutExpression = pointcutParser.parsePointcutExpression(expression);
}
@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;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 将实现加入原有的逻辑中
- 无
# 测试
- 测试类
@Test
public void aspectJExpressionPointcutTest() throws NoSuchMethodException {
AspectJExpressionPointcut pointcut =
new AspectJExpressionPointcut("execution(* com.snail.springframework.beans.factory.bean.Cat.*(..))");
Class<Cat> clazz = Cat.class;
Method method = clazz.getDeclaredMethod("toString");
System.out.println(pointcut.matches(clazz));
System.out.println(pointcut.matches(method, clazz));
}
2
3
4
5
6
7
8
9
10
- 测试结果
true
true
Process finished with exit code 0
2
3
4
# 封装动态代理
动态代理的实现肯定是代理切面表达式匹配到的方法,所以我们实现一个动态代理需要的核心三元素就是:
- MethodMatcher 方法匹配器
- TargetSource 被代理对象
- MethodInterceptor 方法拦截器,拦截方法实现类,由用户自己实现
我们要做的就是把这些封装简化,对用户而言只需要一个 AopProxy#getProxy
获取代理对象。
# 定义一些职责和能力
AopProxy
AopProxy 接口是实现 AOP 的关键,它用于创建一个代理对象来拦截目标对象的方法调用,并将增强逻辑嵌入到方法调用过程中。通过 AopProxy,可以将任何普通的 Java 对象转换成带有切面的代理对象,从而实现 AOP 功能。
Spring 提供了多种类型的 AopProxy 实现,包括 JDK 动态代理和 CGLIB 代理。在使用 Spring 进行 AOP 编程时,可以根据需要选择不同的代理实现来生成代理对象。
public interface AopProxy {
/**
* 获得代理
*
* @return {@link Object}
*/
Object getProxy();
}
2
3
4
5
6
7
8
9
10
# 具体的实现
- 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 target;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
AdvisedSupport
主要是用于把代理、拦截、匹配的各项属性包装到一个类中,方便在 AopProxy 实现类进行使用。
public class AdvisedSupport {
/**
* 被代理对象
*/
private TargetSource targetSource;
/**
* 方法拦截器,拦截方法实现类,由用户自己实现
*/
private MethodInterceptor methodInterceptor;
/**
* 方法匹配器(切面表达式)
*/
private MethodMatcher methodMatcher;
public TargetSource getTargetSource() {
return targetSource;
}
public void setTargetSource(TargetSource targetSource) {
this.targetSource = targetSource;
}
public MethodInterceptor getMethodInterceptor() {
return methodInterceptor;
}
public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}
public MethodMatcher getMethodMatcher() {
return methodMatcher;
}
public void setMethodMatcher(MethodMatcher methodMatcher) {
this.methodMatcher = methodMatcher;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
ReflectiveMethodInvocation
ReflectiveMethodInvocation 是 Spring AOP 中实现 MethodInvocation 接口的一个具体类,用于封装方法调用信息并提供一些便捷的操作方法。在 AOP 操作中,ReflectiveMethodInvocation 负责将目标对象的方法调用转发到代理对象,并提供了运行时切面通知所需的 Method 对象、参数,以及目标对象等信息。
public class ReflectiveMethodInvocation implements MethodInvocation {
/**
* 目标
*/
protected final Object target;
/**
* 方法
*/
protected final Method method;
/**
* 参数
*/
protected final Object[] arguments;
public ReflectiveMethodInvocation(Object target, Method method, Object[] arguments) {
this.target = target;
this.method = method;
this.arguments = arguments;
}
@Override
public Method getMethod() {
return method;
}
@Override
public Object[] getArguments() {
return arguments;
}
@Override
public Object proceed() throws Throwable {
return method.invoke(target, arguments);
}
@Override
public Object getThis() {
return target;
}
@Override
public AccessibleObject getStaticPart() {
return method;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 将实现加入原有的逻辑中
这里没有原先的逻辑,只是将上述的实现整合加入不同的动态代理实现类中。
JdkDynamicAopProxy
JdkDynamicAopProxy 是 Spring AOP 中的一个核心类,实现了 AopProxy 接口和 jdk 代理的 InvocationHandler 接口,用于创建使用 JDK 动态代理技术实现的 AOP 代理对象。JDK 动态代理只能代理接口,因此 JdkDynamicAopProxy 只能为实现了接口的目标对象创建代理。
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
private final AdvisedSupport advised;
public JdkDynamicAopProxy(AdvisedSupport advised) {
this.advised = advised;
}
@Override
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
advised.getTargetSource().getTargetClass(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 获取被代理的类
Object targetObject = advised.getTargetSource().getTarget();
// 2. 获取表达式匹配器
MethodMatcher methodMatcher = advised.getMethodMatcher();
// 3. 获取用户自己实现的方法拦截器
MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
// 4. 符合指定的切面规则的话,invoke 3.用户自己实现的方法拦截器
if (methodMatcher.matches(method, targetObject.getClass())) {
return methodInterceptor.invoke(new ReflectiveMethodInvocation(targetObject, method, args));
}
return method.invoke(targetObject, args);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Cglib2AopProxy
Cglib2AopProxy 是 Spring AOP 中基于 CGLIB 技术实现的代理对象创建类,实现了 AopProxy 接口。与 JDK 动态代理不同,CGLIB 可以为没有实现接口的类生成代理,因此 Cglib2AopProxy 可以为任何类创建代理。
public class Cglib2AopProxy implements AopProxy {
private final AdvisedSupport advised;
public Cglib2AopProxy(AdvisedSupport advised) {
this.advised = advised;
}
@Override
public Object getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass());
enhancer.setInterfaces(advised.getTargetSource().getTargetClass());
enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
return enhancer.create();
}
private static class DynamicAdvisedInterceptor implements MethodInterceptor {
private final AdvisedSupport advised;
private DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object targetObject = advised.getTargetSource().getTarget();
MethodMatcher methodMatcher = advised.getMethodMatcher();
org.aopalliance.intercept.MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
CglibMethodInvocation methodInvocation = new CglibMethodInvocation(targetObject, method, args, proxy);
if (methodMatcher.matches(method, targetObject.getClass())) {
return methodInterceptor.invoke(methodInvocation);
}
return methodInvocation.proceed();
}
}
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
private final MethodProxy methodProxy;
public CglibMethodInvocation(Object target, Method method, Object[] arguments, MethodProxy methodProxy) {
super(target, method, arguments);
this.methodProxy = methodProxy;
}
@Override
public Object proceed() throws Throwable {
return methodProxy.invoke(getThis(), getArguments());
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 测试
xml 准备,无。
提前准备
自定义 aop 拦截后的处理类:TigerInterceptor
public class TigerInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("~方法执行前~");
Object result = invocation.proceed();
ThreadUtil.sleep(100);
System.out.println("~方法执行后~ 返回结果:" + result);
return result;
}
}
2
3
4
5
6
7
8
9
10
11
12
- 测试类
@Test
public void aopTest() throws NoSuchMethodException {
AspectJExpressionPointcut pointcut =
new AspectJExpressionPointcut("execution(* com.snail.springframework.beans.factory.bean.Animal.*(..))");
Tiger tiger = new Tiger();
tiger.setName("[11 hu~]");
System.out.println("tiger:" + tiger.getAnimalName());
System.out.println("------------------");
AdvisedSupport advised = new AdvisedSupport();
advised.setMethodMatcher(pointcut);
advised.setTargetSource(new TargetSource(tiger));
advised.setMethodInterceptor(new TigerInterceptor());
Animal jdkProxy = (Animal) new JdkDynamicAopProxy(advised).getProxy();
tiger.setName("[12 hu~ jdkProxy]");
System.out.println("jdk aop:" + jdkProxy.getAnimalName());
System.out.println("------------------");
Animal cglibProxy = (Animal) new Cglib2AopProxy(advised).getProxy();
tiger.setName("[13 hu~ cglibProxy]");
System.out.println("cglib aop:" + cglibProxy.getAnimalName());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- 测试结果
tiger:[11 hu~]
------------------
~方法执行前~
~方法执行后~ 返回结果:[12 hu~ jdkProxy]
jdk aop:[12 hu~ jdkProxy]
------------------
~方法执行前~
~方法执行后~ 返回结果:[13 hu~ cglibProxy]
cglib aop:[13 hu~ cglibProxy]
Process finished with exit code 0
2
3
4
5
6
7
8
9
10
11
# 总结
AOP(面向切面编程)代理的实现可以分为两类:基于 JDK 动态代理和基于 CGLIB 技术。
基于 JDK 动态代理的实现是通过实现 InvocationHandler 接口以及调用 Proxy.newProxyInstance 方法来创建代理对象。JDK 动态代理只能代理接口,因此需要为目标对象接口定义一个代理对象,并实现 InvocationHandler 的 invoke 方法来实现增强逻辑。JDK 动态代理的优点是速度相对较快,缺点则是只能代理接口,无法代理类。
基于 CGLIB 技术的实现是通过继承 Enhancer 类并重写 CallbackFilter 和 intercept 方法来创建代理对象。CGLIB 可以为没有实现接口的类生成代理,因此可以为任何类创建代理。在 CGLIB 实现中,可以使用 MethodInterceptor 将代理对象、目标对象和方法参数打包成 MethodInvocation 对象,并通过该对象调用目标方法并应用切面增强逻辑。CGLIB 的优点是可以代理任意类,缺点则是相对 JDK 动态代理速度稍慢。
在 Spring AOP 中,还有一些其它的类和接口,比如 ReflectiveMethodInvocation、AopProxy、AdvisedSupport 等,它们与代理对象创建密切相关。ReflectiveMethodInvocation 类用于封装方法调用信息并提供一些便捷的操作方法,AopProxy 接口用于定义获取代理对象的方法,AdvisedSupport 类用于保存 AOP 配置信息。这些类和接口配合使用,可以方便地创建代理对象,并将切面逻辑应用到目标对象的方法调用中。
总之,AOP 代理是 Spring AOP 的核心功能之一,它可以帮助程序员将切面逻辑与目标对象的业务逻辑分离,从而实现更加灵活、高效的应用开发。无论是基于 JDK 动态代理还是基于 CGLIB 技术实现,都能够通过 Spring 框架提供的类和接口来快速创建代理对象,并将切面逻辑应用到目标对象的方法调用中。
打赏一下
「真诚赞赏,手留余香」
# 打赏记录
打赏者 | 打助金额 (元) | 支付方式 | 时间 | 备注 |
---|---|---|---|---|
John | 12 | 微信 | 2020-06-09 | tip of you |
艾斯 | 32 | 支付宝 | 2020-07-11 | 火拳赞赏 |
HickSalmon | 15 | 微信 | 2020-09-21 | 有赏交流 |