设计模式 - 代理模式
静态代理
静态代理其实代理类和被代理类实现同一个接口,然后将被代理类的实例通过构造函数传入代理类,在代理类中,调用接口上的方法时,去调用被代理类的同方法即可,在调用前、后、异常等场景可以加入你的代理逻辑。
如:
public class SimpleStaticProxyDemo {
static interface IService {
public void sayHello();
}
static class RealService implements IService {
@Override
public void sayHello() {
System.out.println("hello");
}
}
static class TraceProxy implements IService {
private IService realService;
public TraceProxy(IService realService) {
this.realService = realService;
}
@Override
public void sayHello() {
System.out.println("entering sayHello");
this.realService.sayHello();
System.out.println("leaving sayHello");
}
}
public static void main(String[] args) {
IService realService = new RealService();
IService proxyService = new TraceProxy(realService);
proxyService.sayHello();
}
}在这个例子中,IService 是公共接口,实际对象是 RealService,代理类为 TraceProxy,TraceProxy 内部持有一个 IService 类型的成员变量,指向实际对象,在构造函数中初始化,对于 sayHello 的调用,它转发给了实际对象,在调用前后输出了一些跟踪信息。
程序输出为:
entering sayHello
hello
leaving sayHello静态代理实现了代理模式的核心需求:"在不修改原类的情况下增强其功能" 不过,输出跟踪日志是一个很常见的需求,可能不止 RealService 需要,如果更多的类需要的话,就要为每个实际对象创建代理,代理要实现所有接口,整个过程非常繁琐。所以就有了动态代理。
动态代理 -JDK
和静态代理不一样,动态代理的代理类是动态生成的,如:
public class SimpleJDKDynamicProxyDemo {
static interface IService {
public void sayHello();
}
static class RealService implements IService {
@Override
public void sayHello() {
System.out.println("hello");
}
}
static class SimpleInvocationHandler implements InvocationHandler {
private Object realObj;
public SimpleInvocationHandler(Object realObj) {
this.realObj = realObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("entering " + method.getName());
Object result = method.invoke(realObj, args);
System.out.println("leaving " + method.getName());
return result;
}
}
public static void main(String[] args) {
IService realService = new RealService();
IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(),
new Class<?>[] { IService.class }, new SimpleInvocationHandler(realService));
proxyService.sayHello();
}
}只需要创建一个类,实现 InvocationHandler 接口的 invoke 方法即可,方法第一个参数是代理类,也就是生成的代理对象,第二个参数是调用的方法,第三个参数是方法参数。
那么如何调用这个它来生成代理类呢,使用 JDK 自带的 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
loader:类加载器,使用和实际类实现的接口的类加载器即可。interfaces:实际类实现的接口列表。h:就是上面我们实现InvocationHandler接口的实现类SimpleInvocationHandler。
JDK 动态代理的原理是自动创建一个类,实现的接口为上面的 interfaces 中的接口列表,也就是实际类实现的接口列表,生成的代理类代码大概如下:
final class $Proxy0 extends Proxy implements SimpleJDKDynamicProxyDemo.IService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject) {
return ((Boolean) this.h.invoke(this, m1,
new Object[] { paramObject })).booleanValue();
}
public final void sayHello() {
this.h.invoke(this, m3, null);
}
public final String toString() {
return (String) this.h.invoke(this, m2, null);
}
public final int hashCode() {
return ((Integer) this.h.invoke(this, m0, null)).intValue();
}
static {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("laoma.demo.proxy.SimpleJDKDynamicProxyDemo$IService")
.getMethod("sayHello",new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}
}这个类通过构造方法传入一个 InvocationHandler 类型的对象,调用所有方法时,包含 Object 中的 hashCode,equals 和 toString 方法,都会调用 InvocationHandler 的 invoke 方法,也就是这个类其实只做转发作用。
通过 JDK 的动态代理,可以不用再为每个实际类手动创建一个代理类了,上面我们是获取 IService 的一个代理类:
IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(),
new Class<?>[] { IService.class }, new SimpleInvocationHandler(realService));实际上我们也可以获取其他接口实现的代理类,如 IServiceA、IServiceB:
IServiceA proxyService = (IServiceA) Proxy.newProxyInstance(IServiceA.class.getClassLoader(),
new Class<?>[] { IServiceA.class }, new SimpleInvocationHandler(realService));IServiceB proxyService = (IServiceB) Proxy.newProxyInstance(IServiceB.class.getClassLoader(),
new Class<?>[] { IServiceB.class }, new SimpleInvocationHandler(realService));动态代理 - CGLIB
JDK 动态代理的局限是只能为接口创建代理,返回的代理类也只能转为某个接口实现类,如果一个类没有接口,或者向代理非接口中定义的方法,就没办法实现了。
cglib 使用其他方式避免了这些局限,如:
public class SimpleCGLibDemo {
static class RealService {
public void sayHello() {
System.out.println("hello");
}
}
static class SimpleInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("entering " + method.getName());
Object result = proxy.invokeSuper(object, args);
System.out.println("leaving " + method.getName());
return result;
}
}
@SuppressWarnings("unchecked")
private static <T> T getProxy(Class<T> cls) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(cls);
enhancer.setCallback(new SimpleInterceptor());
return (T) enhancer.create();
}
public static void main(String[] args) throws Exception {
RealService proxyService = getProxy(RealService.class);
proxyService.sayHello();
}
}注:上述代码引用了 cglib 的包,以 maven 为例:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>RealService 为被代理的类,它没有接口, getProxy 方法接收一个 Class 对象,方法返回一个代理类。
getProxy 方法中使用了 cglib 的 Enhancer 类,Enhancer 类的 superclass 设置成了被代理的类。setCallback 设置被代理类的 public 非 final 方法被调用时的处理类。上例用使用的处理类是 SimpleInterceptor 继承 MethodInterceptor 并实现 intercept 方法。
- 第一个参数是 object,是代理类对象。
- 第二个参数是 method,是被调用的方法。
- 第三个参数是 args,是被调用的方法参数。
- 第四个参数是 methodProxy,方法中使用它的 invokeSuper (object, args) 来调用被代理类的方法,注意别写成了 invoke (object, args),不然它调用的是代理类,然后又进入了这个方法,会无限循化。
与 JDK 动态代理不同的是,cglib 的用法,我们在 main 方法中也没有创建被代理类的对象,创建的对象直接就是代理对象。
实现原理:
cglib 不同于 jdk 动态代理,它是通过继承实现的,它是动态创建了一个类,但这个类的父类是被代理的类,代理类重写了父类的所有 public 非 final 方法,改为调用 callback 中的相关方法,在上例中,调用 SimpleInterceptor 的 intercept 方法。
比较
从原理上来看:
- JDK 动态代理面向的是一组接口,它为这些接口创建了一个实现类,接口的具体实现是通过自定义的 InvocationHandler 实现的,也就是说,一个代理类背后都不一定有真正被代理的对象,也可能有多个实际对象,根据情况动态选择。局限性是要求被代理类必须实现了接口。
- CGLIB 动态代理面向的是一个具体的类,它为这个类动态创建一个子类,父类所有 public 非 final 方法,然后在这些方法中调用 MethodInterceptor 的实现,这个实现中定义了代理逻辑。不要求代理类实现接口。
从代理的角度来看:
- JDK 动态代理:代理的是对象,需要首先有一个实际对象,然后使用自定义 InvocationHandler 引用该对象。
- CGLIB:只会创建代理类,所有的操作都是针对代理类,无需手动创建实际对象。
参考链接
- https://www.cnblogs.com/swiftma/p/6869790.html