代理模式
代理可以简单理解为,B类托管A类的功能,并根据需求,对A类的访问作控制,这里的控制可以理解为对A类方法执行的流程的影响,包括但不限于:
在方法执行之前,先做其他事(前置通知) 在方法执行之后,再做某事(后置通知) 决定方法是否执行(环绕通知) ...
Java中代理的主要应用体现在权限控制
,日志管理
,事务控制
等
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也有其局限:
- final 类不能通过cglib代理
- 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方法进行代理 |
cglib
动态代理的简易区别解析JDK动态代理 #
- To be done...