zoukankan      html  css  js  c++  java
  • Spring 静态代理和动态代理

    现在我们来模拟一下,某位学生去考试。

      假设他(小明)正常的考试。

      运行结果:

           结果:

    突然某一天,他睡过头了,来不急去考试,所有他打算叫另一个人(Cheater)去代替他考试。

      运行结果:

     结果:

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

    静态代理:

    优点:

      1、  实现松散耦合。

      2、做到在不修改目标对象的功能前提下,对目标功能扩展。

    还是拿上面的例子进行修改吧,小明还是一样起晚了,叫Cheater去代替他考试。

    定义一个Exam接口 ,代表着考试的行为。

    public interface Exam {//考试的接口
        void exam();
    }

    去Student类中实现此接口,并实现方法。 (被代理)

    public class Student implements Exam {
        
        public void exam(){
            System.out.println("奋笔疾书,完成考试啦");
        }
    }

    Cheater也实现Exam接口,并实现方法 (代理)

    public class Cheater implements Exam {
      //被代理的对象
        private final Exam student;
    
        public Cheater(Exam student){
            this.student = student;
        }
    
        public void exam() {
            System.out.println("考试的时候唱了一首凉凉,差点被劝退了。");
            student.exam();//调用Student类的方法
        }
    }

    测试:

    public class Main {
    
        public static void main(String[] args) {
    
            Exam xiaoMing = new Student();
            xiaoMing.exam();//原来的行为
    System.out.println("-----------下面是代理的行为------------");
    Exam cheater
    = new Cheater(xiaoMing); cheater.exam();//代理的行为 } }

    结果:

    1 奋笔疾书,完成考试啦
    2 -----------下面是代理的行为------------
    3 考试的时候唱了一首凉凉,差点被劝退了。 4 奋笔疾书,完成考试啦

    静态代理的缺点:

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

    如果想解决上面的问题,可以使用动态代理。

    动态代理:

    1、使用JDK内置的Proxy实现

    还是拿上面的例子进行修改吧,小明还是一样起晚了,叫人去代替他考试。(重新开始哈)

    定义一个Exam接口。

    public interface Exam {
        void exam();
    }

    Student 实现 Exam接口 

    public class Student implements Exam {
        public void exam() {
            System.out.println("奋笔疾书,完成考试啦");
        }
    }

    创建一个 JdkProxy 类 实现 InvocationHandler 接口

    public class JdkProxy implements InvocationHandler {
    
        private Object object;//被代理的对象
    
        public JdkProxy(){}
    
        public JdkProxy(Object object){//初始化的时候就赋值
            this.object = object;
        }/**
         * 当用户调用对象中的每个方法时都通过下面的方法执行,方法必须在接口
         * proxy 被代理后的对象
         * method 将要被执行的方法信息(反射)
         * args 执行方法时需要的参数
         */
        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 x){
                System.out.println("异常信息:"+x.getMessage());
            }return invoke;//调用被代理对象原来的方法(行为)
        }
    }

    测试:

    public class Main {
        public static void main(String[] args) {
    
            /*
             * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
             * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
             * 第二个参数person1.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
             * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
             */
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            Exam o = (Exam) Proxy.newProxyInstance(
                    cl,//类加载器
                    new Class[]{Exam.class},//获取被代理对象的所有接口
                    new JdkProxy(new Student())//InvocationHandler对象
            );
            o.exam();//代理后的行为
    
            // 另一种写法
    //        Exam o1 = (Exam) Proxy.newProxyInstance(
    //                Thread.currentThread().getContextClassLoader(),
    //                Student.class.getInterfaces(),
    //                new JdkProxy(new Student())
    //        );
    //        o1.exam();
    
        }
    }

    运行结果:

    1 考试的时候,吃颗糖压压惊,没有人知道,这次不是本人来考试的
    2 奋笔疾书,完成考试啦

    使用内置的Proxy实现动态代理有一个问题被代理的类必须实现接口,未实现接口则没办法完成动态代理。

    2、动态代理,使用cglib实现

    还是拿上面的例子来说吧。

    这次我们不写接口了。不过 我们要实现MethodInterceptor接口,并实现方法

    去maven 中心仓库 找到 CGlib 的依赖

    Student类

    public class Student {
    
        public void exam(){
            System.out.println("奋笔疾书,完成考试啦");
        }
    
    }

    创建一个 CglibProxy 的类 并 实现  MethodInterceptor 接口 ,并实现方法

    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class CglibProxy implements MethodInterceptor {
    
        /*
        * 参数
        * Object 为由CGLib动态生成的代理类实例
        * method 为上文中实体类所调用的被代理的方法引用
        * objects 为参数值列表
        * methodProxy 为生成的代理类对方法的代理引用
        * return 从代理实例的方法调用返回的值
        * */
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("考试的时候,唱了一首 我爱洗澡 ,差点被劝退 ");
            return methodProxy.invokeSuper(o,objects);
        }
    
    }

    测试:

    import net.sf.cglib.proxy.Enhancer;
    
    public class Main {
        public static void main(String[] args) {
    
            //增强器,动态代码生成器
            Enhancer enhancer = new Enhancer();
            //设置 生成类 的 父类
            enhancer.setSuperclass(Student.class);
            //回调函数
            enhancer.setCallback(new CglibProxy());
            //动态生成字节码并返回代理对象
            Student o = (Student) enhancer.create();
            o.exam();
            
            //这里是简化写法
            //第一个参数 设置 生成类 的父类 ,第二参数 被代理类的所有接口 ,回调函数
            Student student = (Student) new Enhancer().create(Student.class, null, new CglibProxy());
            student.exam();
        }
    }

    运行结果:

    1 考试的时候,唱了一首 我爱洗澡 ,差点被劝退 
    2 奋笔疾书,完成考试啦 

    这种代理的缺点:被代理的类必须不是final类。

    小结:

    使用cglib可以实现动态代理,即使被代理的类没有实现接口,但被代理的类必须不是final类。

    示例代码下载地址: https://github.com/oukele/Spring-Proxy

  • 相关阅读:
    稳扎稳打Silverlight(47) 4.0UI之操作剪切板, 隐式样式, CompositeTransform, 拖放外部文件到程序中
    返璞归真 asp.net mvc (9) asp.net mvc 3.0 新特性之 View(Razor)
    返璞归真 asp.net mvc (6) asp.net mvc 2.0 新特性
    稳扎稳打Silverlight(48) 4.0其它之打印, 动态绑定, 增强的导航系统, 杂七杂八
    精进不休 .NET 4.0 (9) ADO.NET Entity Framework 4.1 之 Code First
    稳扎稳打Silverlight(42) 4.0控件之Viewbox, RichTextBox
    稳扎稳打Silverlight(53) 4.0通信之对WCF NetTcpBinding的支持, 在Socket通信中通过HTTP检索策略文件, HTTP请求中的ClientHttp和BrowserHttp
    稳扎稳打 Silverlight 4.0 系列文章索引
    稳扎稳打Silverlight(54) 4.0通信之对UDP协议的支持: 通过 UdpAnySourceMulticastClient 实现 ASM(Any Source Multicast),即“任意源多播”
    返璞归真 asp.net mvc (8) asp.net mvc 3.0 新特性之 Model
  • 原文地址:https://www.cnblogs.com/oukele/p/10132015.html
Copyright © 2011-2022 走看看