在 Java Spring 框架中,AOP (面向切面编程) 的代理实现主要通过以下几种方式:
JDK 动态代理:
- JDK 动态代理是基于 Java 的
java.lang.reflect.Proxy
类实现的.- 当需要代理的目标对象实现了一个或多个接口时,Spring 会选择使用 JDK 动态代理.
- JDK 动态代理的工作原理如下:
- 首先,Spring 会为目标对象的接口创建一个动态代理类.
- 这个动态代理类会实现目标对象的所有接口,并且包含与目标对象相同的方法签名.
- 在动态代理类的方法实现中,会首先执行切面逻辑,然后再调用目标对象的对应方法.
- 最后,Spring 会在运行时生成动态代理类的实例,并将其作为目标对象的代理使用.
- 使用 JDK 动态代理的优点是无需依赖第三方库,可以在纯 Java 环境下运行.但缺点是只能代理接口,不能代理具体的实现类.
CGLIB 代理:
- CGLIB (Code Generation Library) 是一个第三方字节码生成库.
- 当需要代理的目标对象没有实现任何接口时,Spring 会选择使用 CGLIB 代理.
- CGLIB 代理的工作原理如下:
- CGLIB 会为目标对象生成一个子类.
- 在子类中,CGLIB 会重写目标对象的所有非 final 方法.
- 在这些重写方法的实现中,CGLIB 会先执行切面逻辑,然后再调用父类(目标对象)的对应方法.
- 最后,Spring 会使用 CGLIB 生成的子类作为目标对象的代理.
- 使用 CGLIB 代理的优点是可以代理具体的实现类,而不仅限于接口.但缺点是需要依赖第三方库,并且对于 final 方法无法进行代理.
AspectJ 编译时织入:
- 除了 JDK 动态代理和 CGLIB 代理,Spring 还支持使用 AspectJ 进行编译时织入.
- 在编译阶段,AspectJ 会将切面逻辑织入到目标类中,生成包含切面逻辑的字节码文件.
- 这种方式的优点是执行效率高,但需要在编译时进行特殊处理.
Spring 会根据目标对象的情况自动选择合适的代理方式。如果目标对象实现了接口,则使用 JDK 动态代理;否则使用 CGLIB 代理。开发者也可以通过配置显式指定使用哪种代理方式。
在实现 AOP 功能时,Spring 会在运行时动态地为目标对象生成代理对象,并在代理对象中注入切面逻辑。这些代理对象会替代原始的目标对象,从而实现在不修改目标对象源码的情况下添加切面功能。
JDK 动态代理示例
假设有一个接口 HelloService
,以及它的实现类 HelloServiceImpl
:
public interface HelloService {
void sayHello();
}
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello() {
System.out.println("Hello from HelloServiceImpl!");
}
}
使用 JDK 动态代理来创建 HelloService
的代理对象:
public class JdkProxyExample {
public static void main(String[] args) {
// 创建目标对象
HelloService helloService = new HelloServiceImpl();
// 创建 InvocationHandler 实现类
InvocationHandler handler = (proxy, method, args1) -> {
System.out.println("Before method invocation");
method.invoke(helloService, args1);
System.out.println("After method invocation");
return null;
};
// 使用 Proxy 生成动态代理对象
HelloService proxy = (HelloService) Proxy.newProxyInstance(
HelloService.class.getClassLoader(),
new Class[]{HelloService.class},
handler
);
// 调用代理对象的方法
proxy.sayHello();
}
}
在这个示例中:
- 创建了一个
InvocationHandler
实现类,它包含了切面逻辑(打印方法调用前后的信息)。- 然后使用
Proxy.newProxyInstance()
方法创建了HelloService
的动态代理对象。- 最后,我们调用代理对象的
sayHello()
方法,它会先执行切面逻辑,然后再调用目标对象的对应方法。
CGLIB 代理示例
假设有一个 HelloServiceImpl
类,但它没有实现任何接口:
public class HelloServiceImpl {
public void sayHello() {
System.out.println("Hello from HelloServiceImpl!");
}
}
使用 CGLIB 代理来创建 HelloServiceImpl
的代理对象:
public class CglibProxyExample {
public static void main(String[] args) {
// 创建 CGLIB 增强器
Enhancer enhancer = new Enhancer();
// 设置需要代理的目标类
enhancer.setSuperclass(HelloServiceImpl.class);
// 设置回调函数
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method invocation");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method invocation");
return result;
}
});
// 生成代理对象
HelloServiceImpl proxy = (HelloServiceImpl) enhancer.create();
// 调用代理对象的方法
proxy.sayHello();
}
}
在这个示例中:
- 创建了一个 CGLIB 的
Enhancer
对象,并设置需要代理的目标类HelloServiceImpl
。- 然后,我们设置了一个
MethodInterceptor
回调函数,它包含了切面逻辑(打印方法调用前后的信息)。- 最后,我们使用
Enhancer.create()
方法生成了HelloServiceImpl
的代理对象,并调用了它的sayHello()
方法。
在这个示例中,CGLIB 动态生成了 HelloServiceImpl
的子类,并在子类中重写了 sayHello()
方法,在方法实现中添加了切面逻辑。
总的来说,JDK 动态代理和 CGLIB 代理都是通过动态生成代理类的方式,在不修改目标对象源码的情况下,实现了切面功能的添加。开发者可以根据实际需求选择合适的代理方式。
标签:--,Spring,09,代理,HelloServiceImpl,对象,切面,CGLIB,动态 From: https://blog.csdn.net/m0_72328778/article/details/140536311