zoukankan      html  css  js  c++  java
  • 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动态代理实现原理(未完待续················)

  • 相关阅读:
    程序用windows快捷键
    vc6中如何加入BMP图片资源
    vc 中解析字符串
    视频压缩编码问与答
    ActiveX and Com
    Windows 2000 IIS 安装、配置.WEB篇
    javascript 如何调用activex的方法和属性
    bmp 转为jpg
    Writing a Custom Membership Provider for the Login Control in ASP.NET 2.0
    几种DC及区别
  • 原文地址:https://www.cnblogs.com/zplogo/p/6432502.html
Copyright © 2011-2022 走看看