为什么要学习代理模式?

因为代理模式是SpringAOP的底层,在Spring面试中,SpringAOP和SpringMVC是必问。

代理模式的好处:

  1. 可以不用改动原有业务代码(这是大忌,可能会导致原来可以运行的代码无法再运行了);
  2. 使得真实角色的操作更加纯粹,不用去关注一些公共的业务,公共业务放在代理里实现;
  3. 公共业务发生扩展的时候,方便集中管理。

什么是代理模式?

所谓的代理模式,就是在两个对象之间,加一层代理,来实现原有的功能,并且提供一些增值服务。

举个例子,租客找房东租房,中间加一层代理,那么租客直接找该代理完成租房的功能(该功能属于房东,现在 由代理帮忙完成)。此外,提供一些增值服务,例如,带看房,签合同,做保洁,收取费用等。

可以发现,原来是A接触B,现在A不接触B,通过接触代理,也可以完成原来的功能。此外,代理还能在不修改B的代码的条件下,提供额外的功能。

代理模式的分类

  • 静态代理
  • 动态代理

静态代理模式中的角色

  1. 接口 (例如租房这个接口)
  2. 真实角色 (例如房东)
  3. 代理角色 (列入中介)
  4. 客户端访问代理角色 (例如租客)

  5. 接口

    1. public interface Rent {
    2. public void rent();
    3. }
  6. 真实角色

    public class Host implements Rent{
     public void rent() {
         System.out.println("房东要出租房子");
     }
    }
    
  7. 代理角色 ```java package com.kuang.demo01;

public class Proxy implements Rent{

private Host host;

public Proxy() {
}

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

public void rent() {
    inspectHouse();
    host.rent();
    signContract();
    chargeFee();
}

public void inspectHouse() {
    System.out.println("中介带看房");
}

public void signContract() {
    System.out.println("签署合同");
}

public void chargeFee() {
    System.out.println("收中介费");
}

}


4. 访问代理角色
```java
package com.kuang.demo01;

public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        // 不直接通过房东租房子,而是通过中介来租
        // 中间会添加一些附属操作。
        Proxy proxy = new Proxy(host);

        proxy.rent();

    }
}

动态代理

  • 动态代理和静态代理角色是一样的;
  • 但是,动态代理的代理类是动态生成的,不是直接写好的。
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理。
    • 基于接口 —- JDK动态代理
    • 基于类 —- cglib
    • java字节码实现:javassit

需要了解两个类:proxy和invocation

实现步骤:

  1. 实现InovacationHandler;
  2. 通过set方法设置要代理的对象;
  3. 重写invoke方法,当调用被代理对象里的方法时,会转而调用invoke方法;
  4. 通过getProxy方法来获取要得到的代理对象;
// 实现InvocationHandler
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);
    }

    // 重写invoke方法,当调用被代理对象的方法时,转而执行该invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 其他操作
        log(method.getName());

        // 调用被代理对象的方法
        Object result = method.invoke(target, args);
        return result;
    }

    // 示范用的额外操作
    public void log(String msg) {
        System.out.println("使用了" + msg + "方法");
    }
}

在使用的时候:

  1. 生成真实对象;
  2. 生成handler对象;
  3. 将真实对象设置到handler里去;
  4. 通过getProxy方法来生成并获取代理对象;
  5. 通过代理对象调用被代理对象的方法,实际是去调用invoke;

    public class Client {
     public static void main(String[] args) {
         // 生成真实对象;
         Host host = new Host();
    
         // 生成handler对象;
         ProxyInvocationHandler pih = new ProxyInvocationHandler();
    
         // 将真实对象设置到handler里去;
         pih.setTarget(host);
    
         // 通过getProxy方法来生成并获取代理对象;
         Rent proxy = (Rent) pih.getProxy();
    
         // 通过代理对象调用被代理对象的方法,实际是去调用invoke;
         proxy.rent();
    
     }
    }
    

动态代理除了具有静态代理的优点以外,还能:

  • 一个动态代理类可以代理多个类。