代理模式(静态代理、JDK代理、cglib代理)的简单用法

代理模式

代理模式分为:静态代理,JDK代理,cglib代理。代理模式主要目的是我们需要做的事,本身不想做,然后交给一个代理去做,然后我们又可以在代理中对过程进行增强处理。我们通过这个代理类对我们需要代理的类控制访问,代理对象在客户端和目标对象的一个中介。
下面我们用一个例子来说明代理模式:
我们在学校中每个人都要吃饭,某一天我突然中午有事,或者太懒,不想去买饭吃,想让同学代我去买饭,这个时候同学就是我的代理,买饭这个操作就是我们需要代理的方法,当然也不止买饭,我们可以有很多操作可以让同学帮我们代理(洗衣服,拿快递等等)。那么我们通过代理模式怎么样用代码帮我们实现呢?
talk is cheap show me the code

静态代理:

首先看我们静态代理
我们先要新建代表全部同学的接口,代表我们学校每个人都要吃饭、洗衣服

public interface Schoolmate {

    /**
     * 买食物
     */
    public void buyFood();

    /**
     * 洗衣服
     */
    public void washClothes();
}

然后我们再新建一个代表我自己,当然也要需要实现这个接口,我自己也要吃饭,洗衣服

public class Myself implements Schoolmate {

    /**
     * 由于每一个同学都有一个要去买食物的动作,所以我们定义一个同学接口,接口中规定我们买饭的方法
     * 到我这里了,我突然有事,或者生病了,走不开,我们想要我们同学帮我们买饭这个操作,我们需要怎么做呢?
     * 新建一个代理类对象
     * @see JDKSchoolmate
     * @see Myself
     * 这个代理类就是代理我自己去买饭这一部操作。
     */
    @Override
    public void buyFood() {
        System.out.println("我自己不想去买食物,或者有事没时间,麻烦同学帮我买一点吃的。");
    }

    @Override
    public void washClothes() {
        System.out.println("你帮我洗衣服吧。");
    }
}

这个时候,正如我们所有,我现在自己不想去买饭,想找个人帮我去买,我就需要创建一个代理我去买饭的类,代表我想叫一个同学帮我买饭。这个类需要我们指定帮哪一个人买饭,所以我们应用一个我自己的类,具体是帮我买饭。

public class MyselfProxy implements Schoolmate {

    private Myself myself;

    //通过构造参数把我们需要指定需要代理的类传进来
    public MyselfProxy(Myself myself) {
        this.myself = myself;
    }

    @Override
    public void buyFood() {
        before();
        //调用我们需要被代理的方法
        this.myself.buyFood();
        after();
    }

    @Override
    public void washClothes() {

    }
}

到这里,我们整个静态代理模式帮我买饭的code就写完了,我们来测试一下

public class MyselfProxyTest {

    public static void main(String[] args) {
        //实例化我们的代理类,然后把我自己通过构造参数传入代理类,对代理类里面的引用类型进行绑定
        MyselfProxy myselfProxyTest = new MyselfProxy(new Myself());
        //直接调用我们代理的方法,寓意就是直接让我们代理对象帮我去买饭
        myselfProxyTest.buyFood();
    }
}

测试结果:
代理模式(静态代理、JDK代理、cglib代理)的简单用法_第1张图片
可以看到我们的代理类已经帮我执行了买饭这个操作。
我们前面所说,代理模式可以增强我们业务实现过程,那什么是增强我们的业务实现过程呢?比如这个时候我渴了,我想要代理先帮我买瓶水然后再去买饭,我的快递又打电话给我了,我又要代理帮我买完饭回来的路上,在帮我拿个快递,我们应该怎么改我们的代码呢?其实很简单,就在我们的代理类的买饭方法里面直接去调用我们买水和拿快递的两个方法就可以了。

public class MyselfProxy implements Schoolmate {

    private Myself myself;

    //通过构造参数把我们需要指定需要代理的类传进来
    public MyselfProxy(Myself myself) {
        this.myself = myself;
    }

    @Override
    public void buyFood() {
        before();
        //调用我们需要被代理的方法
        this.myself.buyFood();
        after();
    }

    @Override
    public void washClothes() {

    }

    public void before(){
        System.out.println("先买瓶水");
    }

    public void after(){
        System.out.println("再拿个快递");
    }
}

可以看到,我们在买饭前,买饭后,又调用了我们before方法和after方法,这样就可以直接实现我们的想法了。看测试结果
代理模式(静态代理、JDK代理、cglib代理)的简单用法_第2张图片

JDK代理:

下面我们用JDK代理来实现这个例子
我们知道的是:JDK只代理被代理类的所实现的接口所有的方法
先看代码

/**
 * @description: 代理对象,我们暂且称之为同学,同学帮我们买饭,用于代理我自己去买饭的操作
 * @author: Jh Lee
 * @create: 2019-03-12 21:48
 **/
public class JDKSchoolmate implements InvocationHandler {

    //这个属性就是我们被代理的对象,我们这个代理同学不止帮我自己买饭,还可以帮我们其他同学买饭,所以我们定义属性为Object,用于不同的同学对象
    private Object proxyObject;

    /**
     * 对我们代理对象的一个绑定,以及返回一个我们JDK自动通过反射生成的一个代理类($proxy0)
     * 在这个方法里面生成一个代理类。
     * 当我想找人买饭时,我就可以通过这个对象的这个方法返回一个代理类,让这个代理类帮我实现。
     * @param proxyObject
     * @return
     */
    public Object bind(Object proxyObject){
        //接口业务实现类传过来的参数(就是我们需要帮助哪一个同学买饭,进行我们的代理对象绑定)
        this.proxyObject = proxyObject;

        //通过我们被代理类的类加载器,代理类class,以及我们实现了InvocationHandler这个接口的本身参数,JDK通过反射机制,帮我们生成一个代理类并返回。
        Object o = Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(), proxyObject.getClass().getInterfaces(), this);
        return o;
    }

    /**
     * 这个方法才是我们调用被代理类所要代理的方法,当然我们,在这里使用这个方法之前,我们可以做一些其他事。
     * 例如:叫同学帮我买饭之前,先帮我买一瓶水,买完饭之后回来的路上,再帮我去拿个快递。
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        this.before();
        //这里是我们代理所真要执行的代理方法
        Object invoke = method.invoke(proxyObject, args);
        this.after();
        return invoke;
    }

    public void before(){
        System.out.println("先帮我买一瓶水!");
    }

    public void after(){
        System.out.println("回来再拿个快递!");
    }
}

从上可以看出,我们的代理中首先需要实现JDK中的InvocationHandler,需要重写接口中的invoke方法,我们还需要把我们需要代理的类作为构造参数对我们代理类进行复制绑定,我们通过代理类中的bind方法进行绑定,在bind方法中,我们除了给我们代理类的属性进行复制,而且还调用了
在这里插入图片描述
这个时候,我们看一下这个方法的源码
代理模式(静态代理、JDK代理、cglib代理)的简单用法_第3张图片
可以看到,这个方法的3个参数需要传一个类加载器,一个class类型的interface数组,还有一个就是我们实现了的InvocationHandler接口类型的参数。
我们知道,这个方法是返回一个由JDK通过反射自动生成的一个代理类Object,参数肯定需要知道我们需要生成一个具体哪一个类的代理类,这里我们JDKSchoolmate代理中的引用类型就是我们的代理类,所以我们直接把我们引用对象的信息传过去,最后一个InvocationHandler参数就直接传把我们实现了这个InvocationHandler的类传过去,它就会生成一个被生成的代理类。
再来看我们的invoke方法

/**
     * 这个方法才是我们调用被代理类所要代理的方法,当然我们,在这里使用这个方法之前,我们可以做一些其他事。
     * 例如:叫同学帮我买饭之前,先帮我买一瓶水,买完饭之后回来的路上,再帮我去拿个快递。
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        this.before();
        //这里是我们代理所真要执行的代理方法
        Object invoke = method.invoke(proxyObject, args);
        this.after();
        return invoke;
    }

参数proxy就是我们生成的代理类,method就是当我们具体使用的方法,args就是我们调用的方法的参数。
Object invoke = method.invoke(proxyObject, args);
这个就是我们代理所要执行的代理方法
我们也可以在里面进行业务加强(调用before和after方法)
看测试结果

/**
 * @description: JDK代理测试类
 * @author: Jh Lee
 * @create: 2019-03-12 22:16
 **/
public class JDKSchoolmateTest {

    public static void main(String[] args) {
        //我想买饭了,新建一个代理人帮我去买
        JDKSchoolmate jdkSchoolmate = new JDKSchoolmate();
        //代理人跟我绑定,相当于这个代理是我叫的,你现在只能帮我买
        Schoolmate schoolmate = (Schoolmate)jdkSchoolmate.bind(new Myself());
        //让代理帮我去买饭
        schoolmate.buyFood();

    }
}

代理模式(静态代理、JDK代理、cglib代理)的简单用法_第4张图片
可以看到,使用JDK代理实现了我们一样的效果!

cglib代理

cglib不需要想JDK那样只代理实现了接口的类,只代理实现了这个接口的所有的方法。cglib是可以代理任何一个普通的类的方法。原理是cglib会为我们需要代理的类生成一个继承我们需要代理类的一个子类。
我们使用cglib代理时,需要代理类实现MethodInterceptor接口,然后实现intercept方法,这个方法跟jdk的invoke方法一样,都是执行我们被代理类的具体执行方法。
看代码

/**
 * @description: cglib代理实现帮我买饭
 * @author: Jh Lee
 * @create: 2019-03-12 22:44
 **/
public class CglibSchoolmate implements MethodInterceptor {

    /**
     * 相当于jdk代理中的绑定
     * 通过构造器参数把我们需要代理的类传进来
     * 也可以通过声明一个代理属性,然后像JDK那样通过这个方法的参数进行赋值
     * @param clazz
     * @return
     */
    public Object getInstance(Class<?> clazz){

        //创建加强器,用来创建我们动态代理类
        Enhancer enhancer = new Enhancer();
        //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
        //因为我们cglib生成的代理是继承我们需要被代理的人,所以我们这里这是代理类的父类为我们被代理类
        enhancer.setSuperclass(clazz);
        //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
        enhancer.setCallback(this);
        return enhancer.create();
    }


    /**
     * 拦截方法,cglib通过这个方法帮我们执行我们所要代理的类的方法
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        //调用业务类(父类中)的方法
        Object invoke = methodProxy.invokeSuper(o, objects);
        after();
        return invoke;
    }

    private void before(){
        System.out.println("先买一瓶水");
    }

    private void after(){
        System.out.println("再拿个快递");
    }
}

代码中,getInstance方法跟我们JDK中bind方法一样,都是对代理类的一种绑定,这个cglib是通过Enhancer加强器来创建我们的动态代理类。在方法中可以看到我们加强器需要一个superClass和一个callback,这两个东西一个就是我们代理类对象,因为cglib生成的是我们被代理的子类,这个superClass相当于我们生成的代理中的父类,所以就是我们的被代理类,callback是一个回调,我们在调用代理类上的方法时,都是调用这个callback,而这个参数则需要实现我们intercept()进行拦截。
看测试代码

/**
 * @description: cglib动态代理测试类
 * @author: Jh Lee
 * @create: 2019-03-13 22:39
 **/
public class CglibSchoolmateTest {

    public static void main(String[] args) {
        //通过我们的代理类getInstance方法生成我们代理类对象,然后我们强转为我自己,实际这个Myself类为cglib为我们自动生成的
        Myself myself = (Myself)new CglibSchoolmate().getInstance(Myself.class);
        //通过调用buyFood方法实现我们代理类的中方法,
        myself.buyFood();
    }
}

代理模式(静态代理、JDK代理、cglib代理)的简单用法_第5张图片
实现了一样的效果

总结

静态代理有局限性,只能为指定的代理类进行代理,而JDK代理和cglib代理则为动态代理,可以从上面代码看到,我们所声明的代理类类型都是Object类型,我们可以动态的传入不同的代理对象,但是JDK代理对象必须要实现了某一个接口,而且只代理这个接口所有的方法,而cglib代理则可以为任务一个对象,任何一个方法进行代理。

你可能感兴趣的:(成长路程,学习笔记)