代理模式

代理模式

代理模式介绍

为什么要用代理模式?

代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问
在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用

代理模式种类

如果按照代理创建的时期来进行分类的话,可以分为两种:静态代理、动态代理。
静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.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());
        }
    }
}
原文地址:https://www.cnblogs.com/liuligang/p/10516824.html