设计模式六:代理模式

【为什么要有“代理”】

  • 代理就是被代理这没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事情,这才是“代理” 存在的原因、
  • 例如要租房子,房产中介可以在我们住房前代理我们找房子,中介就是代理,而我们就是被代理了。

在代码设计中,代理模式作用主要就是让 "被代理对象" 的某个方法执行之或者执行之加入其他增强逻辑。

【代理模式存在两种实现方式】:

  • 静态代理(用于对动态代理做铺垫)
  • 动态代理(为后面学习框架做铺垫)

两者区别:在程序运行之前,代理类就存在了,这就是静态代理;动态代理是程序运行时动态生成代理类

1、静态代理

1.1、概述

静态代理是由程序员创建 或 工具生成代理类的源码,再编译代理类。

在程序运行前就已经存在代理类的字节码文件,代理类和被代理类的关系在运行前就确定了。

1.2、案例实践

以现实中经纪人代理明星

接口:

// 1.抽象角色
interface Star {
    // 真人秀方法
    double liveShow(double money);
}

定义被代理类:

  • 定义王宝强类,实现Star方法
// - 定义被代理角色(宝强)
class BaoQiang implements Star {

    @Override
    public double liveShow(double money) {
        System.out.println("宝强参加了一个真人秀活动赚了" + money + "钱");
        return money;
    }
}

定义代理类:

  • 定义宋喆经纪人类
// - 定义代理角色(宋喆),增强被代理角色的功能
class SongZhe implements Star {
    private BaoQiang baoQiang;

    public SongZhe(BaoQiang baoQiang) {
        this.baoQiang = baoQiang;
    }

    @Override
    public double liveShow(double money) {
        // 前增强
        System.out.println("宋喆帮宝强拉了一个真人秀的活动,获取佣金" + money * 0.4 + "元");
        // 被代理角色的功能
        double result = baoQiang.liveShow(money * 0.6);
        // 后增强
        System.out.println("宋喆帮宝强把赚的钱存了起来...");
        return result;
    }
}

测试类:

/*
    静态代理实现的步骤  :
        - 存在一个抽象角色
        - 定义被代理角色(宝强)
        - 定义代理角色(宋喆),增强被代理角色的功能
 */
public class StaticAgentDemo {
    public static void main(String[] args) {
        // 创建被代理角色 , 没有任何增强
        BaoQiang baoQiang = new BaoQiang();
        double result = baoQiang.liveShow(1000);
        System.out.println(result);

        System.out.println("===========================");

        // 创建代码角色对象 , 可以对被代理角色功能做前后增强
        SongZhe songZhe = new SongZhe(baoQiang);
        double result2 = songZhe.liveShow(1000);
        System.out.println(result2);

    }
}

关系图:

1.3、静态代理与装饰类对比

能看出静态代理和装饰者模式很像,主要区别:

  1. 装饰设计模式是功能扩展功能,在原有的功能基础之上增加了新的功能
  2. 而代理主要对功能的前后做了增强

2、动态代理

2.1、概述

在实际开发过程中往往我们自己不会去创建代理类而是通过JDK提供的Proxy类在程序运行动态创建而成,这就是我们所谓的动态代理

虽然我们不需要自己定义代理类创建代理对象,但是我们要定义对被代理对象直接访问方法的拦截,原因就是对拦截的方法做增强

动态代理技术在框架中使用居多,例如:数据库框架MyBatis框架(Spring,SpringMVC)中都使用了动态代理技术。

2.2、API学习

Proxy类

  • java.lang.reflect.Proxy 类提供了用于创建动态代理类和对象的静态方法

​ 它还是由这些方法创建的所有动态代理类的超类(代理类的父类是Proxy)。

public static Object newProxyInstance (
  ClassLoader loader, 
  Class<?>[] interfaces,  
  InvocationHandler h ) //获取代理对象的方法 

/*
- 返回值:该方法返回就是动态生成的代理对象
- 参数列表说明:
  1. ClassLoader loader 	- 定义代理类的类加载器 【只要是系统类加载器就可以】
    	随便一个自己写的类的类加载器
  2. Class<?>[] interfaces 	- 代理类要实现的接口列表,要求与被代理类的接口一样。
    	new Class[]{要实现的接口的代理类...}
  3. InvocationHandler h 	- 就是具体实现代理逻辑的接口
*/  

InvocationHandler接口

源码 :

interface InvocationHandler{
	public Object invoke(Object proxy, Method method, Object[] args);  //代理逻辑
}

java.lang.reflect.InvocationHandler是代理对象的实际处理代理逻辑的接口,具体代理实现逻辑在其 invoke 方法中。所有代理对象调用的方法,执行是都会经过invoke。因此如果要对某个方法进行代理增强,就可以在这个invoke方法中进行定义。

invoke() 方法

public Object invoke(Object proxy, Method method, Object[] args);

/*
- 返回值:方法被代理后执行的结果。
- 参数列表:
   1. proxy  - 就是代理对象
   2. method - 代理对象调用的方法
   3. args   - 代理对象调用方法传入参数值的对象数组.
*/

2.3、动态代理调用流程

2.4、扩展

缺点 :只能针对接口的实现类做代理对象,普通类是不能做代理对象的。

后面框架学习的时候会接触到CGLib(Code Genneration Library ): 可以实现对类的代理

posted @ 2022-02-13 15:38  火烧云Z  阅读(88)  评论(0)    收藏  举报