代理模式

代理模式

  • 静态代理
  • 动态代理

静态代理

  • 抽象角色Subject:可以是抽象类或者接口,包含了需要代理的方法
  • 真实角色RealSubject:被代理的角色
  • 代理角色Proxy:在Subject中抽象的方法都交给RealSubject处理,其它额外功能在Proxy中完成

代理角色和真实角色都实现了抽象接口中的方法,在客户实现需求时,只需要调用Proxy中的方法,不直接接触真实角色。

抽象角色

public interface Rent {    public void rent();  }

真实角色

public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("rent...");
    }
}

代理角色

public class Proxy implements Rent {
    private Host host;
    public Proxy(){}
    public Proxy(Host host){
        this.host=host;
    }
    public void before(){
        System.out.println("before...");
    }
    @Override
    public void rent() {
        before();
        host.rent();  //核心功能(抽象接口中的方法)仍然由真实角色处理
        after();
    }
    public void after(){
        System.out.println("after...");
    }
}

客户

public class Client {
    @Test
    public void rent(){
        Host host=new Host();
        Proxy proxy=new Proxy(host);
        proxy.rent();  //客户只调用代理角色方法,不接触被代理角色
    }
}

缺点:

  • 一个代理类只服务于一个类或接口。程序中将产生多个代理类,服务不同的接口。
  • 一旦该类增加方法,代理类和真实角色类都要实现增加的方法

动态代理

使用JDKjava.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口

Proxy.newProxyInstance 生成动态代理类

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException

参数:

loader - 定义代理类的类加载器

interfaces - 代理类要实现的接口列表

h - 指派方法调用的调用处理程序

返回:

一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口

InvocationHandler.invoke处理代理实例上的方法,在自定义的继承InvocationHandler的子类中重写该方法

Object invoke(Object proxy,
              Method method,
              Object[] args)
              throws Throwable

在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。

  • 参数:

    proxy - 在其上调用方法的代理实例

    method - 对应于在代理实例上调用的接口方法的 Method 实例。 Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。

    args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integerjava.lang.Boolean)的实例中。

  • 返回:

    从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException

抽象角色

public interface Rent {    public void rent();  }

自定义代理实例的调用处理程序类

public class ProxyInvocationHandler implements InvocationHandler {
    private Object target;

    public void setTarget(Object target) {
        this.target=target;
    }
    //生成代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }
    //处理代理实例上的方法并返回调用结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        //核心功能在真实角色中实现
        Object result=method.invoke(target,args);
        after();
        return result;
    }
    //代理类上实现的额外功能
    public void after(){
        System.out.println("after...");
    }
    public void before(){
        System.out.println("before...");
    }
}

客户

public class Client {
    @Test
    public void dynamicProxy(){
        Host host=new Host(); //真实角色
        //代理实例的调用处理程序
        ProxyInvocationHandler handler=new ProxyInvocationHandler();
        handler.setTarget(host);
        Rent rent= (Rent) handler.getProxy(); //代理角色
        rent.rent();
    }
}

优点:

  • 抽象接口增加方法时,只需要在真实角色中实现,在代理类中不需要实现,统一由invoke方法处理
  • 一个代理类可以代理多个不同接口,通过setTarget方法实现
  • 动态代理更加灵活,代理类可以在程序运行时,通过反射动态生成,而不是由代码定义

参考:
https://blog.csdn.net/qq_33369905/article/details/105828919
https://www.jianshu.com/p/9cdcf4e5c27d

posted @ 2020-12-08 15:44  剑意由心  阅读(27)  评论(0)    收藏  举报