Fork me on GitHub

代理模式

代理模式定义

为某个对象(目标对象)提供一种代理对象以控制对这个对象的访问。在某些情况下在(比如安全性问题),客户端不能直接访问某个对象(目标对象),还是通过代理对象间接访问目标对象,代理对象在客户端和目标对象之间起到中介的作用,而且可以通过代理对象对目标对象的功能进行扩展,代理对象一般对目标对象的非核心业务的扩展,比如权限控制问题,记录日志,事务控制等等。我们日常的生活的代理模式例子有房产中介,明星经纪人,火车票代售点等等,在工作中代理模式有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代理。

 
 
posted @ 2020-05-03 23:36  carrykai  阅读(207)  评论(0编辑  收藏  举报