代理模式(Proxy Design Pattern)的原理和代码实现都不难掌握。它在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。
什么时候使用接口,什么时候使用继承 参照基于接口而非实现编程的设计思想,将原始类对象替换为代理类对象的时候,为了让代码改动尽量少,在刚刚的代理模式的代码实现中,代理类和原始类需要实现相同的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的(比如它来自一个第三方的类库),我们也没办法直接修改原始类,给它重新定义一个接口。在这种情况下,对于这种外部类的扩展,我们一般都是采用继承的方式。这里也不例外。我们让代理类继承原始类,然后扩展附加功能。
直接上代码
interface IUserController
package proxy;
public interface IUserController {
void login(String telephone, String password);
void register(String telephone, String password);
}
class UserController
package proxy;
public class UserController implements IUserController {
public void login(String telephone, String password) {
// ... 省略login逻辑...
System.out.println("login ...");
}
public void register(String telephone, String password) {
// ... 省略register逻辑...
System.out.println("register ...");
}
}
class UserControllerProxy
package proxy;
import java.time.Instant;
public class UserControllerProxy implements IUserController {
private IUserController userController;
public UserControllerProxy(IUserController userController) {
this.userController = userController;
}
@Override
public void login(String telephone, String password) {
System.out.println("-----使用了普通代理-----");
long startTimestamp = Instant.now().toEpochMilli();
// 委托
userController.login(telephone, password);
long endTimeStamp = Instant.now().toEpochMilli();
long responseTime = endTimeStamp - startTimestamp;
System.out.println(responseTime);
}
@Override
public void register(String telephone, String password) {
System.out.println("-----使用了普通代理-----");
long startTimestamp = Instant.now().toEpochMilli();
userController.register(telephone, password);
long endTimeStamp = Instant.now().toEpochMilli();
long responseTime = endTimeStamp - startTimestamp;
System.out.println(responseTime);
}
}
所谓动态代理(Dynamic Proxy),就是我们不事先为每个原始类编写代理类,而是在运行的时候,动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。那如何实现动态代理呢?
示例代码
class UserControllerInvocationHandler
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.time.Instant;
public class UserControllerInvocationHandler implements InvocationHandler {
private Object proxiedObject;
public UserControllerInvocationHandler(Object proxiedObject) {
this.proxiedObject = proxiedObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----使用了动态代理-----");
Object invoke;
String methodName = method.getName();
if (methodName.equals("login") || methodName.equals("register")) {
long startTimestamp = Instant.now().toEpochMilli();
invoke = method.invoke(this.proxiedObject, args);
long endTimeStamp = Instant.now().toEpochMilli();
long responseTime = endTimeStamp - startTimestamp;
System.out.println(responseTime);
} else {
invoke = method.invoke(this.proxiedObject, args);
}
return invoke;
}
}
Main 调用类
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
IUserController controller = new UserController();
// 静态代理
IUserController controllerProxy = new UserControllerProxy(new UserController());
controllerProxy.register("lisi", "666666");
// 动态代理
ClassLoader loader = controller.getClass().getClassLoader();
Class<?>[] interfaces = controller.getClass().getInterfaces();
InvocationHandler handler = new UserControllerInvocationHandler(controller);
IUserController proxy = (IUserController) Proxy.newProxyInstance(loader, interfaces, handler);
proxy.register("wangwu", "111");
}
}
Proxy 只能对 interface 进行代理,无法实现对 class 的动态代理。观察动态生成的代理继承关系图可知原因,他们已经有一个固定的父类叫做 Proxy,Java语法限定其不能再继承其他的父类。要实现对 class 的动态代理,可以使用 CGLIB。
CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于 AOP 框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate 作为一个比较受欢迎的ORM框架,同样使用 CGLIB 来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。CGLIB 作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib
相关资料
设计模式之美设计模式代码重构-极客时间 https://time.geekbang.org/column/intro/250