1. 代理模式

代理模式,给目标对象创建代理对象,在不影响源代码的情况下,实现对目标对象的功能横向扩展。在代理模式中,主要有 4 种角色:

  • 抽象角色:一般会使用接口和抽象类来解决。
  • 真实角色:被代理的角色,目标对象。
  • 代理角色:代理真实角色,一般会做其他附属操作。
  • 客户:访问代理角色的人。

分类:

  • 静态代理,一个真实角色产生一个代理角色。
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理。
    • 基于接口:JDK 动态代理
    • 基于类:Cglib 动态代理
    • Java 字节码实现:Javasist

优点:

  • 可以使真实角色(目标对象)的操作更加纯粹,不用去关注一些公共的业务,公共业务交给了代理角色实现了业务的分工。
  • 公共业务发生扩展时,需要集中管理。

缺点:

  • 一个真实角色就产生一个代理角色。代码量翻倍(开放效率变低)

image.png


2. 静态代理

静态代理,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。

优点:写法简单,代码量少。
缺点:一个真实角色就产生一个代理角色。

  1. //租房
  2. public interface Rent {
  3. public void rent();
  4. }
  5. //房东,真实角色
  6. public class Host implements Rent{
  7. @Override
  8. public void rent() {
  9. System.out.println("房东要出租房子");
  10. }
  11. }
  12. //中介,代理角色
  13. public class Proxy implements Rent{
  14. private Host host;
  15. public Proxy(Host host) {
  16. this.host = host;
  17. }
  18. @Override
  19. public void rent() {
  20. host.rent();
  21. sendHouse();
  22. fare();
  23. hetong();
  24. }
  25. //看房
  26. public void sendHouse() {
  27. System.out.println("中介带你看房!");
  28. }
  29. //收中介费
  30. public void fare() {
  31. System.out.println("收中介费");
  32. }
  33. //签租赁合同
  34. public void hetong() {
  35. System.out.println("签租赁合同!");
  36. }
  37. }
  38. //客户端访问代理角色
  39. public class Client {
  40. public static void main(String[] args) {
  41. //房东要租房子
  42. Host host = new Host();
  43. //代理,中介帮房东租房子,但是,代理角色一般会有一些附属操作
  44. Proxy proxy = new Proxy(host);
  45. //不用面对房东,直接找中介租房
  46. proxy.rent();
  47. }
  48. }

3. JDK动态代理

JDK 动态代理,主要说的是两个接口 InvocationHandler 和 Proxy,都是 reflect 包下面的。

  • Proxy:自动生成代理类实例。
  • 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
        );
    }


    //处理代理实例,并返回结果
    @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 + "方法");
    }
}

public class Client {
    public static void main(String[] args) {
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //代理角色,不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();

        //1、设置要代理的对象
        pih.setTarget(userService);

        //2、动态生成代理类
        UserService proxy = (UserService) pih.getProxy();
        proxy.insert();
    }
}

4. Cglib动态代理

需要引入 cglib 依赖。

<dependency>
  <groupId>org.sonatype.sisu.inject</groupId>
  <artifactId>cglib</artifactId>
  <version>3.1</version>
</dependency>

不需要实现接口,在内存中构建一个子类对象从而实现对目标对象功能扩展,Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类。

public class CglibProxy implements MethodInterceptor {
    // 维护一个目标对象
    private Object target;

    // 传入一个被代理的对象,真实角色
    public CglibProxy(Object target) {
        this.target = target;
    }

    // 返回一个代理对象,是 target 的代理对象
    public Object getProxyInstance() {
        // 1、创建一个工具类
        Enhancer enhancer = new Enhancer();
        // 2、设置父类
        enhancer.setSuperclass(target.getClass());
        // 3、设置回调函数
        enhancer.setCallback(this);
        // 4、创建子类对象,即代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        watch();
        Object resultVal = method.invoke(target, objects);
        return resultVal;
    }

    public void watch() {
        System.out.println("看房。。。");
    }
}

public class Client {
    public static void main(String[] args) {
        Host host = new Host();

        Host proxyInstance = (Host)new CglibProxy(host).getProxyInstance();
        proxyInstance.rent();
    }
}

CGLIB代理与JDK动态代理的区别

  • jdk动态代理:目标对象需要实现一个或多个接口,使用反射完成,使用了动态生成字节码技术。
  • cglib动态代理:可以直接代理类,使用字节码技术,不能对 final类进行继承。使用了动态生成字节码技术。