Design Pattern [6] —— 代理模式 Proxy
意义:代理类在不改变原有类(也就是被代理类)的情况下,对其功能进行扩展.
分类:静态代理 & 动态代理
应用:AOP
我的Github里有源码,可以clone下来自己跑下:https://github.com/Yang2199/Design-Pattern/tree/master/src
静态代理模式
/** * Worker接口,有一个方法可以卖票; */ interface Worker { // 售票方法; void sell(); } /** * 售票员类,实现了Worker接口,可以进行售票,也就是"被代理类"对象; */ class Seller implements Worker { @Override public void sell() { System.out.println("成功把票卖出去了!"); } } /** * 黄牛类,也实现了Worker接口,也可以售票,称为"代理类"对象; */ class Scalper implements Worker { // 私有一个被代理类的父类引用,这样做是为了适应所有的被代理类对象,只要实现了接口就好; private Worker worker; // 传入被代理类对象,这里的作用是初始化"代理类"中的"被代理类"对象; public Scalper(Worker worker) { this.worker = worker; } /** * 增强服务和功能; */ @Override public void sell() { // 代理服务; worker.sell(); // 额外服务; noQueue(); } // 代理类本身自带功能; public void noQueue() { System.out.println("黄牛附加功能:不用排队哟!!!"); } } /** * 主函数; */ public class StaticProxyDemo { public static void main(String[] args){ // 首先是原始类,也就是被代理类; System.out.println("======正常买票======"); Worker worker = new Seller(); worker.sell(); System.out.println("======黄牛买票======"); // 其次是代理类,传入被代理类对象; Worker pw = new Scalper(worker); pw.sell(); } }
结果:
类图:
动态代理模式
与静态代理不同,动态代理不需要提前创建对象,只需要提供一个动态创建器,程序会在运行时候动态生成对应的代理类。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface App { void webSell(); } class TicketApp implements App { @Override public void webSell() { System.out.println("APP卖票:顾村公园"); } } interface Seller { // 售票方法; void sell(); } class OridinarySeller implements Seller { @Override public void sell() { System.out.println("窗口卖票:顾村公园"); } } /** * 代理类,也就是代理对象,就类似于上述静态代理中的Scalper类; */ class ProxySubject implements InvocationHandler { // 涉及到动态代理需要实现这个接口InvocationHandler // 实现了接口的被代理类的对象引用声明; private Object object; public Object getNewInstance(Object object) { // 实例化被代理类的对象; this.object = object; // 返回一个代理类的对象; /** * 这里的newProxyInstance的三个参数:(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) * 1.第一个参数是需要传入类的加载器,这里指的是被代理类的类加载器,简单来说就是和被代理类使用相同的类加载器; * 2.第二个参数是需要传入类的接口,也就是说,这个类实现了哪些接口,我都要传过来; * 3.第三个参数是需要传入的一个InvocationHandler对象,指的是代理类对象,也就是调用这个函数的this对象(ProxySubject对象); */ return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this); } // 当通过代理类的对象发起对被重写的方法的调用时,都会转换为对以下invoke方法的调用; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 增强代码(前); System.out.println("++AOP前附加功能:节日打折"); // 被代理类的方法; Object value = method.invoke(object, args); // 增强代码(后); System.out.println("++AOP后附加功能:节日赠品=>吹泡泡机/风筝"); return value; } } public class DynamicProxyDemo { public static void main(String[] args){ // 1.创建被代理类对象; TicketApp ticketApp = new TicketApp(); // 2.创建一个实现了InvocationHandler接口的类的对象; ProxySubject proxySubject = new ProxySubject(); // 3.父类引用指向子类对象; App app = (App)proxySubject.getNewInstance(ticketApp); // 4.执行代理类的方法; app.webSell(); System.out.println("==========================="); // 使用前面静态代理的例子,创建一个Seller的被代理类对象; OridinarySeller oridinarySeller = new OridinarySeller(); // 创建一个Worker的子类对象,指向Seller的代理类对象; Seller seller = (Seller)proxySubject.getNewInstance(oridinarySeller); // 执行其方法; seller.sell(); } }
结果:
类图:
总结
代理模式可以在不改变目标类的情况下对功能进行扩展
静态代理编译时便生成class文件;动态代理运行时通过反射生成对应的class文件
静态代理需要实现接口的所有方法,一个代理类能服务的目标类有限;动态代理可以只对某些方法进行处理,一个代理处理器可以服务多个目标类
对于一些接口比较简单、或者自动生成的通用性代码,可以选择使用静态代理;对于一些庞大的接口,频繁地需要改动接口,你已经觉得改得很烦,可以选择使用动态代理。