接着学习设计模式系列,今天讲解的是代理模式。
定义什么是代理模式?
代理模式,也叫委托模式,其定义是给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。它包含了三个角色:
Subject:抽象主题角色。可以是抽象类也可以是接口,是一个最普通的业务类型定义。
RealSubject:具体主题角色,也就是被代理的对象,是业务逻辑的具体执行者。
Proxy:代理主题角色。负责读具体主题角色的引用,通过真实角色的业务逻辑方法来实现抽象方法,并在前后可以附加自己的操作。
用类图来表示的话大概如下:
我们可以用举一个电影演员拍戏的例子,一般来说,演员最主要的工作就是演戏,其他的事可以交给他的经纪人去做,例如谈合同,安排档期等等,而负责这些场外工作的经纪人就相当于Proxy,而负责核心业务的演员就是 RealSubject 。
这就是代理模式的设计思路,除此之外,代理模式分为静态代理和动态代理,静态代理是我们自己创建一个代理类,而动态代理是程序自动帮我们生成一个代理类,可以在程序运行时再生成对象,下面分别对它们做介绍。
静态代理静态代理在程序运行之前,代理类.class文件就已经被创建了。还是用上面演员演戏的例子,在静态代理模式中,我们要先创建一个抽象主题角色 Star ,
public interface Star { // 演戏 void act(); }接下来就是创建具体的主题角色和代理主题角色,分别实现这个接口,先创建一个具体的主题角色 Actor ,
/** * 演员,也就是具体的主题角色 * * @author Tao * @since 2019/7/9 18:34 */ public class Actor implements Star { public void act() { System.out.println("演员演戏~~~"); } }然后就是创建代理主题角色,也就是代理类,代理类本身并不负责核心业务的执行流程,演戏这事还得明星自己来。所以在代理类中需要将真实对象引入,下面是具体的代码实现:
/** * 代理对象 * @author Tao * @since 2019/7/9 18:43 */ public class Agent implements Star { /** * 接收真实的明星对象 */ private Star star; /** * 通过构造方法传进来真实的明星对象 * * @param star star */ public Agent(Star star) { this.star = star; } public void act() { System.out.println("签合同"); star.act(); System.out.println("演完戏就收钱了"); } }代码的逻辑还是比较清晰的,通过维护一个Star对象,可以在act里调用具体主题角色的业务逻辑,并且在核心逻辑前后可以做一些辅助操作,比如签合同,收钱等,这样代理模式的角色就都分工完成了,最后用一个场景类来验证下:
public class Client { public static void main(String[] args) { Star actor = new Actor(); Agent agent = new Agent(actor); agent.act(); } }运行的结果如下:
签合同
演员演戏~~~
演完戏就收钱了
动态代理分为两种,分别是JDK动态代理和 CGLIB 动态代理,怎么又分了,代理模式分类真多,不过来都来了,就都学习一下吧。
JDK动态代理前面说了,在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK再运行时帮我们动态的来创建。
/** * 动态代理处理类 * * @author Tao * @since 2019/7/9 19:04 */ public class JdkProxyHandler { /** * 用来接收真实明星对象 */ private Object star; /** * 通过构造方法传进来真实的明星对象 * * @param star star */ public JdkProxyHandler(Star star) { super(); this.star = star; } /** * 给真实对象生成一个代理对象实例 * * @return Object */ public Object getProxyInstance() { return Proxy.newProxyInstance(star.getClass().getClassLoader(), star.getClass().getInterfaces(), (proxy, method, args) -> { System.out.println("签合同"); // 执行具体的业务逻辑 Object object = method.invoke(star, args); System.out.println("演出完经纪人去收钱……"); return object; }); } }这里说一下Proxy.newProxyInstance 这个方法,该方法包含了三个参数,
ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的;
Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型;
InvocationHandler:指定动态处理器,执行目标对象的方法时会触发事件处理器的方法。
写完了动态代理实现类,我们写个场景类测试下,
public class Client { public static void main(String[] args) { Star actor = new Actor(); // 创建动态代理对象实例 Star jdkProxy = (Star) new JdkProxyHandler(actor).getProxyInstance(); jdkProxy.act(); } }执行结果正常输出:
签合同
演员演戏~~~
演出完代理去收钱……