在Spring框架中,动态代理主要分为两种类型:JDK动态代理和CGLIB动态代理。这两种代理机制在Spring的AOP(面向切面编程)和事务管理等功能中起到关键作用。
1.JDK动态代理
在Spring框架中,JDK动态代理主要用于实现AOP(面向切面编程),特别是在处理基于接口的代理时。
1. 实现原理
JDK动态代理利用了Java的反射机制。其核心是java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口。
-
Proxy类 :用于在运行时动态创建代理对象。它的
newProxyInstance
方法可以创建实现了指定接口的代理对象。 -
创建代理实例: 这个方法需要三个参数:
- 类加载器(ClassLoader):用于定义代理类。
- 接口数组(Class<?>[] interfaces):代理类需要实现的接口列表。
- 调用处理器(InvocationHandler):当代理对象的方法被调用时,将会执行调用处理器的
invoke
方法。
javapublic static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { // 核心逻辑:创建代理类并返回其实例
上边的核心逻辑补充:ProxyClassFactory是一个私有的静态内部类,位于Proxy类中。它的作用是生成代理类的字节码。它是通过Java的字节码操作完成的,这部分工作实际上是由Java核心库中的sun.misc.ProxyGenerator类完成的,ProxyClassFactory会为指定的接口生成一个新的类(代理类)。这个代理类会实现所有提供的接口,并将所有方法调用转发到一个InvocationHandler。
-
InvocationHandler接口 :用于定义代理对象的方法调用的行为。当代理对象的方法被调用时,实际上是调用
InvocationHandler
的invoke
方法。 调用处理逻辑: invoke`方法接收三个参数:- 代理对象(Object proxy):调用该方法的代理实例。
- 方法对象(Method method):对应于在代理实例上调用的接口方法。
- 参数数组(Object[] args):传递给方法的参数。
javapublic interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
2. 实际代码案例
假设你有一个接口和一个实现类,你想通过JDK动态代理来创建代理对象:
-
接口(MyService.java):
javapublic interface MyService { void performAction(); }
-
实现类(MyServiceImpl.java):
javapublic class MyServiceImpl implements MyService { @Override public void performAction() { System.out.println("Action performed"); } }
-
创建代理:
javapublic class MyServiceProxy { public static void main(String[] args) { MyService original = new MyServiceImpl(); MyService proxy = (MyService) Proxy.newProxyInstance( MyService.class.getClassLoader(), new Class[]{MyService.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 添加代理逻辑(例如日志记录、安全检查等) System.out.println("Before invoking " + method.getName()); // 调用实际对象的方法 Object result = method.invoke(original, args); System.out.println("After invoking " + method.getName()); return result; } }); // 使用代理对象 proxy.performAction(); } }
在上述示例中,MyServiceProxy
类创建了MyService
的一个代理实例。在代理实例上调用performAction
方法时,实际上会先执行InvocationHandler
的invoke
方法,然后才调用真实对象MyServiceImpl
的performAction
方法。
2.CGLIB动态代理
在Spring Boot中,CGLIB(Code Generation Library)动态代理是一种基于类的代理机制,用于创建目标类的子类作为代理。它在代理没有实现接口的类或者需要通过类代理而非接口代理的场景中特别有用。
1. 实现原理
CGLIB代理通过在运行时动态生成被代理类的子类来实现代理功能。它不依赖于接口。
-
Enhancer类 :CGLIB的核心是
Enhancer
类,用于生成代理对象。它可以设置超类(被代理的类),并将方法调用重定向到MethodInterceptor
。 -
Enhancer的创建过程:
javaEnhancer enhancer = new Enhancer(); enhancer.setSuperclass(MyClass.class); // 设置被代理类 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 在调用方法前后可以执行的代码 return proxy.invokeSuper(obj, args); // 调用原始对象的方法 } }); MyClass proxy = (MyClass) enhancer.create(); // 创建代理对象
上边的创建代理对象补充:CGLIB在运行时使用ASM(一个Java字节码操作和分析框架)动态生成代理类的字节码,并加载到JVM中。生成的字节码实际上扩展了被代理类,并在运行时作为该类的子类实例化。
-
MethodInterceptor接口 :这是一个回调接口,用于拦截对原始类方法的调用。通过实现
MethodInterceptor
,可以在方法调用前后添加自定义行为。 -
MethodInterceptor:
javapublic interface MethodInterceptor extends Callback { Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable; }
2. 实际代码案例
假设有一个没有实现任何接口的类,你想通过CGLIB动态代理来创建代理对象:
-
目标类(MyClass.java):
javapublic class MyClass { public void performAction() { System.out.println("Action performed in MyClass"); } }
-
创建代理:
javaimport net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class MyClassCglibProxy { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MyClass.class); enhancer.setCallback(new MethodInterceptor() { @Override 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; } }); MyClass myClassProxy = (MyClass) enhancer.create(); myClassProxy.performAction(); } }
在上述示例中,MyClassCglibProxy
类使用Enhancer
创建了MyClass
的代理。代理对象的performAction
方法被调用时,实际上会先执行MethodInterceptor
的intercept
方法,然后再调用真实对象的performAction
方法。
3.在Spring Boot中的使用动态代理
在Spring Boot中,当使用@Transactional
注解时,Spring Boot框架会根据目标类是否实现了接口自动选择使用JDK动态代理还是CGLIB动态代理。这一过程涉及到Spring AOP的配置和代理创建机制。
1. 引入依赖
首先,确保Spring Boot项目中包含了事务管理和Spring AOP的依赖:
xml
<!-- Spring Boot Starter AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Spring Boot Starter Data JPA (如果使用JPA) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
2. 使用@Transactional
在服务类或方法上使用@Transactional
注解。Spring将基于此注解创建代理来管理事务。
示例代码
假设有一个服务接口和一个实现类:
java
public interface MyService {
void performAction();
}
@Service
public class MyServiceImpl implements MyService {
@Override
@Transactional
public void performAction() {
// 业务逻辑
}
}
3. 代理的选择
Spring Boot如何决定使用哪种代理方式:
- 如果目标类实现了接口 :Spring Boot默认使用JDK动态代理。在上面的例子中,由于
MyServiceImpl
实现了MyService
接口,Spring将默认使用JDK动态代理。 - 如果目标类没有实现接口 :Spring Boot将使用CGLIB代理。如果
MyServiceImpl
没有实现任何接口,Spring将自动使用CGLIB来创建代理。
4. 代理的创建过程
Spring使用ProxyFactory
来创建代理。这个过程涉及以下步骤:
- 检查目标类:确定目标类是否实现了接口。
- 选择代理策略:基于检查结果选择JDK代理或CGLIB代理。
- 配置代理 :根据
@Transactional
等注解配置代理,如拦截器TransactionInterceptor
,它管理事务的边界和行为。
5. 事务的管理
当调用被@Transactional
注解的方法时,事务拦截器会根据注解的配置管理事务。这包括:
- 开启新事务或加入现有事务。
- 在方法正常返回时提交事务。
- 在方法抛出异常时回滚事务。
4.总结
JDK动态代理适用于代理实现了接口的类,依赖Java自带的反射机制,而CGLIB代理可以代理没有实现接口的类,通过运行时生成类的子类来实现代理。JDK代理侧重于接口,CGLIB代理侧重于类本身。