Java设计模式(05-- 代理模式模式 )

学习代理模式之前需要了解的内容
1)面向对象的设计思想 2)了解多态概念 3)了解反射原理

本课程包括代理模式的概念及其分类、了解代理模式开发中的应用场景、掌握代理模式的实现方式、
理解JDK动态代理的实现。

代理模式的定义:
为其他对象提供一种代理以控制对这个对象的访问,代理对象起到中介作用,可去掉功能服务或者增加
额外的服务。

分类:
1)远程代理:为不同地理的对象提供局域网代表对象,类似客户端和服务器这种模式。
2)虚拟代理:根据需求将资源消耗很大的对象进行延迟处理,真正需要的时候再进行创建。(如一篇文章
的图片很大,我们可以用代替让图片进行延迟加载)
3)保护代理:如论坛的访问权限,我们就可以通过保护代理来实现。
4)智能引用代理:最常用的代理,一般都是需要在代理的服务之前增添某些功能。

以智能引用代理为例其实现方式有静态代理和动态代理两种方式。

静态代理:代理和被代理对象在代理之前都是确定的,他们都是实现了相同的接口或者继承了相同的抽象类。
实现静态方法可以通过继承的方式和聚合的方式来实现
实例:对行驶的汽车进行计时服务。

目标接口(被代理类和代理类都需要实现的接口类):Moveable

 

1 package com.imooc.pattern.proxy;
2 
3 public interface Moveable {
4     void move();
5 }

 

被代理类(提供汽车行驶功能)Car

 1 package com.imooc.pattern.proxy;
 2 
 3 import java.util.Random;
 4 
 5 public class Car implements Moveable {
 6 
 7     public void move() {
 8         try {
 9             Thread.sleep(new Random().nextInt(1000));
10             System.out.println("汽车行驶中······");
11         } catch (InterruptedException e) {
12             e.printStackTrace();
13         }
14     }
15 
16 }

 

使用继承方式实现静态代理的代理类 CarExtends

 1 package com.imooc.pattern.proxy;
 2 
 3 /**
 4  * 通过继承的方式实现静态代理
 5  * CarExtends通过重写move方法,调用super.move()方法实现了对父类Car的代理,
 6  * 同时在super.move()方法前后加入了自己提供的额外服务
 7  * @author zplogo
 8  *
 9  */
10 public class CarExtends extends Car {
11 
12     public void move() {
13         long startTime = System.currentTimeMillis();
14         System.out.println("汽车开始行驶······");
15         super.move();
16         long endTime = System.currentTimeMillis();
17         System.out.println("汽车终止行驶····本次行程共花费时间:"+(endTime-startTime)+"毫秒");
18     }
19     
20 }

 

使用聚合方式实现静态代理的代理类 Car3

 1 package com.imooc.pattern.proxy;
 2 
 3 /**
 4  * 使用聚合的方式实现静态代理
 5  * 聚合就是在一个类中需要调用另一个对象,通过构造方法进行注入,然后在实现接口的方法中调用被代理对象的方法
 6  * 在该方法前后加入自己的逻辑功能
 7  * @author zplogo
 8  *
 9  */
10 public class Car3 implements Moveable {
11     
12     private Car car;
13     
14     public Car3(Car car) {
15         super();
16         this.car = car;
17     }
18 
19     public void move() {
20         long startTime = System.currentTimeMillis();
21         System.out.println("汽车开始行驶····计时开始··");
22         car.move();
23         long endTime = System.currentTimeMillis();
24         System.out.println("汽车终止行驶····本次行程共花费时间:"+(endTime-startTime)+"毫秒");
25     }
26 
27 }

 

继承方式和聚合方式哪种更好??
现实情况我们需要在代理服务前后做很多功能的叠加,如在move()方法前后不仅需要做计时功能还需要权限功能,日志记录服务。
而Java是单继承的,继承的方式实现静态代理来达到功能的叠加,继承将会无限膨胀下去,因此并不可取。
聚合的方式需要代理类和被代理类都实现相同的接口。

现在我们用聚合的方式完成汽车行驶计时和日志功能的叠加

时间代理类 CarTimeProxy

 1 package com.imooc.pattern.proxy;
 2 
 3 /**
 4  * 使用聚合的方式实现静态代理   
 5  * 功能:在代理汽车行驶功能的同时加入了计时功能
 6  * 聚合就是在一个类中需要调用另一个对象,通过构造方法进行注入,然后在实现接口的方法中调用被代理对象的方法
 7  * 在该方法前后加入自己的逻辑功能
 8  * @author zplogo
 9  *
10  */
11 public class CarTimeProxy implements Moveable {
12     
13     private Moveable m;
14     
15     //这里由于代理类和被代理类都实现了相同的接口,所以我们可以传入Moveable对象
16     public CarTimeProxy(Moveable m) {
17         super();
18         this.m = m;
19     }
20 
21     public void move() {
22         long startTime = System.currentTimeMillis();
23         System.out.println("汽车开始行驶····计时开始··");
24         m.move();
25         long endTime = System.currentTimeMillis();
26         System.out.println("汽车终止行驶····本次行程共花费时间:"+(endTime-startTime)+"毫秒");
27     }
28 
29 }
View Code

 

日志记录代理类 CarLogProxy

 1 package com.imooc.pattern.proxy;
 2 
 3 /**
 4  * 使用聚合的方式实现静态代理   
 5  * 功能:在代理汽车行驶功能的同时加入了日志记录的功能
 6  * 聚合就是在一个类中需要调用另一个对象,通过构造方法进行注入,然后在实现接口的方法中调用被代理对象的方法
 7  * 在该方法前后加入自己的逻辑功能
 8  * @author zplogo
 9  *
10  */
11 public class CarLogProxy implements Moveable {
12     
13     private Moveable m;
14     
15     //这里由于代理类和被代理类都实现了相同的接口,所以我们可以传入Moveable对象
16     public CarLogProxy(Moveable m) {
17         super();
18         this.m = m;
19     }
20 
21     public void move() {
22         System.out.println("日志记录开始········");
23         m.move();
24         System.out.println("日志记录结束········");
25     }
26 
27 }
View Code

 

客户端测试类 ClientTest

 1 package com.imooc.pattern.proxy;
 2 
 3 import com.imooc.pattern.cglibproxy.CglibProxy;
 4 import com.imooc.pattern.cglibproxy.Train;
 5 
 6 public class ClientTest {
 7 
 8     public static void main(String[] args) {
 9 //        Car car = new Car();
10 //        car.move();
11         //通过继承的方式实现代理父类的功能,同时增加额外的功能
12 //         Moveable m = new CarExtends();
13 //         m.move();
14         //使用聚合的方式实现静态代理,把被代理对象注入代理类构造方法,代理类实现接口方法中实现代理同时加入自己的功能
15 //        Car car = new Car();
16 //        Moveable m = new Car3(car);
17 //        m.move();
18         
19         //用聚合的方式实现静态代理比继承灵活,完成计时功能和日志功能的叠加【这种设计模式我们发现做功能的叠加非常好,代码清晰便于维护】
20         //先完成计时功能,然后叠加日志记录功能
21         /*Car car = new Car();
22         CarTimeProxy ctp = new CarTimeProxy(car);
23         CarLogProxy clp = new CarLogProxy(ctp);
24         clp.move();*/
25         //先完成日志记录功能,然后叠加计时功能
26     /*    Car car = new Car();
27         CarLogProxy clp = new CarLogProxy(car);
28         CarTimeProxy ctp = new CarTimeProxy(clp);
29         ctp.move();*/
30         
31         //测试Cglib产生代理类,好像cglib使用的继承方式完成动态代理
32         CglibProxy cglibProxy = new CglibProxy();
33         Train t = (Train)cglibProxy.getProxy(Train.class);//在getProxy()方法中已经设置了代理类的父类为Train.class
34         t.move();
35     }
36 
37 }
View Code

 

 

JDK动态代理
首先被代理对象需要实现某些接口,运行时产生代理类的class对象,我们需要定义一个Hander实现InvocationHandler接口,让它接管业务,具体业务在Handler中实现。

JDK动态代理的实现步骤
1.创建实现InvocationHandler接口的类,它必须实现invoke方法。
2.创建被代理类及接口。
3.调用Proxy的静态方法创建一个代理类,newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
4.通过代理类调用方法

实例

汽车计时处理类 TimeHandler

 1 package com.imooc.pattern.jdkproxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 
 6 public class TimeHandler implements InvocationHandler {
 7     
 8     private Object target;
 9     
10     public TimeHandler(Object target) {
11         super();
12         this.target = target;
13     }
14     
15     /*
16      * 参数:
17      * proxy 被代理对象
18      * method 被代理对象的方法
19      * args 方法的参数
20      * 
21      * 返回值:
22      * Object 该方法的返回对象
23      */
24     public Object invoke(Object proxy, Method method, Object[] args)
25             throws Throwable {
26         long startTime = System.currentTimeMillis();
27         System.out.println("汽车开始行驶····计时开始··");
28         method.invoke(target, null);//通过传入被代理的对象,调用被代理对象的方法完成代理,(在该行上下加入额外的功能)
29         long endTime = System.currentTimeMillis();
30         System.out.println("汽车终止行驶····本次行程共花费时间:"+(endTime-startTime)+"毫秒");
31         return null;
32     }
33 
34 }
View Code

 

JDK动态代理测试类 JdkProxyTest

 1 package com.imooc.pattern.jdkproxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Proxy;
 5 
 6 import com.imooc.pattern.proxy.Car;
 7 import com.imooc.pattern.proxy.Moveable;
 8 
 9 public class JdkProxyTest {
10 
11     public static void main(String[] args) {
12         Car car = new Car();
13         InvocationHandler h = new TimeHandler(car);
14         Class<?> cls=car.getClass();
15         /*
16          * loader 被代理类的类加载器
17          * interfaces 实现接口
18          * h  InvocationHandler
19          */
20         Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces() , h);
21         m.move();
22     }
23 
24 }
View Code

 

思考作业:
我们用JDK完成了对Car运行时间的记录,另外我们需要在进行叠加记录日志的功能。

 

CgLib实现动态代理

首先需要引入Cglib的jar包 cglib-nodep-2.2.jar

实例代码:

被代理类 Train

1 package com.imooc.pattern.cglibproxy;
2 
3 public class Train {
4     public void move(){
5         System.out.println("火车行驶中········");
6     }
7 }

Cglib代理类  CglibProxy

 1 package com.imooc.pattern.cglibproxy;
 2 
 3 import java.lang.reflect.Method;
 4 
 5 import net.sf.cglib.proxy.Enhancer;
 6 import net.sf.cglib.proxy.MethodInterceptor;
 7 import net.sf.cglib.proxy.MethodProxy;
 8 
 9 public class CglibProxy implements MethodInterceptor {
10     
11     private Enhancer enhancer = new Enhancer();//创建代理类的属性
12     
13     public Object getProxy(Class clazz){
14         enhancer.setSuperclass(clazz);//设置创建子类的类(被代理类)
15         enhancer.setCallback(this);
16         return enhancer.create();
17     }
18     
19     /*
20      * 拦截所有目标类的方法的调用
21      * object 目标类的实例
22      * m 目标方法反射的对象
23      * args 方法的参数
24      * proxy 代理类的实例
25      */
26     @Override
27     public Object intercept(Object object, Method m, Object[] args,
28             MethodProxy proxy) throws Throwable {
29         System.out.println("开始记录日志·······");
30         //代理类调用父类的方法
31         proxy.invokeSuper(object, args);
32         System.out.println("结束记录日志·······");
33         return null;
34     }
35 
36 }

 

测试代码见上面的客户端测试类 。

智能引用代理在实际开发中应用最广泛,可以进行日志处理,权限管理,事务管理。

动态代理在面向切面编程(AOP)非常有用。

 

模拟JDK动态代理实现原理(未完待续················)

 

posted on 2017-02-23 11:03  ${}  阅读(338)  评论(0编辑  收藏  举报

导航