代理模式

代理模式

代理可以简单理解为,B类托管A类的功能,并根据需求,对A类的访问作控制,这里的控制可以理解为对A类方法执行的流程的影响,包括但不限于:

在方法执行之前,先做其他事(前置通知) 在方法执行之后,再做某事(后置通知) 决定方法是否执行(环绕通知) ...

Java中代理的主要应用体现在权限控制,日志管理,事务控制

iCu1S0.md.png

java 代理模式简单图解

静态代理 #

静态代理的作用,可扩展性,可维护性相对较差。典型的静态代理可以通过继承包装器两种方式来实现,包装器方式比起继承方式稍方便。

继承方式的静态代理 #

 1/**
 2 * @author:wangy
 3 * @date: 2018/9/21 / 15:15
 4 * @description: 通过继承的方式实现java静态代理, 主要在于理解所谓"代理"的概念
 5 */
 6public interface CellPhone {
 7    void sendMessage();
 8}
 9
10/**
11 * 模拟被代理类对象
12 */
13public class Iphone5 implements CellPhone {
14
15    @Override
16    public void sendMessage() {
17        System.out.println("the sending message is in the air.");
18    }
19}
20
21/**
22 * 模拟代理类对象
23 */
24public class Iphone5S extends Iphone5 {
25    @Override
26    public void sendMessage() {
27        System.out.println("you need to unlock your phone first.");
28        super.sendMessage();
29        // 模拟短信发送
30        try {
31            Thread.sleep(1000);
32        } catch (InterruptedException e) {
33            e.printStackTrace();
34        }
35        System.out.println("message sent successful.");
36    }
37}
38
39/**
40 *客户端
41 */
42public class Client {
43    public static void main(String[] args) {
44        CellPhone phone = new Iphone5S();
45        phone.sendMessage();
46    }
47}
48
49///:~
50you need to unlock your phone first.
51the sending message is in the air.
52message sent successful.

上例中,Iphone5S 类继承了Iphone5,并对5的功能“加了点椰果”,这就是最简单的代理模式。但是,如果我还想“加点奶油”,那么就需要再创建一个类,给5“加奶油”,如果有很多手机,要加很多功能,只能通过“硬编码”来完成,这样显然是不合适的。

包装器方式的静态代理 #

 1/**
 2 * @author:wangy
 3 * @date: 2018/9/21 / 15:15
 4 * @description: 通过包装器的方式实现java静态代理
 5 */
 6 public interface IceCream {
 7    void iceCreamMaker();
 8}
 9
10/**
11 * 模拟被代理类对象
12 */
13 public class BerryCream implements IceCream {
14    @Override
15    public void iceCreamMaker() {
16        System.out.println("this is a IceCream with blueberry juice.");
17    }
18}
19
20 /**
21  * 模拟代理类对象
22  */
23  public class Jelly implements IceCream {
24    // 代理对象中封装了被代理对象
25    private BerryCream berryCream;
26
27    public Jelly(BerryCream berryCream) {
28        this.berryCream = berryCream;
29    }
30
31    @Override
32    public void iceCreamMaker() {
33        System.out.println("add Grass jelly into Berry IceCream");
34        berryCream.iceCreamMaker();
35        System.out.println("now it's a Grass-jelly-Berry-IceCream");
36    }
37}
38
39 /**
40  * 模拟客户端
41  */
42    public class Client {
43    public static void main(String[] args) {
44
45        IceCream iceCream = new Jelly(new BerryCream());
46        iceCream.iceCreamMaker();
47    }
48}
49
50/*
51add Grass jelly into Berry IceCream
52this is a IceCream with blueberry juice.
53now it's a Grass-jelly-Berry-IceCream
54*///:~

在包装器模型中,被代理类对象被封装在代理类对象中。通过这种形式,如果我需要在“莓派冰激凌”上加上“烧仙草”和“珍珠果”,只需要将代码作一些改动,并且,其相对于继承模式的又是在于:继承是类似于一个链式的叠加,并且对于功能的改动比较麻烦(比如想先加珍珠果再加烧仙草,就需要改动继承关系)。而包装器设计就相对比较灵活了,只需要作简单改动:

 1// 将代理类对象1的封装类直接改为接口
 2public class Jelly implements IceCream {
 3    private  IceCream iceCream;
 4
 5    public Jelly(IceCream iceCream) {
 6        this.iceCream = iceCream;
 7    }
 8
 9    @Override
10    public void iceCreamMaker() {
11        System.out.println("add Grass jelly into Berry IceCream");
12        iceCream.iceCreamMaker();
13        System.out.println("now it's a Grass-jelly-Berry-IceCream");
14    }
15}
16
17// 添加代理对象2
18public class Pearl implements IceCream {
19    private  IceCream iceCream;
20
21    public Pearl(IceCream iceCream) {
22        this.iceCream = iceCream;
23    }
24
25    @Override
26    public void iceCreamMaker() {
27        System.out.println("add pearl fruit into Berry Ice Cream.");
28        iceCream.iceCreamMaker();
29        System.out.println("now it's a what a icecream!");
30    }
31}
32
33// 测试类
34public class Client {
35    public static void main(String[] args) {
36        // 如果想改变“加料”的顺序,只需要改变对象初始化的顺序就ok
37        IceCream iceCream = new BerryCream();
38        Pearl pearl = new Pearl(iceCream);
39        Jelly jelly = new Jelly(pearl);
40        jelly.iceCreamMaker();
41    }
42}
43
44///:~
45add Grass jelly into Berry IceCream
46add pearl fruit into Berry Ice Cream.
47this is a IceCream with blueberry juice.
48now it's a what a icecream!
49now it's a Grass-jelly-Berry-IceCream

相较于静态代理,动态代理带来的好处是:一个代理工具(或者叫代理处理程序),能够有效地实现对不同委托类的代理,这样使代码更加灵活。代理模式一定程度上可以看作“功能增强”,而“功能增强”的需求本质上就是相对灵活的,这和动态代理的初衷相契合。

JDK 动态代理 #

JDK 动态代理是比较常见的代理模式。其实现方法可大致总结为:

  • 1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
  • 2.创建被代理的类以及接口
  • 3.通过Proxy的静态方法 newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理类实例
  • 4.通过代理类实例调用委托类方法

1. 动态代理需要一个接口 #

这个接口供所有需要被代理的类实现

1public interface Subject {
2
3    void sayHallo();
4}

2. 和一个简单的实现类 #

1public class RealSubject implements Subject {
2    @Override
3    public void sayHallo() {
4        System.out.println("大家好!");
5    }
6}

3. 自定义代理处理程序,须实现 InvocationHandler 接口 #

 1public class MyProxyHandler implements InvocationHandler {
 2    private Subject subject;
 3
 4    MyProxyHandler() {
 5    }
 6
 7    MyProxyHandler(Subject subject) {
 8        this.subject = subject;
 9    }
10
11    /**
12     * 此方法用于生成一个指定接口的代理类实例, 该接口可以将方法调用指派到 [指定的调用处理程序],
13     * 这个所谓的 [调用处理程序] 就是 InvocationHandler的invoke()方法
14     *
15     * @param subject 需要被代理的类的实例对象(被代理接口的实现类)
16     * @return 一个指定接口的代理类实例
17     * 这里, 指定接口就是说的是 Subject 接口, 常说的JDK动态代理需要有一个接口,就是这个原因
18     */
19     Object bind(Subject subject) {
20        this.subject = subject;
21        return Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), this);
22        // 此Proxy类的静态方法等价于
23        /*try {
24            // 获取实现指定接口的被代理类实例对象
25            Class<?> proxyClass = Proxy.getProxyClass(Subject.class.getClassLoader(), Subject.class.getInterfaces());
26            // 获取指定的 [调用处理程序对象] 的构造器
27            Constructor<?> proxyClassConstructor = proxyClass.getConstructor(MyProxyHandler.class);
28            // 通过指定的InvocationHandler实例创建实现指定接口的代理类实例对象
29            return proxyClassConstructor.newInstance(new MyProxyHandler(subject));
30        }catch(Exception e){
31            e.printStackTrace();
32        }*/
33    }
34
35    /*  ********************************************
36     *  java.lang.reflect.Proxy [extends Object implements Serializable] 类                                            
37     *      -- 该类提供用于创建动态代理类和实例的静态方法, 它是由这些静态方法创建的动态代理类的超类
38     *      -- 动态代理类(以下称为代理类)是一个实现 [在创建类时在运行时指定的接口列表] 的类                             
39     *      -- 代理接口(Subject)是代理类实现的一个接口                                                                  
40     *      -- 代理实例是代理类的一个实例, 每个代理实例都有一个关联的 [调用处理程序] 对象, 其实现接口 InvocationHandler    
41     *      -- 代理实例调用方法(sayHallo())时, 会被指派到 [调用处理程序] 对象的 invoke() 方法                           
42     *  ********************************************/
43
44    /**
45     * @param proxy  代理类对象
46     * @param method 被代理类的方法实例
47     * @param args   被代理类对象(subject)的方法实例method的参数
48     * @return null
49     * @throws Throwable
50     */
51    @Override
52    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
53        System.out.println("before...");
54        method.invoke(subject, args);
55        System.out.println("after...");
56        return null;
57    }
58}

4. 客户端生成代理类对象并且调用委托类的方法 #

这个client介绍了3种创建动态代理类的方法

 1public class Client {
 2    /**
 3     * 被代理类对象(接口)
 4     */
 5    private static Subject subject = new RealSubject();
 6    /**
 7     * 代理实例关联的调用处理程序对象实例
 8     */
 9    private static InvocationHandler handler = new MyProxyHandler(subject);
10    /**
11     * 代理实例关联的调用处理程序对象, 一般是自定义的InvocationHandler类的实现类
12     */
13    private static MyProxyHandler my_proxy = new MyProxyHandler();
14
15    public static void main(String[] args) {
16        m1();
17        m2();
18        m3();
19    }
20
21    /**
22     * 通过 [代理类实例] 调用 [被代理类] 实例的指定方法时, 会被[自定义代理类处理程序]指派给 [调用处理程序](即invoke()方法),并且执行invoke方法的增强代码
23     */
24    private static void m3() {
25        Subject proxySubject = (Subject) my_proxy.bind(subject);
26        proxySubject.sayHallo();
27    }
28
29    private static void m2() {
30        Subject proxy_subject = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler);
31        proxy_subject.sayHallo();
32    }
33
34    private static void m1() {
35        Subject proxySubject = null;
36        try {
37            Class<?> proxyClass = Proxy.getProxyClass(Subject.class.getClassLoader(), Subject.class);
38            Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
39            proxySubject = (Subject) constructor.newInstance(handler);
40        } catch (Exception e) {
41            e.printStackTrace();
42        }
43        proxySubject.sayHallo();
44    }

cglib 动态代理 #

与JDK动态代理不同的是,cglib动态代理不需要委托类实现某个接口,其生成的代理类是委托类的子类。当然,cglib也有其局限:

  1. final 类不能通过cglib代理
  2. final 修饰的方法不能通过cglib作增强处理

cglib实现动态代理可简单地归纳为:

  • 1.创建委托类对象
  • 2.创建[自定义代理类生成程序]类,该类须实现MethodInterceptor
  • 3.通过Enhancer来创建代理类实例
  • 4.通过代理类实例调用委托类方法

1.创建委托类 #

1public class Flower {
2    public void bloom(){
3        System.out.println("the flower will bloom...");
4    }
5}

2. 自定义代理类生成程序 #

 1public class ProxyInterceptor implements MethodInterceptor {
 2    private Enhancer enhancer = new Enhancer();
 3
 4    Object getProxyClass(Class clazz){
 5        enhancer.setSuperclass(clazz);
 6        enhancer.setCallback(this);
 7        return enhancer.create();
 8    }
 9    @Override
10    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
11        System.out.println("after you fertilized...");
12        methodProxy.invokeSuper(o, objects);
13        System.out.println("and you will have a harvest.");
14        return null;
15    }
16}

3. 客户端 #

1public class Client {
2    private static ProxyInterceptor proxyInterceptor =  new ProxyInterceptor();
3    public static void main(String[] args) {
4        Flower proxyClass = (Flower) proxyInterceptor.getProxyClass(Flower.class);
5        proxyClass.bloom();
6    }
7}

JDK和cglib的区别 #

代理特点优点缺点
JDK代理类与委托类实现同一接口,主要是通过代理类实现InvocationHandler并重写invoke方法来进行动态代理的,在invoke方法中将对方法进行增强处理,底层使用反射机制进行方法的调用不需要硬编码接口,代码复用率高只能够代理实现了接口的委托类
cglib代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理,底层将方法全部存入一个数组中,通过数组索引直接进行方法调用可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口不能对final类以及final方法进行代理
JDK和cglib动态代理的简易区别

解析JDK动态代理 #

  • To be done...