代理模式(Proxy)

为其他对象提供一种代理以控制对这个对象的访问

举个例子

金鹏驾校开张了。正常报名的话,我们都要去驾校排队报名。瑞哥觉得有人不想排队,就想出来了一个生财的道道。瑞哥要给鹏哥做代理。报名直接找瑞哥就行了。但是瑞哥觉得收钱爽,退钱不爽。所以就只做报名收钱这个,如果觉得报名后悔的话,还得找鹏哥要钱。说白了就是:为其他对象提供一种代理以控制对这个对象的访问。可以去掉或增加额外的功能。

代理的种类

1. 远程代理

为不同地理的对象提供局域网代表对象

2. 虚拟代理

根据需要将资源消耗比较大的对象进行延迟,真正需要的时候在进行创建

3. 智能代理

两种实现方式:静态代理,动态代理

4. 保护代理

可以简单理解为控制对一个对象的访问权限

智能引用代理

静态代理

代理和被代理对象在代理之前是确定的,他们都实现了相同的接口或者继承了相同的抽象类。

Paste_Image.png
public class ProxyDemo {
    public static void main(String args[]){ 
        RealSubject subject = new RealSubject(); 
        Proxy p = new Proxy(subject); 
        p.request(); }
}

interface Subject{ 
    void request();
}

class RealSubject implements Subject{ 
    public void request(){ 
        System.out.println("request"); 
  }
}

class Proxy implements Subject{ 
    private Subject subject; 
    public Proxy(Subject subject){ 
        this.subject = subject; 
    } 
    public void request(){ 
        System.out.println("PreProcess"); 
        subject.request(); 
        System.out.println("PostProcess"); 
    }
}
动态代理

代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。

public class DynamicProxyDemo01 {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();//1.创建委托对象
        ProxyHandler handler = new ProxyHandler(realSubject);//2.创建调用处理器对象
        Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
                                RealSubject.class.getInterfaces(), handler);//3.动态生成代理对象
        proxySubject.request();//4.通过代理对象调用方法
    }
}

/**
 * 接口
 */
interface Subject{
    void request();
}

/**
 * 委托类
 */
class RealSubject implements Subject{
    public void request(){
        System.out.println("====RealSubject Request====");
    }
}
/**
 * 代理类的调用处理器
 */
class ProxyHandler implements InvocationHandler{
    private Subject subject;
    public ProxyHandler(Subject subject){
        this.subject = subject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("====before====");//定义预处理的工作,当然你也可以根据 method 的不同进行不同的预处理工作
        Object result = method.invoke(subject, args);
        System.out.println("====after====");
        return result;
    }
}
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler)
{
    //1. 根据类加载器和接口创建代理类
    Class clazz = Proxy.getProxyClass(loader, interfaces); 
    //2. 获得代理类的带参数的构造函数
    Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
    //3. 创建代理对象,并制定调用处理器实例为参数传入
    Interface Proxy = (Interface)constructor.newInstance(new Object[] {handler});
}

这个函数是在代理对象调用任何一个方法时都会调用的,方法不同会导致第二个参数method不同,第一个参数是代理对象(表示哪个代理对象调用了method方法),第二个参数是 Method 对象(表示哪个方法被调用了),第三个参数是指定调用方法的参数。

动态生成的代理类具有几个特点:

  1. 继承 Proxy 类,并实现了在Proxy.newProxyInstance()中提供的接口数组。
  2. public final。
  3. 命名方式为 $ProxyN,其中N会慢慢增加,一开始是 $Proxy1,接下来是$Proxy2...
  4. 有一个参数为 InvocationHandler 的构造函数。这个从 Proxy.newProxyInstance() 函数内部的clazz.getConstructor(new Class[] { InvocationHandler.class }) 可以看出。

Java 实现动态代理的缺点:因为 Java 的单继承特性(每个代理类都继承了 Proxy 类),只能针对接口创建代理类,不能针对类创建代理类。

不难发现,代理类的实现是有很多共性的(重复代码),动态代理的好处在于避免了这些重复代码,只需要关注操作。

Java 动态代理的内部实现

现在我们就会有一个问题: Java 是怎么保证代理对象调用的任何方法都会调用 InvocationHandler 的 invoke() 方法的?

这就涉及到动态代理的内部实现。假设有一个接口 Subject,且里面有 int request(int i) 方法,则生成的代理类大致如下:

public final class $Proxy1 extends Proxy implements Subject{
    private InvocationHandler h;
    private $Proxy1(){}
    public $Proxy1(InvocationHandler h){
        this.h = h;
    }
    public int request(int i){
        Method method = Subject.class.getMethod("request", new Class[]{int.class});    //创建method对象
        return (Integer)h.invoke(this, method, new Object[]{new Integer(i)}); //调用了invoke方法
    }
}

通过上面的方法就成功调用了 invoke() 方法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容