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

    代理模式

    什么是代理模式?(简单举例几个例子)

    1、打王者段位一直打不上王者段位怎么办?请游戏代练。

    2、过年回家自已抢不到高铁票怎么办?找黄牛帮我们抢票。

    3、.......(生活中处处可见。)

    由上可知,代理模式 就是:

    给某一个对象提供一个代理对象,并由代理对象控制对 原对象的引用。

    通俗滴讲就是我们常说的:中介。

    有哪几种代理模式(它们的区别与优缺点)?

    一般分为两种:

    1、静态代理 (代理类与被代理类的关系在程序运行前就已经确定好了。)

    2、动态代理 (代理类与被代理类的关系在程序运行时才确定。)

    1. jdk动态代理
    2. cglib动态代理

    什么是静态代理?

    在程序运行前就已经存在的代理类的字节码文件。

    代理类和委托类的关系在运行前就确定了。

    什么是动态代理?

    在程序运行时,通过反射机制动态创建的。

    静态代理 与 动态代理 优缺点

    优点

    实现在不改变目标对象源码情况下,对目标对象进行功能扩展。

    静态代理缺点

    假设项目中有多个类,则需要编写多个代理类,工作量大,不好修改,不好维护,不能应对变化。

    JDK动态代理缺点

    被代理的类必须实现接口,未实现接口则没办法完成动态代理。

    CGLIB动态代理缺点

    被代理类必须不是 final 类。

    代码实现代理

    代码背景:某人(王五)吃苹果。

    简单的代理(耦合性太强,采用继承的方式)不推荐使用

    没有代理的情况下:

    /**
     * 王五
     * @author oukele
     */
    public class WangWu {
    
        /**
         * 吃苹果
         */
        public void eatApple(){
            System.out.println("王五吃苹果!");
        }
    
    }
    

    运行结果:

        public static void main(String[] args) {
    
            WangWu wangWu = new WangWu();
            wangWu.eatApple();
    
        }
    

    王五吃苹果!

    有代理的情况下:

    (张三将削好的苹果递给王五,然后王五吃苹果!)

    /**
     * 张三
     * @author oukele
     */
    public class ZhangSan extends WangWu {
    
        @Override
        public void eatApple() {
    
            System.out.println("张三从众多苹果中挑了一个苹果。");
            System.out.println("1. 然后拿去洗了洗...。");
            System.out.println("2. 将苹果鲜红的外衣悄悄的脱掉...。");
            System.out.println("N. N步操作在这里省略...。");
            System.out.println("将处理好的苹果递给王五。");
            // 王五吃苹果
            super.eatApple();
        }
    }
    

    运行结果:

        public static void main(String[] args) {
    
            ZhangSan zhangSan = new ZhangSan();
            zhangSan.eatApple();
    
        }
    

    张三从众多苹果中挑了一个苹果。

    1. 然后拿去洗了洗...。
    2. 将苹果鲜红的外衣悄悄的脱掉...。

    ​ N. N步操作在这里省略...。
    ​ 将处理好的苹果递给王五。
    ​ 王五吃苹果!

    上面的这些例子就是一个简单的代理行为。这个简单代理,耦合性太强了。作为演示就好了。


    1、静态代理

    代码背景:还是上面的例子,某人(王五)吃苹果。

    第一步:创建 服务接口

    /**
     * 吃的服务行为接口
     * @author oukele
     */
    public interface Eat {
        
        /**
         * 吃的行为
         */
        void eat();
    }
    

    第二步:实现服务接口

    /**
     * 王五(被代理类)
     * @author oukele
     */
    public class WangWu implements Eat {
        
        @Override
        public void eat() {
            System.out.println("王五吃苹果!");
        }
    }
    

    第三步:创建代理类(让张三来帮忙削皮)

    /**
     * 张三(代理类)
     *
     * @author oukele
     */
    public class ZhangSan implements Eat {
    
        /**
         * 被代理类(王五)
         */
        private Eat eat = null;
    
        public ZhangSan(Eat eat){
            this.eat = eat;
        }
    
        @Override
        public void eat() {
            System.out.println("0.-----张三的操作-----");
            System.out.println("1.张三帮王五削好了苹果。");
            // 王五吃苹果
            eat.eat();
            System.out.println("3.王五吃完了,张三顺便帮忙收拾好。");
        }
    }
    

    第四步:进行测试

        public static void main(String[] args) {
    
            System.out.println("-------------------没有代理----------------------");
            // 被代理类
            WangWu wangWu = new WangWu();
            wangWu.eat();
            System.out.println("-------------------没有代理----------------------");
            System.out.println();
            System.out.println("-------------------有代理----------------------");
            // 代理类
            ZhangSan zhangSan = new ZhangSan(wangWu);
            zhangSan.eat();
            System.out.println("-------------------有代理----------------------");
        }
    

    运行结果:

    -------------------没有代理----------------------
    王五吃苹果!
    -------------------没有代理----------------------

    -------------------有代理----------------------
    0.-----张三的操作-----
    1.张三帮王五削好了苹果。
    王五吃苹果!
    3.王五吃完了,张三顺便帮忙收拾好。
    -------------------有代理----------------------


    2、JDK动态代理

    代码背景:还是上面的例子,某人(王五)吃苹果。

    第一步:创建 服务接口

    /**
     * 吃的服务行为接口
     * @author oukele
     */
    public interface Eat {
        
        /**
         * 吃的行为
         */
        void eat();
    }
    

    第二步:实现服务接口

    /**
     * 王五(被代理类)
     * @author oukele
     */
    public class WangWu implements Eat {
        
        @Override
        public void eat() {
            System.out.println("王五吃苹果!");
        }
    }
    

    第三步:创建代理类并实现 InvocationHandler 接口,这次就不叫 张三来帮忙了

    /**
     * 使用JDK内置的Proxy实现动态代理(代理类)
     * @author oukele
     */
    public class JdkProxy implements InvocationHandler {
    
        /**
         * 被代理类
         */
        private Object object = null;
    
        private JdkProxy(){};
    
        public JdkProxy(Object object){
            this.object = object;
        }
    
        /**
         * 用户调用代理对象的什么方法,都是在调用处理器的invoke方法。【被拦截】
         * @param proxy 被代理后的对象
         * @param method 将要被执行的方法
         * @param args 调用方法时需要的参数
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("某某人....帮王五削好苹果。");
            Object invoke = null;
            try {
                invoke = method.invoke(object, args);
            }catch (Exception e){
                e.printStackTrace();
                throw new RuntimeException("出现异常:" +  e.getMessage());
            }
            System.out.println("某某人....帮忙收拾好现场。");
            return invoke;
        }
    
    }
    

    第四步:进行测试

        public static void main(String[] args) {
    
            Eat eat = (Eat) Proxy.newProxyInstance(
                    WangWu.class.getClassLoader(),
                    WangWu.class.getInterfaces(),
                    new JdkProxy(new WangWu())
                    );
            eat.eat();
    
        }
    
    注意 Proxy.newProxyInstance() 方法接受三个参数
        1. ClassLoader loader: 指定当前目标对象使用的类加载器,获取加载器的方法是固定的。A
        2. Class<?>[] interfaces: 指定目标对象实现的接口类型。 B
        3. InvocationHandler: 指定动态处理器,执行目标对象的方法时,触发事件处理器的方法。C
    大白话:
        A: 生成代理对象使用哪个类装载器【一般我们使用的是被代理类的装载器】
        B: 生成哪个对象的代理对象,通过接口指定【指定要被代理类的接口】
        C: 生成的代理对象的方法里干什么事【实现InvocationHandler接口,我们想怎么增强原有对象的功能就随意怎么增强。】
    

    运行结果:

    某某人....帮王五削好苹果。
    王五吃苹果!
    某某人....帮忙收拾好现场。


    3、CGLIB动态代理

    其原理:通过字节码技术为一个类创建子类。

    代码背景:还是上面的例子,某人(王五)吃苹果。

    注意:需要 cglib 的依赖;

    此处使用的版本为

            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>3.3.0</version>
            </dependency>
    

    1、第一步:创建一个 王五 类

    /**
     * 王五
     * @author oukele
     */
    public class WangWu {
    
        /**
         * 吃苹果
         */
        public void eatApple(){
            System.out.println("王五吃苹果!");
        }
    
    }
    

    2、第二步:创建 CGLIB 代理类 并实现 MethodInterceptor 接口

    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
    
    /**
     * Cglib 动态代理类
     * @author oukele
     */
    public class CglibProxy  implements MethodInterceptor {
    
        /**
         *
         * @param o 由CGLib动态生成的代理类实例
         * @param method 上文中实体类所调用的被代理的方法引用
         * @param objects 参数值列表
         * @param methodProxy 生成的代理类对方法的代理引用
         * @return 代理实例的方法调用返回的值
         * @throws Throwable
         */
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("某某人....帮王五削好苹果。");
            Object invoke = null;
            try {
                invoke = method.invoke(o, objects);
            }catch (Exception e){
                e.printStackTrace();
                throw new RuntimeException("出现异常:" + e.getMessage());
            }
            System.out.println("某某人....帮忙收拾好现场。");
            return invoke;
        }
    }
    

    3、第三步:进行测试

        public static void main(String[] args) {
    
            // 增强器,动态代码生成器
            Enhancer enhancer = new Enhancer();
            // 设置 被代理类
            enhancer.setSuperclass(WangWu.class);
            // 放置 代理类
            enhancer.setCallback(new CglibProxy());
            // 动态生成字节码并返回代理对象
            WangWu wangWu = (WangWu) enhancer.create();
            wangWu.eat();
            
            // 简写
            WangWu wangWu1 = (WangWu)Enhancer.create(
                    WangWu.class,
                    new CglibProxy()
            );
            wangWu1.eat(); 
            
        }
    

    运行结果:

    某某人....帮王五削好苹果。
    王五吃苹果!
    某某人....帮忙收拾好现场。

    小结:

    JDK 动态代理 和 CGLIB 字节码生成的区别?

    JDK动态代理

    只能对实现了接口的类生成代理

    CGLIB动态代理

    针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,
    并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final.

    JDK 代理 与 CGLIB 代理 中如何选择?

    目标对象是否实现了接口

    采用JDK的动态代理

    采用CGLIB动态代理

  • 相关阅读:
    asp.net mvc (三)
    asp.net mvc(二)
    实现属于自己的Custom Formatter
    属性(Properties)和字段在C#中的关系
    LINQ 和泛型
    asp.net mvc(一)
    asp.net mvc(四)
    C#中的string
    asp.net mvc(五)
    Nullable Value Type
  • 原文地址:https://www.cnblogs.com/oukele/p/13467397.html
Copyright © 2011-2022 走看看