zoukankan      html  css  js  c++  java
  • 五、代理模式

    代理模式

    定义

    为其他对象提供一种代理以控制对这个对象的访问。

    为什么要用代理模式?

    • 中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
    • 开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

    有哪几种代理模式?

    按照代理创建的时期来进行分类的话, 可以分为两种:静态代理、动态代理。静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。动态代理是在程序运行时通过反射机制动态创建的。

    静态代理

    静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。

    实现

    这里假设Tom要买一套房子,然后自己没有足够的资金,于是就让他的父亲代理他来给他买下这套房子。

    买房子接口:

    public interface BuyHouse {
        void Buy();
    }
    

    Tom类:

    public class Tom implements BuyHouse {
        public void Buy() {
            System.out.println("Tom买到房子了...");
        }
    }
    

    Father类:

    public class Father implements BuyHouse {
        private Tom tom;
    
        public Father(Tom tom){
            this.tom = tom;
        }
        
        public void Buy() {
            System.out.println("Father给Tom买了房子...");
            tom.Buy();
        }
    }
    

    静态代理的优缺点:

    • 优点:在不修改目标对象功能的前提下,能通过代理对象对目标功能进行扩展
    • 缺点:因为代理对象要与目标对象实现一样的接口,所以会有很多代理类;一旦接口方法增加,目标对象与代理对象都需要维护。

    动态代理

    • 代理对象不需要实现接口,但是目标对象仍然需要实现接口,否则不能使用动态代理
    • 代理对象的生成,是利用JDK的API,动态的内从中构建代理对象

    下面我们使用JDK动态代理的方式来对上面的静态代理示例进行改写

    买房子接口和Tom类都和上面的一样

    创建代理类JdkProxy:

    public class JdkProxy implements InvocationHandler {
        private Object target;
        //接收被代理的目标对象对象
        public JdkProxy(Object target){
            this.target = target;
        }
    	//生成目标对象的代理对象
        public Object getProxyInstance(){
            Object instance = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
            return instance;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("开始Jdk动态代理,来给Tom买房子");
            //执行目标对象的方法
            Object returnVal = method.invoke(target,args);
            return returnVal;
        }
    }
    

    测试类:

    public class testJdkProxy {
        @Test
        public void  test(){
            BuyHouse tom = new Tom();
            JdkProxy jdkProxy = new JdkProxy(tom);
            BuyHouse proxyInstance = (BuyHouse)jdkProxy.getProxyInstance();
            proxyInstance.Buy();
        }
    }
    
    /**测试结果
    开始Jdk动态代理,来给Tom买房子
    Tom买到房子了...
    */
    

    注意Proxy.newProxyInstance()方法接受三个参数:

    • ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
    • Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
    • InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法

    Cglib代理

    静态代理和JDK代理模式都要求目标对象实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何对象,这个时候可以使用目标对象的子类来实现代理,这就是Cglib代理。

    CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

    实现

    创建没有实现接口的Tom类:

    public class Tom {
        public void Buy() {
            System.out.println("Tom买到房子了...");
        }
    }
    

    创建Cglib代理类:

    public class CglibProxy implements MethodInterceptor {
        private Object target;
    
        public Object getProxyInstance(Object target){
            this.target = target;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(this.target.getClass());
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("开始Cglib代理,来给Tom买房子");
            Object returnVal = method.invoke(target, objects);
            return returnVal;
        }
    }
    

    创建测试类:

    public class testCglibProxy {
        @Test
        public void test(){
            Tom proxyInstance = (Tom) new CglibProxy().getProxyInstance(new Tom());
            proxyInstance.Buy();
        }
    }
    
    /**测试结果
    开始Cglib代理,来给Tom买房子
    Tom买到房子了...
    */
    

    CGLIB代理总结: CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

  • 相关阅读:
    「codeforces
    「sdoi2019
    「ABC 218」解集
    「hdu
    「atcoder
    「tricks」平凡二分幻术
    并查集
    Bellman-Ford算法 & SPFA & SPFA_DFS
    最近公共祖先(LCA)
    题解 P5751 【[NOI1999]01串】
  • 原文地址:https://www.cnblogs.com/lee0527/p/11884981.html
Copyright © 2011-2022 走看看