代理模式
代理模式定义
为某个对象(目标对象)提供一种代理对象以控制对这个对象的访问。在某些情况下在(比如安全性问题),客户端不能直接访问某个对象(目标对象),还是通过代理对象间接访问目标对象,代理对象在客户端和目标对象之间起到中介的作用,而且可以通过代理对象对目标对象的功能进行扩展,代理对象一般对目标对象的非核心业务的扩展,比如权限控制问题,记录日志,事务控制等等。我们日常的生活的代理模式例子有房产中介,明星经纪人,火车票代售点等等,在工作中代理模式有windows快捷键,spring框架aop等等
使用代理模式的原因
- 中介隔离作用:在某些情况下,客户端不能直接访问某个对象(目标对象),还是通过代理对象间接访问目标对象,代理对象器中介的作用。
- 开闭原则,增加功能:代理模式遵循设计模式的开闭原则,目标对象的功能的扩展是通过新增一个代理类,在代理类中进行扩展,而不是在目标类中修改代码,进行扩展。
代理模式分类
静态代理:创建一个接口,代理类,目标类实现同一个接口,代理类持有目标对象的一个引用。创建一个飞机类为例子,创建飞机的代理类,计算飞机移动的时间,记录飞机飞行过程的(日志)类图如下:
代码如下:
1:创建一个接口Movable接口(抽象类也行)
/**
* 该接口用来指定代理的方法(需要增加内容的方法),代理对象,被代理对象都该继承该接口 * @author Administrator * */ public interface Movable { public void move() throws InterruptedException; }
2:创建目标类Plane,实现Movable接口
/** * 需要代理的对象飞机类,实现movable接口 * @author Administrator * */ public class Plane implements Movable { public void move() throws InterruptedException { System.out.println("plane luanching..."); Thread.sleep(new Random().nextInt((int) (1000))); } }
3.1:创建一个时间代理类PlaneTimeProxy,实现movable接口,并持有一个Movable类型的引用。
/** * 飞机类的时间代理对象,用来计算飞机飞行的时间,即是飞机对象move方法调用的时间。 * @author Administrator * */ public class PlaneTimeProxy implements Movable{ Movable mover; public PlaneTimeProxy(Movable mover){ this.mover=mover; } public void move() throws InterruptedException { // TODO Auto-generated method stub long begin=System.currentTimeMillis(); mover.move(); long end =System.currentTimeMillis(); System.out.println("plane time:"+(end-begin)); } }
飞机飞行记录代理类PlaneLogProxy(日志)实现movable接口,并持有一个Movable类型的引用。
/** * 飞机飞行过程记录代理类(日志) * @author Administrator * */ public class PlaneLogProxy implements Movable { Movable mover; public PlaneLogProxy(Movable mover){ this.mover=mover; } public void move() throws InterruptedException { System.out.println("plane begin"); mover.move(); System.out.println("plane end"); } }
4:客户端测试类
/** *静态代理测试类 */ public class TestProxy { public static void main(String[] args) throws InterruptedException { Movable p=new Plane(); //日志代理类 PlaneLogProxy plp=new PlaneLogProxy(p); plp.move(); //时间代理类型 PlaneTimeProxy simplePtp =new PlaneTimeProxy(p); simplePtp.move(); //时间代理类嵌套日志代理类 PlaneTimeProxy ptp=new PlaneTimeProxy(plp); ptp.move(); } }
静态代理总结:使用静态代理对目标对象功能的扩展,比继承更加灵活,比如上面的例子,如果即需要记录飞机时间,记录飞行的日志,就需要再在新建一个类实现Movable接口,如果用静态代理则可以通过嵌套方式实现。静态代理的不足,1:如果一个目标类被多个类代理,则需要创建多个代理类,而且代理类的逻辑相似,重复代码写了多遍,2:如果在接口增加一个方法,则需要修改代理类跟目标类的逻辑,为了解决上面的问题就产生了动态代理了,动态代理有二种,一种是jdk代理,一种是cglib代理。
jdk代理:代理对象是由jdk动态生成,不需要我们手动创建代理对象,代理对象是通过Proxy类的newProxyInstance方法创建,我们只需要创建一个类实现InvocationHandler接口,重写invoke方法对目标对象的功能进行增强。还是以上面的l例子的日志代理类为例,接口跟目标类已经有了,就不再在下面展示了,代码如下:
创建一个类实现InvocationHandler接口,该类持有一个Movable类型的引用,目标对象的功能扩展通过重写invoke方法实现:
public class MyHandler implements InvocationHandler{ Movable mover; public MyHandler(Movable mover) { super(); this.mover = mover; } /** * * @param proxy 第一个参数指的是jdk生成的代理对象 * @param method 第二个参数指的是目标对象的目标方法 * @param args 第三个参数指的是目标对象的目标方法的参数 * @return 方法返回值为调用目标方法的返回值 * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("plane begin");//新增的逻辑 Object obj=method.invoke(mover, args);//调用目标对象的目标方法 System.out.println("plane end");//新增的逻辑 return obj; } }
客户端测试类:
/** * jdk动态代理:用反射技术,由jdk动态在内存生成代理对象。 * @author Administrator * */ public class TestJdkProxy { public static void main(String[] args) throws InterruptedException { Movable p= (Movable) new Plane(); MyHandler h=new MyHandler(p); /** *通过Proxy类的newProxyInstance方法创建代理对象,newProxyInstance方法:第一个参数表示目标类的类加载器, * 第二个参数指定目标类实现的接口的字节码数组对象, * 第三个参数指的是实现目标方法扩展的InvocationHandler实现类。 */ Movable mover=(Movable) Proxy.newProxyInstance(Plane.class.getClassLoader(), new Class[]{Movable.class}, h); mover.move(); } }
jdk代理总结:jdk代理用反射技术,由jdk动态在内存生成代理对象,将创建代理对象的工作交给jdk,不需要自己创建各种代理类,减少了我们开发任务,当在接口中新增一个方法时,只需要在目标类中新增这个方法的逻辑,jdk代理类会自动新增这个方法,解决静态代理的不足。但是jdk代理也有不足,如果目标对象不实现接口,则无法产生代理对象,所以当目标类没有实现接口,想对目标类功能进行扩展,这时就需要用cglib代理实现。
cglib代理:cglib代理也叫子类代理,通过在内存构建一个子类,从而实现目标对象功能的扩展,目标对象不需要实现接口,也就是说代理类是目标类的子类。cglig代理对象由Enhancer类来创建,cgliib代理只需要编写一个拦截器类实现MethodInterceptor接口,重写interceptor方法,在intercept方法中对目标对象的功能进行增强操作。还是以上面的列子的日志代理类为例,接口跟目标类已经有了,就不再在下面展示了,代码如下:
创建一个拦截器类实现MethodInterceptor接口,持有一个Movable类型的引用,通过重写intercept方法来实现目标类功能的增强。
public class MyInterceptor implements MethodInterceptor{ Movable mover; public MyInterceptor(Movable mover) { this.mover = mover; } @Override public Object intercept(Object proxy, Method arg1, Object[] arg2, MethodProxy methodProxy) throws Throwable { System.out.println("plane begin"); //Object obj=methodProxy.invoke(mover, arg2); Object obj=methodProxy.invokeSuper(proxy,arg2); System.out.println("plane end"); return obj; } }
客户端测试类:
/** * cglib代理也叫子类代理,通过在内存构建一个子类,从而实现目标对象功能的扩展,目标对象不需要实现接口(当然实现也可以),本例实现了接口 * 优点:1:解决了jdk动态代理的目标对象一定要实现接口的缺点。 * 2:CGLIB 是一个强大的,高性能的代码生成库,它可以在运行期扩展java类。 * 3:CGLIB 底层是使用小而快的字节码处理框架asm,来转换字节码,生成代理类。 * @author Administrator * */ public class TestCgLibProxy { public static void main(String[] args) throws InterruptedException { Enhancer enhancer=new Enhancer();//定义一个增强器 enhancer.setSuperclass(Plane.class);//设置目标对象的父类为代理类的父类 Movable m= (Movable) new Plane(); //Plane m=new Plane(); MyInterceptor callback=new MyInterceptor(m);//创建自定义拦截器对象 enhancer.setCallback(callback);//设置回调对象即是拦截器 Plane proxy=(Plane) enhancer.create();//创建代理对象 proxy.move(); } }
cglib代理总结:cglib代理解决了jdk代理的目标类必须实现接口的缺点。一般当目标对象(被代理对象)没有实现接口,则用cglib代理,当目标对象实现某个接口时,如果创建的代理对象是单例对象并且目标类不被fianl修饰时,则选用cglib代理,因为cglib创建的态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多,因为cglib采用继承方法产生代理对象,就产生的代理对象继承目标对象。final修饰的类不能继承的,继续否则就用jdk代理。