代理模式:静态代理;动态代理

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

一、静态代理

角色分析:

  • 抽象事务 :一般会使用接口或者抽象类
  • 真实角色 :被代理的角色
  • 代理角色 :代理真实角色 , 代理真实角色后,我们一般会做一些附属操作
  • 客户 : 访问代理对象

下面以租房为例,学习静态代理模式

  • 抽象事务:出租房,对应Rent 接口
  • 真实角色:房东,他需要出租房,即要实现Rent 接口,并实现出租的 rent()方法
  • 代理角色:房屋中介,他需要帮房东出租房子,同样需要实现Rent 接口,实现 rent() 方法,并对此方法做出一些扩展操作
  • 客户:需要租房的人

代码实现:

  • 接口
public interface Rent {
    void rent();
}
  • 真实角色
public class Host implements Rent{
    public void rent() {
        System.out.println("【Host】房东要出租房子!");
    }
}
  • 代理角色
public class Proxy implements Rent{
    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }

    public void rent() {
        lookHouse();
        host.rent();
        contract();
        rentRent();
    }

    public void contract() {
        System.out.println("【Proxy】签合同!");
    }

    public void rentRent() {
        System.out.println("【Proxy】收房租!");
    }

    public void lookHouse() {
        System.out.println("【Proxy】看房!");
    }
}
  • 客户 (测试类)
public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}
  • 测试结果:
【Proxy】看房!
【Host】房东要出租房子!
【Proxy】签合同!
【Proxy】收房租!

静态代理模式的利弊:

优点:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务;
  • 公共的业务交给代理角色,实现了业务的分工;
  • 公共业务发生扩展的时候,方便集中管理。

缺点:

  • 必须事先知道要代理的是什么;

  • 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低!

二、动态代理

  • 动态代理的实现方式:

    • 基于接口实现 【JDK动态代理】
    • 基于类实现:cglib
    • 基于java字节码实现:Javassist
  • 动态代理和静态代理的角色一样,代理类是动态生成的,不是直接写好的

  • 学习动态代理需要用到的两个类:

    • Proxy:提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
    //返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)            
    
    • InvocationHandler:是代理实例的调用处理程序 实现的接口。 每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke
      方法。
    //在代理实例上处理方法调用并返回结果。
    Object invoke(Object proxy, Method method, Object[] args) 
    

JDK动态代理代码实现:

  • 仍以上面租房为例,接口和真实角色都不变!
  • 动态代理类
public class InvocationHandlerProxy implements InvocationHandler {

    //需要代理的接口的实例
    private Object target;

    public void setObject(Object object) {
        this.target = object;
    }

    //获取代理实例
    public Object getProxy(){
        /*  参数:
            ClassLoader loader: 代理类的类加载器
            Class<?>[] interfaces: 代理类要实现的接口列表
            InvocationHandler h:指派方法调用的调用处理程序,即InvocationHandler的实例,也就是当前对象
        */
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    /* 参数:
    *   proxy:代理实例
    *   method:对应于在代理实例上调用的接口方法的 Method 实例。
    *   args:方法的参数列表
    * */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        lookHouse();
        //执行接口的方法
        //target:调用此方法的对象 args:参数列表
        Object invoke = method.invoke(target,args);
        contract();
        rentRent();
        return invoke;
    }

    public void contract() {
        System.out.println("【Proxy】签合同!");
    }

    public void rentRent() {
        System.out.println("【Proxy】收房租!");
    }

    public void lookHouse() {
        System.out.println("【Proxy】看房!");
    }
}
  • 测试类
public class Client {
    public static void main(String[] args) {
        Rent rent = new Host();
        InvocationHandlerProxy ihp = new InvocationHandlerProxy();
        ihp.setObject(rent);
        Rent proxy = (Rent) ihp.getProxy();
        proxy.rent();
    }
}
  • 动态代理的优点:
    • 包含静态代理的所有优点,且解决了其缺点;
    • 一个动态代理类可以代理一个接口下的所有类;
    • 动态代理事先不知道要代理的是什么,在运行的时候才确定;
    • 实现无侵入式的代码扩展,也就是方法的增强;让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情。
发布了55 篇原创文章 · 获赞 23 · 访问量 4321

猜你喜欢

转载自blog.csdn.net/y_Engineer/article/details/102593222