代理模式
代理模式
代理模式介绍
为什么要用代理模式?
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问
在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用
代理模式种类
如果按照代理创建的时期来进行分类的话,可以分为两种:静态代理、动态代理。
静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。
动态代理是在程序运行时通过反射机制动态创建的。
代理模式的角色
抽象角色:声明真实对象和代理对象的共同接口:(租房子)
代理角色:代理对象内部含有真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便 在任何时刻都能代理真实对象。同时,代理对象可以在执行真实对象操作时,附加其他操作,相当于对真实对象进行封装:中介
真实角色:实现抽象角色,也就是定义真实角色所需要实现的业务逻辑(房主)
静态代理
抽象角色
//也就是需要为这个对象的进行代理,因为接口中有方法,接口又是由其他对象实现其中的方法,那么我们可以为这个方法增强一些功能
public interface Subject {
void request();
}
具体角色:实现抽象角色接口(或抽象类),也就是真正的实现逻辑,我们代理就是在这个实现逻辑的基础上增强一些功能
public class RealSubject implements Subject {
//在这里实现真正的逻辑
@Override
public void request() {
System.out.println("一个真实的角色");
}
}
代理对象:代理对象中肯定是需要具体角色的一个引用的,要不然没法操纵具体角色的
public class DynamicSubject implements Subject {
private RealSubject realSubject;
public DynamicSubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
System.out.println("代理前。。。。在这里可以干一些事情");
//通过具体角色的引用来操作具体角色
realSubject.request();
System.out.println("代理后。。。在这里也可以干一些事情");
}
}
存在的问题:
静态代理:真实角色必须实现已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决
动态代理(JDK代理)
InvocationHandler
/**
* {@code InvocationHandler} is the interface implemented by
* the <i>invocation handler</i> of a proxy instance.
* <p>Each proxy instance has an associated invocation handler.
* When a method is invoked on a proxy instance, the method
* invocation is encoded and dispatched to the {@code invoke}
* method of its invocation handler.
*/
//每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了
//实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会
//被转发到实现InvocationHandler接口类的invoke方法来调用
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
/**
关于Method中invoke方法
public Object invoke(Object obj, Object... args)
它在这里有两个版本的实现
1.最终调用的一个native方法invoke0,它在HotSpot JVM里调用JVM_InvokeMethod函数
2.Java版实现:这里运行了asm动态生成字节码技术
**/
Proxy
public class Proxy implements java.io.Serializable {
//获得一个代理类,其中loader时类加载,interfaces是真实类所拥有的全部接口的数组
public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)
//返回代理类的一个实例,返回后的代理类可以当作被代理类使用
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
}
抽象角色
public interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("一个真实的角色");
}
}
代理角色
/**
* 代理角色:肯定需要一个真实对象的引用,要不然怎么操作真实角色呢?
*/
public class DynamicSubject implements InvocationHandler {
//代理角色中含有真实角色
//这里为什么使用Object类型呢?为了避免为每一个真实对象创建一个代理对象,
// 这样的话我们可以传递任意的真实对象
private Object sub;
public DynamicSubject(Object sub) {
this.sub = sub;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before calling: " + method);
//通过反射技术,调用真实对象的方法
method.invoke(this.sub, args);
System.out.println("after calling: " + method);
return null;
}
}
Client
public class Client {
public static void main(String[] args) {
// System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
RealSubject rs = new RealSubject();
InvocationHandler ds = new DynamicSubject(rs);
Class<? extends RealSubject> cls = rs.getClass();
/**
//Generate the specified proxy class.
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
*/
Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(),ds);
//在这里会将其上交给InvocationHandler处理
subject.request();
System.out.println(subject.getClass()); //class com.sun.proxy.$Proxy0
}
}
从字节码分析JDK动态代理
在JDK1.8中对于基于JDK的动态代理代码有所修改,调用了函数式编程
//从Proxy的newProxyInstance开始
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h){
//。。。。
Class<?> cl = getProxyClass0(loader, intfs);
//。。。。
}
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces);
}
//最终DEBUG我们可以得到
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces){
/*
* Generate the specified proxy class.
*/
//也就是这里会生成字节码文件
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
}
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
//private static final boolean saveGeneratedFiles =
//(Boolean)AccessController.doPrivileged(new
//GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
//通过这里我们可以分析出,如果我们将系统参数
//sun.misc.ProxyGenerator.saveGeneratedFiles设置为true的话,我们就可以将字节码文件写入磁盘中,从而得到字节码文件
if (saveGeneratedFiles) {
//这里会产生文件
}
}
分析com.sun.proxy.$Proxy0字节码文件
//通过设置该系统属性,得到字节码文件
//System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
经过IDEA反编译
/*
通过分析这个字节码文件我们可以得出
1.对于Object中的方法,我们只对equals,toString,hashCode三个方法进行了代理,对于其他方法是没有进行代理的
2.public final class $Proxy0 extends Proxy implements Subject从这里可以看出我们是实现了抽象接口的角色。这也就是为什么Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(),ds);可以进行强制转换了
*/
public final class $Proxy0 extends Proxy implements Subject {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
//return cons.newInstance(new Object[]{h});这里就是一个参数的构造方法,在构造的时候将实现了InvocationHandler的具体实现类传递进去
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就是Proxy中的protected InvocationHandler h;
//而这个h就是我们在newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)我们自己实现了InvocationHandler的具体实现
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.chen.jvm.bytecode.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());
}
}
}