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

代理模式应用场景十分普遍,随便一个框架都会用到,所以学好代理模式对后续框架学习是最基本的要素!!今天咱们就来说讲代理模式java


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

一、简介

Proxy patternweb

1. 核心做用

  • 经过代理,控制对对象的访问设计模式

    能够详细的访问某个(某类)对象的实现,在调用这个方法前作前置处理,调用这个方法后作后置处理(AOP的微观实现)安全

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


2. 角色分析

image-20200808160429399

抽象角色svg

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

真实角色学习

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

代理角色测试

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

客户this

  • 访问代理角色的人

2. 应用场景

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


4. 分类

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



二、静态代理

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

因而可知

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



三、动态代理

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


好处

  • 一个动态代理类代理的是一个接口,通常就是对应的一类业务
  • 一个动态代理类能够代理多个类,只要是实现了同一个接口便可

其余好处同静态代理