设计模式-代理模式
代理模式定义
创建一个对象的代理,用以控制这个对象的访问
从类图我们可以看出,委托类(RealSubject)与代理类(Proxy)都需要实现同一个接口,他们有相同的方法(具有相同的行为),代理类引用了一个委托类的实例,代理类并不具体实现这个方法,而是直接调用委托类的方法,从而达成对委托类的代理。
静态代理
Subject类
public interface Subject {
void request();
}
RealSubject类
public class RealSubject implements Subject{
@Override
public void request() {
System.out.println("request");
}
}
Proxy类
public class Proxy implements Subject{
private RealSubject realSubject = new RealSubject();
@Override
public void request() {
System.out.println("增强功能");
realSubject.request();
System.out.println("增强功能");
}
}
Client类
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.request();
}
}
通过上面静态代理的例子可以看出:
- 代理类和委托类实现了相同的接口,会有大量的重复代码产生,如果接口增加一个方法,不仅委托类要实现这个方法,代理类也需要实现这个方法,这就增加了代码维护的复杂度。
- Proxy类只为RealSubject类提供了代理,如果需要代理其他类,那么就不能胜任了,需要再添加适用于其他类的代理类。
通过以上两点,可以看出静态代理的缺点,那么我们就需要动态代理来解决了。目前Java实现动态代理有两种方案,分别是JDK提供的实现方案和CGLIB,接下来我们分别了解这两种实现方案。
JDK动态代理
在上面的示例中,一个代理只能代理一种类型,而且是在编译期就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象
在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler
接口和 java.lang.reflect.Proxy
类的支持
public interface InvocationHandler {
/**
* proxy - 调用该方法的代理实例
* method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
* args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
public class Proxy implements java.io.Serializable {
/**
* loader - 类加载器来定义代理类
* interfaces - 代理类实现的接口列表
* h - 得到InvocationHandler接口的子类的实例
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
}
现在我们用JDK提供的动态代理方案来实现最开始的例子
DynamicProxyHandler类
public class DynamicProxyHandler implements InvocationHandler {
private Object targetObject;
public Object newProxyInstance(Object targetObject){
this.targetObject = targetObject;
/*
该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
*/
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
}
/**
*
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("增强功能");
Object invoke = method.invoke(targetObject, args);
System.out.println("增强功能");
return invoke;
}
}
Client类
public class Client {
public static void main(String[] args) {
DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler();
Subject subject = (Subject) dynamicProxyHandler.newProxyInstance(new RealSubject());
subject.request();
}
}
由此可以看到,通过动态代理,我们可以代理很多不同类型的对象,代理类是在程序运行过程中动态生成的,即使接口变化也不影响。
查看动态代理生成的class文件
如何看到生成的动态代理的class文件呢,如果能看到,能够方便我们更加清晰的理解。下面这段代码就可以把动态代理生成的class文件保存到本地。
// 获取动态代理类的class字节码
byte[] classFile = ProxyGenerator.generateProxyClass("Proxy0",RealSubject.class.getInterfaces());
// 在当前的工程目录下保存文件
FileOutputStream fos =new FileOutputStream("Proxy0.class");
fos.write(classFile);
fos.flush();
fos.close();
注意:ProxyGenerator类在JDK11更改为私有类,可以在JDK8中尝试
我们反编译生成的class文件
public final class Proxy0 extends Proxy implements Subject {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void request() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.zhaojun.sort.proxy.Subject").getMethod("request");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以发现,该类继承了Proxy,并且实现了我们自己定义的接口,和RealSubject类一样都是Subject的子类,然后我们看request方法,其中h,就是DynamicProxyHandler类。这样一看是不是就清楚多了呢。
CGLIB动态代理
我们发现JDK实现动态代理,需要委托类实现一个接口,生成的代理类也会实现相同的接口,从而具有了和委托类相同的“行为”,而对象的行为其实也可以在类中进行定义,我们可以通过继承委托类,实现委托类的公共方法,从而对方法进行增强。这就是CGLIB的大致思想。
要使用CGLIB首先要引入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
RealSubjectNoImplInterface类,与RealSubject相比,没有实现任何接口
public class RealSubjectNoImplInterface {
public void request() {
System.out.println("request");
}
}
DynamicProxyInterceptor类
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class DynamicProxyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("增强功能");
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("增强功能");
return invoke;
}
}
Client类
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubjectNoImplInterface.class);
enhancer.setCallback(new DynamicProxyInterceptor());
RealSubjectNoImplInterface subjectNoImplInterface = (RealSubjectNoImplInterface)enhancer.create();
subjectNoImplInterface.request();
}
}
区别
名称 | 区别 |
---|---|
静态代理 | 动态代理的理论基础。 |
JDK动态代理 | 需要有顶层接口才能使用(Java中类不能多继承),但是在只有顶层接口的时候也可以使用。使用反射完成。使用了动态生成字节码技术。 |
CGLIB动态代理 | 可以直接代理类,使用字节码技术,不能对 final类进行代理(Java语法决定,final类不能有子类)。使用了动态生成字节码技术。 |