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 }
日志记录代理类 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 }
客户端测试类 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 }
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 }
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 }
思考作业:
我们用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动态代理实现原理(未完待续················)