代理模式

静态代理

1)定义一个接口

package com.imooc.proxy;

public interface Moveable {
    void move();
}

2)被代理类

package com.imooc.proxy;

import java.util.Random;

public class Car implements Moveable {

    @Override
    public void move() {
        //实现开车
        try {
            Thread.sleep(new Random().nextInt(1000));
            System.out.println("汽车行驶中....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3)代理类

日志代理:

package com.imooc.proxy;

public class CarLogProxy implements Moveable {

    public CarLogProxy(Moveable m) {
        super();
        this.m = m;
    }

    private Moveable m;
    
    @Override
    public void move() {
        System.out.println("日志开始....");
        m.move();
        System.out.println("日志结束....");
    }

}

记时代理

package com.imooc.proxy;

public class CarTimeProxy implements Moveable {

    public CarTimeProxy(Moveable m) {
        super();
        this.m = m;
    }

    private Moveable m;
    
    @Override
    public void move() {
        long starttime = System.currentTimeMillis();
        System.out.println("汽车开始行驶....");
        m.move();
        long endtime = System.currentTimeMillis();
        System.out.println("汽车结束行驶....  汽车行驶时间:" 
                + (endtime - starttime) + "毫秒!");
    }

}

测试类:

package com.imooc.proxy;

public class Client {

    /**
     * 测试类
     */
    public static void main(String[] args) {
        Car car = new Car();
        CarLogProxy clp = new CarLogProxy(car);
        CarTimeProxy ctp = new CarTimeProxy(clp);
        ctp.move();
    }

}

结果:

**聚合方式要优于继承方式

继承:继承父类想要的功能

聚合:传入参数,实现想要的父类

如果为继承方式,如果日志和时间的位置要调换,那么需要新定义一个子类

而继承方式只需要在测试类中,调换位置就行

动态代理:

动态代理不必为特定的对象与方法编写特定的代理对象,使用动态代理,可以使得一个处理折Handler服务于各个对象

接口被代理类不用改变

时间代理类:!!继承InvocationHandler的接口

 

package com.imooc.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TimeProxyHandler implements InvocationHandler {

    private Object target;
    public TimeProxyHandler(Object target) {
        super();//调用父类的构造方法
        this.target = target;
    }
    
    /*
     * 参数:
     * proxy  被代理对象
     * method  被代理对象的方法
     * args 方法的参数
     * 
     * 返回值:
     * Object  方法的返回值
     * */
    @Override
    public Object invoke(Object proxy, Method mothod, Object[] args) throws Throwable {
        System.out.println("运行开始");
        long starttime = System.currentTimeMillis();
        mothod.invoke(target);
        long endtime = System.currentTimeMillis();
        System.out.println("运行结束,运行时间为:"+(endtime-starttime)+"毫秒");
        return null;
    }
    
    //获得代理对象
    public static Object getTimeProxy(Object object){
        InvocationHandler h = new TimeProxyHandler(object);
        Class<?> cls = object.getClass();
        /**
         * loader  类加载器
         * interfaces  实现接口
         * h InvocationHandler
         **/
        return Proxy.newProxyInstance(cls.getClassLoader(),
                                                cls.getInterfaces(), h);
        
    }
}

 

测试类

package com.imooc.proxy;

public class Test {
        /**
         * JDK动态代理测试类
         */
        public static void main(String[] args) {
            Moveable m = (Moveable) TimeProxyHandler.getTimeProxy(new Car());
/
/返回代理类,代理类是JVM在内存中动态创建的,该类实现传入的接口数组的全部接口(的全部方法).
       m.move(); 
}
}

 

InvocationHandler角色:

代理模式:

代理类处理的逻辑很简单:在调用某个方法前及方法后做一些额外的业务。

动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用具体对象角色的方法。如下图所示:

 

在这种模式之中:代理Proxy 和RealSubject 应该实现相同的功能,这一点相当重要。(我这里说的功能,可以理解为某个类的public方法)

在面向对象的编程之中,如果我们想要约定Proxy 和RealSubject可以实现相同的功能,有两种方式:

    a.一个比较直观的方式,就是定义一个功能接口,然后让Proxy 和RealSubject来实现这个接口。(JDK机制)

    1.   获取 RealSubject上的所有接口列表;
    2.   确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX ;

    3.   根据需要实现的接口信息,在代码中动态创建 该Proxy类的字节码;

    4 .  将对应的字节码转换为对应的class 对象;

    5.   创建InvocationHandler 实例handler,用来处理Proxy所有方法调用;

    6.   Proxy 的class对象 以创建的handler对象为参数,实例化一个proxy对象

    b.还有比较隐晦的方式,就是通过继承。因为如果Proxy 继承自RealSubject,这样Proxy则拥有了RealSubject的功能,Proxy还可以通过重写RealSubject中的方法,来实现多态。(cglib机制)

1.   查找A上的所有非final 的public类型的方法定义;

2.   将这些方法的定义转换成字节码;

3.   将组成的字节码转换成相应的代理的class对象;

4.   实现 MethodInterceptor接口,用来处理 对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)

CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

两者区别:

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

 

posted @ 2018-03-08 16:32  战斗的小白  阅读(170)  评论(0编辑  收藏  举报