Spring AOP的实现机制主要基于动态代理。当目标对象实现了接口时,Spring AOP使用JDK动态代理来创建代理对象;当目标对象没有实现接口时,Spring AOP使用CGLIB动态代理来创建代理对象。
JDK动态代理
JDK动态代理是Java标准库提供的一种动态代理实现方式。它通过在运行时生成目标对象的代理对象,并实现目标对象所实现的接口来实现代理。在Spring AOP中,JDK动态代理是基于Java反射机制实现的,它能够拦截目标对象方法的调用并在方法执行前后插入通知逻辑。
JDK动态代理是基于接口的代理方式,它要求目标对象必须实现接口。这是因为JDK动态代理是通过生成目标对象的接口的实现类来创建代理对象的。当我们为一个类创建代理对象时,JDK动态代理会在运行时创建一个实现了目标对象接口的代理类,并在代理类中重写接口中的方法来实现代理逻辑。因此,如果一个类没有实现接口,就无法通过JDK动态代理来创建代理对象。
核心类
java.lang.reflect.Proxy
java.lang.reflect.InvocationHandler
Proxy类是JDK动态代理的核心类之一。它提供了创建代理对象的静态方法 newProxyInstance() 用于创建一个实现了指定接口的代理类的实例。
InvocationHandler接口是JDK动态代理的另一个核心部分,它负责处理代理对象的方法调用。当我们调用代理对象的方法时,实际上会调用InvocationHandler的invoke()方法,并将调用的方法名、参数等信息传递给invoke()方法。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义一个接口
interface UserService {
void addUser(String username);
}
// 实现接口的目标类
class UserServiceImpl implements UserService {
public void addUser(String username) {
System.out.println("Adding user: " + username);
}
}
// 实现InvocationHandler接口的代理处理器类
class MyInvocationHandler implements InvocationHandler {
private Object target; // 目标对象
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args); // 调用目标对象的方法
System.out.println("After method: " + method.getName());
return result;
}
}
public class Main {
public static void main(String[] args) {
// 创建目标对象实例
UserService target = new UserServiceImpl();
// 创建代理处理器实例
InvocationHandler handler = new MyInvocationHandler(target);
// 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
// 调用代理对象的方法
proxy.addUser("Alice");
}
}
CGLIB动态代理
CGLIB动态代理是基于字节码生成技术实现的一种动态代理方式。它通过继承目标对象并重写其方法来实现代理。在Spring AOP中,当目标对象没有实现接口时,Spring会使用CGLIB动态代理来创建代理对象。与JDK动态代理相比,CGLIB动态代理能够代理没有实现接口的类。
注意:因为是继承目标对象重写其方法来实现代理,所以final类和final方法时无法被代理的。
同理,静态方法和private方法也是无法被代理的。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 目标类,无需实现接口
class UserService {
public void addUser(String username) {
System.out.println("Adding user: " + username);
}
}
// 实现MethodInterceptor接口的代理拦截器
class MyMethodInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = proxy.invokeSuper(obj, args); // 调用目标对象的方法
System.out.println("After method: " + method.getName());
return result;
}
}
public class Main {
public static void main(String[] args) {
// 创建Enhancer对象
Enhancer enhancer = new Enhancer();
// 设置父类(目标类)
enhancer.setSuperclass(UserService.class);
// 设置回调对象(代理拦截器)
enhancer.setCallback(new MyMethodInterceptor());
// 创建代理对象
UserService proxy = (UserService) enhancer.create();
// 调用代理对象的方法
proxy.addUser("Alice");
}
}
具体选择
Spring中关于JDK动态代理和CGLIB动态代理的选择是在org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy 进行判断的。
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
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.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
可以看到,Spring中默认使用JDK的动态代理;如果配置了优化选项、强制使用代理类或者没有用户提供的代理接口,则根据目标类是否为接口、是否为JDK代理类、是否为Lambda类来决定使用 JDK 动态代理还是 CGLIB 代理。如果目标类是接口或者 JDK 代理类,则使用 JDK 动态代理;否则,使用 CGLIB 代理。
文章标签
冬眠
博主专注于技术、阅读与思考。在这里记录学习、思考与生活。
第 1 篇,共 4 篇
