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动态代理实现原理(未完待续················)

  • 相关阅读:
    hdu 6702 ^&^ 位运算
    hdu 6709 Fishing Master 贪心
    hdu 6704 K-th occurrence 二分 ST表 后缀数组 主席树
    hdu 1423 Greatest Common Increasing Subsequence 最长公共上升子序列 LCIS
    hdu 5909 Tree Cutting FWT
    luogu P1588 丢失的牛 宽搜
    luogu P1003 铺地毯
    luogu P1104 生日
    luogu P1094 纪念品分组
    luogu P1093 奖学金
  • 原文地址:https://www.cnblogs.com/zplogo/p/6432502.html
Copyright © 2011-2022 走看看