设计模式(十二)代理模式

版权声明:转载必须注明本文转自晓_晨的博客:http://blog.csdn.net/niunai112

目录

导航

设计模式之六大设计原则
设计模式(一)单例模式
设计模式(二)工厂模式
设计模式(三)策略模式
设计模式(四)适配器模式
设计模式(五)享元模式
设计模式(六)建造者模式
设计模式(七)原型模式
设计模式(八)桥接模式
设计模式(九)外观模式
设计模式(十)组合模式
设计模式(十一)装饰器模式
设计模式(十二)代理模式
设计模式(十三)迭代器模式
设计模式(十四)观察者模式
设计模式(十五)中介者模式
设计模式(十六)命令模式
设计模式(十七)状态模式
设计模式(十八)访问者模式
设计模式(十九)责任链模式
设计模式(二十)解释器模式
设计模式(二十一)备忘录模式
设计模式(二十二)模板模式
设计模式总结篇(为什么要学习设计模式,学习设计模式的好处)

前言

代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。这个模式在我们平时的日常编码中经常出现,aop就是运用了代理模式,可以在不改变方法的情况下,对方法进行增强。

例子

对于一个大歌星来说,他们的时间是非常非常宝贵的,所以他们大部分的事情都是由助理做的,他们本人并不需要知道助理做了什么事情,他们只要专注于他们自己的事情。

静态代理

LZ用静态代理来实现这个例子

/***
 *
 *@Author ChenjunWang
 *@Description:明星接口
 *@Date: Created in 22:59 2018/4/1
 *@Modified By:
 *
 */
public interface ISuperStar {

    public void sing();
    public void eat();
    public void sleep();

}


/***
 *
 *@Author ChenjunWang
 *@Description:明星实体类(实现明星接口,专注与自己要做的事情【不得不做的事情】)
 *@Date: Created in 23:00 2018/4/1
 *@Modified By:
 *
 */
public class SuperStar implements ISuperStar {
    @Override
    public void sing() {
        System.out.println("明星:月色正朦胧,与清风把酒相送");
    }

    @Override
    public void eat() {

        System.out.println("明星吃饭……");
    }

    @Override
    public void sleep() {
        System.out.println("明星碎觉……");

    }
}

/***
 *
 *@Author ChenjunWang
 *@Description:代理类(代理人同样要实现明星接口,重写方法时在明星方法执行前后进行方法加强)
 *@Date: Created in 23:02 2018/4/1
 *@Modified By:
 *
 */
public class ProxySuperStar implements ISuperStar{
    private ISuperStar superStar;
    public ProxySuperStar(){
        this.superStar = new SuperStar();
        System.out.println("实例化代理类");


    }

    @Override
    public void sing() {
        System.out.println("代理人为明星做好唱歌前的准备工作……");
        superStar.sing();
        System.out.println("代理人为明星做好唱完后的扫尾工作……");

    }

    @Override
    public void eat() {

        System.out.println("代理人做好饮食搭配");
        superStar.eat();
    }

    @Override
    public void sleep() {

        superStar.sleep();
    }


}



/***
 *
 *@Author ChenjunWang
 *@Description:测试类
 *@Date: Created in 23:12 2018/4/1
 *@Modified By:
 *
 */
public class Test {
    public static void main(String[] args) {

        ProxySuperStar proxySuperStar = new ProxySuperStar();
        proxySuperStar.sing();
        proxySuperStar.eat();
        proxySuperStar.sleep();
    }
}


运行结果如下
---------------------------------
实例化代理类
代理人为明星做好唱歌前的准备工作……
明星:月色正朦胧,与清风把酒相送
代理人为明星做好唱完后的扫尾工作……
代理人做好饮食搭配
明星吃饭……
明星碎觉……

静态代理和装饰器模式的区别

上面的代码就能做到对明星的干他的事之前,为他做好需要准备的事,以及之后的扫尾工作,是不是感觉和装饰器模式如出一辙,咋一看确实是这样的,但是代理模式控制了对实际代理对象的访问,就是想要访问实际对象,那就必须通过代理类来,不能直接访问对象。而装饰器模式只注重功能新增。

动态代理

动态代理所实现的功能与静态代理一样,不一样的地方在于静态代理在代码编译的时候关系就已经确定了,.class文件已经生成完毕,而动态代理却是在程序运行的期间动态生成.class字节码文件。
接下来LZ用jdk的动态代理实现上面的例子

/***
 *
 *@Author ChenjunWang
 *@Description:明星接口
 *@Date: Created in 22:59 2018/4/1
 *@Modified By:
 *
 */
public interface ISuperStar {

    public void sing();
    public void eat();
    public void sleep();

}

/***
 *
 *@Author ChenjunWang
 *@Description:明星实体类
 *@Date: Created in 23:00 2018/4/1
 *@Modified By:
 *
 */
public class SuperStar implements ISuperStar {
    @Override
    public void sing() {
        System.out.println("明星:月色正朦胧,与清风把酒相送");
    }

    @Override
    public void eat() {

        System.out.println("明星吃饭……");
    }

    @Override
    public void sleep() {
        System.out.println("明星碎觉……");

    }
}

/***
 *
 *@Author ChenjunWang
 *@Description:代理类实现InvocationHandler接口
 *@Date: Created in 20:17 2018/4/2
 *@Modified By:
 *
 */
public class SuperStarProxy implements InvocationHandler {
    private Object target;//需要代理的对象放这

    public SuperStarProxy(Object target) {
        super();
        this.target = target;//构造函数将需要代理的对象传入
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //下面是对具体的代理逻辑
        if (method.getName().equals("eat")){

            System.out.println("代理人做好饮食搭配");
            method.invoke(target, args);

        } else {
            System.out.println("方法执行前");
            method.invoke(target, args);
            System.out.println("方法执行后");

        }

        return null;
    }
}

/***
 *
 *@Author ChenjunWang
 *@Description:测试类
 *@Date: Created in 20:23 2018/4/2
 *@Modified By:
 *
 */
public class Test {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        ISuperStar superStar = new SuperStar();
        InvocationHandler in = new SuperStarProxy(superStar);
        Class<?> aClass = superStar.getClass();
        //通过Proxy.newProxyInstance方法动态生成字节码并装载实例化出对象
        ISuperStar proxy = (ISuperStar)Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), in);
        proxy.eat();
        proxy.sing();
        proxy.sleep();
    }
}

运行结果如下
---------------------------
代理人做好饮食搭配
明星吃饭……
方法执行前
明星:月色正朦胧,与清风把酒相送
方法执行后
方法执行前
明星碎觉……
方法执行后

上面便是jdk动态代理的实现了,这个实现方式有缺点,那便是一定要基于接口来代理,也就是说,jdk动态代理只能增强接口方法,这点在源码中体现了,假如有朋友有兴趣的话,可以留言,我会专门写一篇文章来分析。
生成的字节码是一个final 修饰继承了Proxy并实现了要增加接口的类,其中会重写equals,hashCode,toString三个方法,然后实现接口的方法,方法的内容是去调用InvocationHandler中的invoke,这样来实现动态代理。

cglib代理

以上来说,大部分的对象都能进行方法增强了,但是,有很多类不基于接口来实现,那么这个时候,就能用cglib来实现动态代理的。
CGLIB的动态代理,其实就是利用了CGLIB,在运行时期生成被代理对象的子类,来作为代理对象。同时重写了被代理对象的所有方法。当我们用代理对象调用方法的时候,其实是调用的重写后的方法。该方法实际调用的是callback中的用户自定义的代码逻辑,例如权限验证。当验证通过了,则通过反射,反射出父类(被代理对象)的方法并调用(invoke)。


/***
 *
 *@Author ChenjunWang
 *@Description:明星实体类 (不实现接口)
 *@Date: Created in 23:00 2018/4/1
 *@Modified By:
 *
 */
public class SuperStar {
    public void sing() {
        System.out.println("明星:月色正朦胧,与清风把酒相送");
    }

    public void eat() {

        System.out.println("明星吃饭……");
    }

    public void sleep() {
        System.out.println("明星碎觉……");

    }
}


/***
 *
 *@Author ChenjunWang
 *@Description:
 *@Date: Created in 23:25 2018/4/2
 *@Modified By:
 *
 */
public class StarCglibProxy implements MethodInterceptor {

    private Object superStar;
    public Object getInstance(final Object target) {
        this.superStar = target;
        //工具类,允许为非接口类型创建一个Java代理。不管是接口还是类他都能正常工作。
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(this.superStar.getClass());
        //设置回掉函数
        enhancer.setCallback(this);
        return enhancer.create();
    }
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if (method.getName().equals("eat")){

            System.out.println("Cglib,代理人做好饮食搭配");
            method.invoke(superStar, objects);

        } else {
            System.out.println("Cglib,方法执行前");
            method.invoke(superStar, objects);
            System.out.println("Cglib,方法执行后");

        }
        return null;
    }
}

/***
 *
 *@Author ChenjunWang
 *@Description:
 *@Date: Created in 20:23 2018/4/2
 *@Modified By:
 *
 */
public class Test {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        SuperStar superStar = new SuperStar();
        StarCglibProxy starCglibProxy = new StarCglibProxy();
        SuperStar proxy = (SuperStar)starCglibProxy.getInstance(superStar);

        proxy.eat();
        proxy.sing();
        proxy.sleep();
    }
}
运行结果如下
--------------------------------------
Cglib,代理人做好饮食搭配
明星吃饭……
Cglib,方法执行前
明星:月色正朦胧,与清风把酒相送
Cglib,方法执行后
Cglib,方法执行前
明星碎觉……
Cglib,方法执行后

总结

优点

(1)不改变源代码就能对对象的方法进行增强,符合开闭原则。
(2)使得代码的拓展性大大提升。

缺点

(1)会让代码的复杂度提升。
(2)一点局限,jdk动态代理生成的代理类必须实现了提供的接口,cglib动态代理的类不能是final修饰的。

Git地址

本篇实例Github地址:https://github.com/stackisok/Design-Pattern/tree/master/src/proxy

回到最上方


有什么不懂或者不对的地方,欢迎留言。
喜欢LZ文章的小伙伴们,可以关注一波,也可以留言,LZ会回你们的。
觉得写得不错的小伙伴,欢迎转载,但请附上原文地址,谢谢^_^!

猜你喜欢

转载自blog.csdn.net/niunai112/article/details/79810490