上一篇文章《探索微服务中的权限控制:一次线上问题排查的思考》中,我们聊到面向切面编程中的切点可以在指定方法执行前后添加代码块,用于权限的控制。在切点执行时,使用了动态代理生成了对应方法的代理类。今天我们就来剖析下切点执行之前,动态代理是如何生成指定方法的代理的。
什么是动态代理?
动态代理(Dynamic Proxy)是一种在程序运行时动态创建代理对象的机制,它能够在不修改原始类代码的基础上,对目标对象的方法调用进行拦截、增强或修改等操作。
Java中动态代理有哪几种实现方式?
在 Java 中,动态代理主要有以下两种常见的实现方式,我们分别来介绍。
1. 基于Java原生反射机制的动态代理
这种方法也叫基于接口的代理,依托于 java.lang.reflect 包下的相关类和接口来实现,主要用于代理实现了接口的类,其核心是 Proxy 类和 InvocationHandler 接口。
2. 基于CGlib(Code Generation Library)的动态代理
这种方式也叫基于类(继承)的代理。依托CGLib,一个功能很强大的第三方代码生成库,它基于字节码操作技术,通过在运行时动态生成目标类的子类来实现动态代理,这种方式不仅可以代理实现了接口的类,更重要的是能够代理那些没有实现接口的普通类,其核心是Enhancer 类和MethodInterceptor 接口。
后续会专门出博客以最佳实战的方式来剖析这两者的源码
切点的动态代理使用了哪一种?
先说结论,切点的动态代理使用了第一种方式,即基于Java原生反射机制的动态代理。由上文可知,第一种动态代理的实现方式主要用于代理实现了接口的类,那么我们来看实现了JoinPoint接口的类是那个?答案是MethodInvocationProceedingJoinPoint类,即MethodInvocationProceedingJoinPoint作为被代理的类。
核心实现解析
MethodInvocationProceedingJoinPoint类主要用于在Spring AOP中基于方法调用的切面场景中,承载着方法调用相关的各种关键信息以及提供了执行目标方法和控制方法调用流程的能力。
public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart {
private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
// 与代理方法调用相关的对象
private final ProxyMethodInvocation methodInvocation;
// 方法参数
private Object[] args;
// 方法签名
private Signature signature;
private SourceLocation sourceLocation;
// ... 其他字段和方法 ...
}
MethodInvocationProceedingJoinPoint类中有个methodInvocation属性,它定义了与代理方法调用相关的一系列操作和属性访问方法,它抽象了在 Spring AOP 通过动态代理拦截目标方法调用时涉及到的核心行为和信息获取功能,是连接 Spring AOP 底层代理机制与上层切面逻辑操作的重要纽带,为切面逻辑在处理方法调用这个连接点(JoinPoint)时提供了统一的交互规范。
核心方法解析
下面,我们来详细介绍 MethodInvocationProceedingJoinPoint 类的核心方法及其作用:
MethodInvocationProceedingJoinPoint类核心方法有以下几个:
- proceed() 方法
- 作用:用于触发目标方法的实际执行。在切面逻辑中,尤其是环绕通知(@Around 注解标记的切面逻辑)里经常会用到它。
- 实现:先克隆当前的 methodInvocation 对象(通过 invocableClone() 方法),然后调用克隆后的对象的 proceed() 方法来启动目标方法的调用流程,克隆操作有助于保证每次执行的独立性以及便于 Spring AOP 对每次调用进行准确管理。
public MethodInvocation invocableClone(Object... arguments) {
if (this.userAttributes == null) {
this.userAttributes = new HashMap();
}
try {
ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation)this.clone();
clone.arguments = arguments;
return clone;
} catch (CloneNotSupportedException var3) {
throw new IllegalStateException("Should be able to clone object of type [" + this.getClass() + "]: " + var3);
}
}
- getArgs() 方法
- 作用:用于获取方法调用时的实际参数数组。它采用了懒加载机制,即仅在首次调用该方法且内部缓存的参数数组 args 为空时,才从 methodInvocation 对象中克隆一份参数数组进行缓存并返回
- 实现
public Object[] getArgs() {
if (this.args == null) {
this.args = (Object[])this.methodInvocation.getArguments().clone();
}
return this.args;
}
- getSignature() 方法
- 作用:用于获取方法签名对象,同样采用懒加载方式,首次调用时会创建一个内部类 MethodSignatureImpl 的实例作为方法签名返回。方法签名包含了目标方法的诸多关键信息,像方法名、修饰符、参数类型、返回值类型、异常类型等。
- 实现
public Signature getSignature() {
if (this.signature == null) {
this.signature = new MethodSignatureImpl();
}
return this.signature;
}
- getThis() 方法
- 作用:返回代理对象本身。在 Spring AOP 中,客户端代码实际调用的是通过动态代理机制生成的代理对象的方法,而切面逻辑围绕着对这个代理对象方法调用的拦截和处理展开。
- 实现
public Object getThis() {
return this.methodInvocation.getProxy();
}
- getTarget() 方法
- 作用
用于获取目标对象,也就是真正提供业务逻辑的那个对象实例。 - 实现
public Object getTarget() {
return this.methodInvocation.getThis();
}
小结
通过上述方法,MethodInvocationProceedingJoinPoint 为我们提供了对代理方法调用的全面控制和访问能力。在切面开发中,我们可以使用这些方法来获取方法信息、参数、目标对象等,从而实现丰富的 AOP 功能。