6.1 静态代理模式
Java中的静态代理模式是一种相对简单的设计模式,它要求代理类和被代理类实现相同的接口或继承自相同的父类。代理类在内部持有被代理类的引用,并在需要时调用被代理类的方法,同时可以在调用前后添加额外的逻辑。
下面是一个简单的静态代理模式的实现:
首先,定义一个接口:
public interface Service {
void performTask();
}
然后,创建被代理类,实现该接口:
public class RealService implements Service {
@Override
public void performTask() {
System.out.println("RealService is performing the task.");
}
}
接下来,创建代理类,同样实现该接口,并在构造函数中接收一个被代理类的实例:
public class ProxyService implements Service {
private final Service realService;
public ProxyService(Service realService) {
this.realService = realService;
}
@Override
public void performTask() {
System.out.println("ProxyService: Before task.");
realService.performTask(); // 调用被代理类的方法
System.out.println("ProxyService: After task.");
}
}
最后,在客户端代码中使用代理类:
public class Client {
public static void main(String[] args) {
// 创建被代理对象
Service realService = new RealService();
// 创建代理对象,将被代理对象作为参数传入
Service proxyService = new ProxyService(realService);
// 通过代理对象执行方法,会触发代理类中的额外逻辑
proxyService.performTask();
}
}
当运行客户端代码时,输出将会是:
ProxyService: Before task.
RealService is performing the task.
ProxyService: After task.
在这个例子中,ProxyService
是代理类,它增强了RealService
的功能,即在执行任务前后输出了额外的日志信息。客户端代码通过代理类ProxyService
来调用performTask
方法,而不需要直接与被代理类RealService
交互。这种方式允许在不修改原始类的情况下增加新的行为或控制访问。
6.2 JDK动态代理的实现
JDK动态代理是Java提供的一种在运行时创建代理类和对象的方式。它主要利用java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来实现。下面是一个使用JDK动态代理模式的实现:
首先,定义一个接口:
public interface Service {
void performTask();
}
然后,创建被代理类,实现该接口:
public class RealService implements Service {
@Override
public void performTask() {
System.out.println("RealService is performing the task.");
}
}
接下来,创建一个实现了InvocationHandler
接口的类。这个类的invoke
方法会在代理对象的方法被调用时被执行:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ServiceInvocationHandler implements InvocationHandler {
private final Object target;
public ServiceInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoking method: " + method.getName());
Object result = method.invoke(target, args); // 调用被代理对象的方法
System.out.println("After invoking method: " + method.getName());
return result;
}
}
最后,在客户端代码中使用Proxy.newProxyInstance
方法创建代理对象,并通过该代理对象调用方法:
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
// 创建被代理对象
Service realService = new RealService();
// 创建InvocationHandler实例,将被代理对象传入
InvocationHandler handler = new ServiceInvocationHandler(realService);
// 使用Proxy.newProxyInstance创建代理对象
Service proxyService = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(), // 类加载器
new Class<?>[] { Service.class }, // 代理类要实现的接口列表
handler // 关联调用处理器
);
// 通过代理对象执行方法,会触发InvocationHandler中的invoke方法
proxyService.performTask();
}
}
当运行客户端代码时,输出将会是:
Before invoking method: performTask
RealService is performing the task.
After invoking method: performTask
在这个例子中,ServiceInvocationHandler
是实现了InvocationHandler
接口的调用处理器。当通过代理对象proxyService
调用performTask
方法时,实际上会调用ServiceInvocationHandler
中的invoke
方法。在invoke
方法内部,我们可以添加任何额外的逻辑,比如方法调用前后的日志输出、权限检查等。通过method.invoke(target, args)
语句,我们实际上是在调用被代理对象realService
的performTask
方法。
6.3 Cglib代理
cglib
是一个强大的、高性能的、高质量的代码生成库,它可以在运行时为 Java 类或接口生成子类或代理类。在 Java 代理模式中,当需要代理的类没有实现接口时,可以使用 cglib
来创建一个该类的子类作为代理。
要使用 cglib
,首先需要将其添加到项目的依赖中。如果你使用 Maven,可以在 pom.xml
文件中添加以下依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version> <!-- 请检查是否有更新的版本 -->
</dependency>
下面是一个使用 cglib
创建代理类的示例:
首先,定义一个普通的类(注意,这个类没有实现任何接口):
public class RealService {
public void performTask() {
System.out.println("RealService is performing the task.");
}
}
然后,创建一个实现了 MethodInterceptor
接口的类,该类将用于拦截对代理类方法的调用:
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class ServiceMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before invoking method: " + method.getName());
Object result = proxy.invokeSuper(obj, args); // 调用父类(被代理类)的方法
System.out.println("After invoking method: " + method.getName());
return result;
}
}
最后,在客户端代码中使用 Enhancer
类来创建代理对象:
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
// 创建被代理对象,实际上这里并不需要直接创建,因为cglib会创建它的子类
// RealService realService = new RealService();
// 创建Enhancer实例,并设置父类为RealService
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealService.class);
// 设置回调,即方法拦截器
enhancer.setCallback(new ServiceMethodInterceptor());
// 创建代理对象
RealService proxyService = (RealService) enhancer.create();
// 通过代理对象执行方法,会触发ServiceMethodInterceptor中的intercept方法
proxyService.performTask();
}
}
当运行客户端代码时,输出将会是:
Before invoking method: performTask
RealService is performing the task.
After invoking method: performTask
在这个例子中,ServiceMethodInterceptor
实现了 MethodInterceptor
接口,用于拦截对代理类方法的调用。在 intercept
方法中,我们可以添加任何额外的逻辑,比如方法调用前后的日志输出。通过 proxy.invokeSuper(obj, args)
语句,我们实际上是在调用被代理类 RealService
的 performTask
方法。Enhancer
类用于创建代理类,并通过 create
方法实例化代理对象。
6.4 三种实现的区别和优缺点
代理模式主要有三种实现方式(静态代理、JDK动态代理和cglib动态代理)中由于静态代理通常针对每个具体类编写,不具有通用性,因此这里主要讨论JDK动态代理和cglib动态代理的区别和优缺点。
6.4.1 JDK动态代理
实现方式:
JDK动态代理要求目标类必须实现至少一个接口。代理类是在运行时动态生成的,实现了目标类所实现的所有接口,并通过反射调用目标类的方法。
优点:
- 无需为每个目标类编写具体的代理类,提高了代码的复用性和可维护性。
- 由于代理类实现了接口,因此系统的耦合度较低,更加灵活。
缺点:
- 目标类必须实现至少一个接口,否则无法使用JDK动态代理。
- 在方法调用频繁的情况下,由于使用了反射机制,性能可能较低。
6.4.2 cglib动态代理
实现方式:
cglib动态代理是通过生成目标类的子类来实现的。它不需要目标类实现任何接口,而是通过继承目标类并重写其方法来创建代理类。在方法调用时,通过方法拦截器来增强方法的调用。
优点:
- 目标类无需实现任何接口,更加灵活。
- 在某些场景下,由于直接调用子类方法而无需经过反射,性能可能优于JDK动态代理。
缺点:
- 由于代理类是通过继承目标类来实现的,因此目标类和方法不能声明为final类型。
- 引入了额外的cglib库依赖,增加了项目的复杂性。
- 系统的耦合度可能较高,因为代理类与目标类是继承关系。
JDK动态代理和cglib动态代理各有优缺点,选择哪种方式取决于具体的需求和场景。如果目标类已经实现了接口,那么JDK动态代理是一个不错的选择。如果目标类没有实现接口,或者需要更高的性能,那么可以考虑使用cglib动态代理。不过,在使用cglib时需要注意目标类和方法不能声明为final类型,以及引入额外依赖的问题。