文章目录
一、简介
1、介绍
2、代理模式在Java中的应用
- 统一异常处理。
- Mybatis使用了代理。
- Spring aop实现代理。
- 日志框架。
二、实现方式
1、静态代理
在Java中,静态代理是一种代理模式,它涉及为另一个对象(目标对象)提供一个代理对象,并由代理对象控制对目标对象的访问。这种代理模式在编译时就已经确定代理类,因此被称为"静态代理"。
静态代理通常包含以下角色:
- 目标对象(Target):需要被代理的真实对象。
- 代理对象(Proxy):持有目标对象的引用,并控制对目标对象的访问。代理对象与目标对象实现了相同的接口(或在Java中,它们可能是同一个类的子类)。
- 接口(Interface):定义了目标对象和代理对象都需要实现的方法。
静态代理的基本实现步骤:
- 定义一个接口,该接口声明了目标对象与代理对象共有的方法。
- 创建目标对象,实现接口并定义真实业务逻辑。
- 创建代理对象,同样实现该接口,并在方法内部调用目标对象的方法,同时可以在调用前后添加额外的逻辑。
- 客户端代码通过代理对象来调用目标对象的方法。
示例代码:
// 接口
public interface UserService {
void doSomething();
}
// 目标对象
public class UserServiceImpl implements UserService {
@Override
public void doSomething() {
System.out.println("执行真正的业务逻辑");
}
}
// 代理对象
public class UserServiceProxy implements UserService {
private UserService userService;
public UserServiceProxy(UserService userService) {
this.userService = userService;
}
@Override
public void doSomething() {
System.out.println("在目标方法执行前执行的逻辑");
userService.doSomething();
System.out.println("在目标方法执行后执行的逻辑");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserService proxy = new UserServiceProxy(userService);
proxy.doSomething(); // 调用代理对象的方法,实际上执行了目标对象的方法,并添加了额外逻辑
}
}
静态代理灵活性较差,每当接口增加或修改方法时,代理类也需要相应地进行修改。对于不同的接口,需要编写不同的代理类,这可能导致"类爆炸"的问题。
2、动态代理
动态代理 是一种在运行时动态地创建代理类及其实例的技术。与静态代理不同,动态代理不需要手动编写代理类的代码,而是在运行时根据目标对象的接口或父类动态地生成代理类的字节码,并加载到JVM中,然后创建代理类的实例。这种代理方式的优点在于其高度的灵活性和可扩展性。
在Java中,实现动态代理主要有两种方式:
- JDK动态代理:基于Java的反射机制实现。JDK动态代理要求目标对象必须实现一个或多个接口,因为代理类是通过继承java.lang.reflect.Proxy类并实现与目标对象相同的接口来创建的。因此,JDK动态代理主要用于对接口进行代理。
- CGLIB动态代理:基于ASM(一个通用的Java字节码操作和分析框架)库实现对类的字节码操作。与JDK动态代理不同,CGLIB动态代理是通过继承目标类来创建代理类的,因此它主要用于对没有实现接口的类进行代理。由于CGLIB是通过继承目标类来创建代理的,因此不能代理final类(因为final类不能被继承),同时目标类中的final方法也会被忽略(因为final方法不能被重写)。
动态代理的主要应用场景包括AOP(面向切面编程)、远程方法调用(RMI)等。在这些场景中,动态代理可以方便地为目标对象添加额外的功能(如日志记录、性能监控、事务管理等),而无需修改目标对象的代码。
三、动态代理
1、JDK
2、Cglib
CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,它可以在运行时扩展Java类和实现接口,而无需修改字节码。CGLIB被广泛用于AOP(面向切面编程)框架中,如Spring AOP,以在运行时动态地创建代理对象。
CGLIB与JDK动态代理的区别:
- JDK动态代理:基于接口实现,代理类必须是某个接口的实现。通过反射机制生成代理类的class字节码,并将其加载到Java虚拟机中。然后基于该接口动态生成代理实例,在调用具体方法前调用InvokeHandler来处理。
- CGLIB动态代理:基于类实现,代理类继承被代理类(通过字节码技术继承)。CGLIB动态代理利用ASM库来转换字节码并生成新的类,比使用Java反射API生成代理类的速度更快。
CGLIB动态代理的工作原理:
- 创建Enhancer对象:Enhancer是CGLIB的核心类,用于创建一个子类对象(即代理对象)。
- 设置回调函数:通过Enhancer的setCallback()或setCallbacks()方法设置当调用代理对象方法时被调用的方法。
- 创建代理对象:通过Enhancer的create()方法创建代理对象。
- 调用代理对象的方法:当调用代理对象的方法时,会先调用Enhancer中设置的回调函数(Callback),然后再调用被代理对象的方法。
CGLIB动态代理的示例:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyDemo {
public static void main(String[] args) {
// 创建Enhancer对象,用于创建目标类的子类
Enhancer enhancer = new Enhancer();
// 设置父类,即目标类
enhancer.setSuperclass(RealSubject.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;
}
});
// 创建代理对象
RealSubject proxyObj = (RealSubject) enhancer.create();
// 调用代理对象的方法
proxyObj.doSomething();
}
// 目标类
static class RealSubject {
public void doSomething() {
System.out.println("RealSubject: doSomething");
}
}
}
在这个示例中,我们使用了CGLIB来创建一个RealSubject类的子类(即代理类),并在调用doSomething()方法时添加了额外的操作(打印方法调用前后的日志)。