一、代理模式
// Printer.java
public class Printer {
public void print() {
System.out.println("打印输出");
}
}
当我们想在调用 print 之前或之后,添加一个日志该怎么做?
1.1、简单粗暴
public class Printer {
public void print() {
System.out.println("打印前记录日志...");
System.out.println("打印输出");
}
}
这样看上去好像没有问题,但是,我们修改了源码,破坏了OOP开闭原则,还可能影响到其它功能,怎么办?
1.2、创建新类并继承
// LogPrinter.java
public class LogPrinter extends Printer {
@Override
public void print() {
System.out.println("打印前记录日志...");
super.print();
}
}
嗯...这样看起来还不错...我们可以进一步优化一下:将 LogPrinter 与 Printer 解耦!
二、静态代理模式
2.1、抽象接口
// IPrinter.java
public interface IPrinter {
void print();
}
2.2、修改Printer类
public class Printer implements IPrinter {
@Override
public void print() {
System.out.println("打印输出");
}
}
2.3、Printer代理类
public class PrinterProxy implements IPrinter {
private IPrinter printer;
public PrinterProxy(IPrinter printer) {
this.printer = printer;
}
@Override
public void print() {
System.out.println("打印前记录日志...");
printer.print();
}
}
2.4、测试
public class Main {
public static void main(String[] args) {
PrinterProxy printer = new PrinterProxy(new Printer());
printer.print();
}
}
// 结果:
打印前记录日志...
打印输出
以后我们可以直接实例化 PrinterProxy,并传递实际工作的对象实例就可以了。
但....这就满意了?完美了?
如果我需要对实际工作的对象的所有方法都加上『记录日志』,难道,接口类和代理类要重复N次?
3、动态代理模式
很多优秀的框架,如『Sprint AOP』、『Android Retrofit』都采用了动态代理模式。
动态代理模式:借助某种方式,统一代理目标中的所有方法,而不需要一个一个去添加接口并实现!
JDK提供了两个类来完美的帮助我们解决了该问题:
- InvocationHandler 接口类
package java.lang.reflect;
public interface InvocationHandler {
// proxy: 被代理的对象
// method: 调用被代理的对象的方法
// args: 调用方法所需的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
- Proxy 类
public class Proxy implements java.io.Serializable {
// 动态代理对象实例
protected InvocationHandler h;
// 该方法可以生成代理对象
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
}
3.1、动态代理类
接口 IPrinter 和类 Printer 保持不变!
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
public Object newProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this // 重点是第三个参数 this,需要实现 InvocationHandler 才行
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("打印前记录日志...调用的方法 => " + method.getName());
return method.invoke(target, args);
}
}
3.2、测试
public class Main {
public static void main(String[] args) {
IPrinter printer = (IPrinter) new DynamicProxy(new Printer()).newProxyInstance();
printer.print();
}
}
运行后的结果:
打印前记录日志...调用的方法 => print
打印输出
四、代理类 Proxy
4.1、newProxyInstance
public class Proxy implements java.io.Serializable {
private static final Class<?>[] constructorParams = { InvocationHandler.class };
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException {
// 不能为null
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
......
// Look up or generate the designated proxy class.
Class<?> cl = getProxyClass0(loader, intfs);
try {
// cons = public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
// 实际就是 protected Proxy(InvocationHandler h)
// 详见 4.3
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
......
return cons.newInstance(new Object[]{h});
} catch ......
}
}
4.2、getProxyClass0
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
// 方法数不能超过 65535
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 检查缓存中是否存在,否则将通过 ProxyClassFactory 来创建代理类
return proxyClassCache.get(loader, interfaces);
}
4.3、cons.newInstance
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
this.h = h;
}
}
网友评论