GOF23设计模式(结构型模式)代理模式~

代理模式应用场景十分广泛,随便一个框架都会用到,因此学好代理模式对后续框架学习是最基本的要素!!今天我们就来讲讲代理模式


首先,了解以下23种设计模式中 代理模式的定位,它是 结构性模式
模式

1、简介

Proxy pattern

1. 核心作用

  • 通过代理,控制对对象的访问

    可以详细的访问某个(某类)对象的实现,在调用这个方法前做前置处理,调用这个方法后做后置处理(AOP的微观实现)

  • 代理模式是SpringAOP的核心实现机制!


2. 角色分析

image-20200808160429399

抽象角色

  • 定义代理角色和真实角色的公共对外方法,一般会使用接口或者抽象类来解决

真实角色

  • 实现抽象角色,定义真实角色要实现的业务,供代理角色调用

代理角色

  • 实现抽象角色,代理真实角色,通过真实角色的业务来实现抽象方法,一般会有附属的业务

客户

  • 访问代理角色的人

2. 应用场景

安全代理:屏蔽对真实角色的直接访问。
远程代理:通过代理类处理远程方法调用(RMI)
延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。


4. 分类

  • 静态代理(静态定义代理类)
  • 动态代理(动态生成代理类)
    • JDK自带的动态代理
    • javaassist字节码操作库实现
    • CGLIB
    • ASM(底层使用指令,可维护性较差)



2、静态代理

static proxy

1. 案例:房东租房

接下来以房东租房这件实例,来讲讲代理模式

房东租房,如果房东懒得管太多,这时候就需要一个中介,来帮助房东租房并打理一切事情,这时候租房者就不需要直接和房东打交道了,而是通过中介间接和房东打交道,中介就是中间者,代理了房东,且可以在租房前后附加其他操作,比如:签合同,看房子等

image-20200808145117892

这时候对象上述的四个角色就有四个对象

  1. 抽象角色:租房业务
  2. 真实角色:房东
  3. 代理角色:中介,可能还有带客户看房子等业务
  4. 客户:租房者

接下来,我们通过代码还原上述四个角色

  1. 抽象角色:表示租房这个业务,用接口实现

    //租房
    public interface Rent {
        public void rent();
    }
    
  2. 真实角色:代表房东,实现租房业务接口

    //房东
    public class Host implements Rent{
    
        public void rent() {
            System.out.println("房东要出租房子了");
        }
    }
    
  3. 代理角色中介,实现租房业务接口

    因为代理了房东,所以私有属性是房东对象,除了租房子业务外,可能还有看房、签合同、收中介费等业务

    public class Proxy implements Rent {
        private Host host;
    
        public Proxy() {
        }
    
        public Proxy(Host host) {
            this.host = host;
        }
    
        //代理租房子
        public void rent() {
            seeHouse();
            host.rent();
            contract();
            fare();
        }
    
        //看房
        public void seeHouse() {
            System.out.println("中介带你看房");
        }
    
        //签合同
        public void contract() {
            System.out.println("租赁合同");
        }
    
        //收中介费
        public void fare() {
            System.out.println("收中介费");
        }
    }
    
  4. 客户租房者,访问中介

    public class Client {
        public static void main(String[] args) {
            //房东租房子
            Host host = new Host();
            //代理,中介帮房东租房子,并且有一些附属操作
            Proxy proxy = new Proxy(host);
            //不需要找房东,直接找中介租房即可
            proxy.rent();
        }
    }
    
    image-20200808152341543


2. 优劣分析

好处

  1. 职责清晰

    使真实角色更加的简单专一,不管具体的业务

    • 这里房东只用给中介费就行了,其他一切都交给中介来做,实现了业务分工
  2. 智能化

    客户只需访问代理角色,减少了直接访问真实角色带来的问题

    • 加入这里房东有很多房子,自己一个人可能管理不过来,这时候每个客户的体验可能会变差,会出现很多问题
  3. 高拓展性

    业务发展拓展的时候,方便集中管理

缺点

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


3. 加深理解

可能上述例子过于简单,不能直观的感受到代理模式的好处,我们再举个例子加深理解

首先创建一个业务实现接口

npublic interface UserService {
    public void add();

    public void delete();

    public void update();

    public void query();
}

然后再来一个业务实现类

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("增加一个用户");
    }

    public void delete() {
        System.out.println("删除一个用户");
    }

    public void update() {
        System.out.println("修改了一个用户");
    }

    public void query() {
        System.out.println("查询了一个用户");
    }
}

再来个测试类

public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        userService.add();
        userService.delete();
        userService.update();
        userService.query();
    }
}

如果此时,我们需要增加一个日志业务,需要打印每个方法的执行

  • 普通修改在业务实现类中的每个方法中都要修改
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("使用了add方法");
        System.out.println("增加一个用户");
    }

    public void delete() {
        System.out.println("使用了delete方法");
        System.out.println("删除一个用户");
    }

    public void update() {
        System.out.println("使用了update方法");
        System.out.println("修改了一个用户");
    }

    public void query() {
        System.out.println("使用了query方法");
        System.out.println("查询了一个用户");
    }
}

业务很多的情况下,修改量十分大,这时候用代理模式就能很好的解决我们的问题

我们新建一个业务代理类,在业务实现类里只需增加一个方法就可以实现上述新增业务

public class UserServiceProxy implements UserService {
    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    public void add() {
        log("使用了add方法");
        userService.add();
    }

    public void delete() {
        log("使用了delete方法");
        userService.delete();
    }

    public void update() {
        log("使用了update方法");
        userService.update();
    }

    public void query() {
        log("使用了query方法");
        userService.query();
    }

    public void log(String msg) {
        System.out.println("使用了" + msg + "方法");
    }
}

然后修改测试类,进行测试

package demo2;

public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);
        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.query();
    }
}
image-20200808152417491

由此可见

  • 当业务需要拓展的时候,代理模式充分的体现了其高拓展性
  • 业务很多的时候,代理模式也方便管理



3、动态代理

dynamic proxy

1. 简介

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 分类
    • JDK自带的动态代理
    • javaassist字节码操作库实现
    • CGLIB
    • ASM(底层使用指令,可维护性较差)

接下来我们讲述JDK自带的动态代理

需要了解:Proxy(代理)、InvocationHandler(调用处理程序)

2. Proxy 类

代理
image-20200808152826044

  • 都是静态方法,直接通过类名调用
  • 作用:动态生成代理类和对象

所有方法
image-20200808153732869
创建动态类的方法:【重要】

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

每次通过Proxy生成的代理类对象都要指定对应的处理器对象,就是第三个参数

三个参数

  • 类加载器,来定义代理类
  • 代理类实现的接口列表
  • 处理器接口对象

3. InvocationHandler 接口

处理器接口
image-20200808153648950

  • 通过invoke方法实现对真实角色的访问(就是调用真实角色的方法)

唯一的一个方法
image-20200808153905412
三个参数

  • proxy:代理类
  • method:正在调用的方法
  • args:方法中的参数,默认即可

4. 代码示例

这里还是以上述租房子为实例,我们用动态代理的方式实现

Rent接口(抽象角色) 和 Host类(真实角色)不变

  1. 定义一个处理器接口实现类,继承InvocationHandler
    image-20200808170644319
    其中的invoke方法实现对真实角色方法的调用,因此该类中有真实角色私有属性,为传参使用

    package demo3;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyInvocationHandler implements InvocationHandler {
        //定义真实角色
        private Rent host;
    
        //真实角色set方法
        public void setHost(Rent host) {
            this.host = host;
        }
    
        /**
        生成代理类方法
        1. 类加载器,为当前类即可
        2. 代理类实现的接口
        3. 处理器接口对象
        **/
        public Object getProxy() {
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                    host.getClass().getInterfaces(), this);
        }
    
        //处理代理实例,并返回结果
        //方法在此调用
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //调用真实角色方法
            Object result = method.invoke(host, args);
            //附加方法
            seeHouse();
            contract();
            fare();
            return result;
        }
    
        //看房
        public void seeHouse() {
            System.out.println("中介带你看房");
        }
    
        //签合同
        public void contract() {
            System.out.println("租赁合同");
        }
    
        //收中介费
        public void fare() {
            System.out.println("收中介费");
        }
    }
    
    
  2. 测试类

    package demo3;
    
    public class Client {
        public static void main(String[] args) {
            //真实角色:房东
            Host host = new Host();
            //处理器接口对象
            ProxyInvocationHandler handler = new ProxyInvocationHandler();
            //设置要代理的真实角色
            handler.setHost(host);
            //动态生成代理类
            Rent proxy = (Rent) handler.getProxy();
            //调用方法
            proxy.rent();
        }
    }
    
  3. 结果
    image-20200808165300371


好处

  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可

其他好处同静态代理

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Baret-H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值