• Spring 静态代理和动态代理


    动态代理:当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。

    动态代理实现:首先必须定义一个接口,还要有一个InvocationHandler(将实现接口的类的对象传递给它)处理类。再有一个工具类Proxy(习惯性将其称为代理类,因为调用他的newInstance()可以产生代理对象,其实他只是一个产生代理对象的工具类)。利用到InvocationHandler,拼接代理类源码,将其编译生成代理类的二进制码,利用加载器加载,并将其实例化产生代理对象,最后返回。

    静态代理:

    优点:

      1、  实现松散耦合。

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

    缺点:

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

    模拟某位学生去考试作为例子:

    下面创建 考试接口

    public interface Examable {
        void exam();
    }

    创建Student学生实现这个考试接口(被代理)

    public class Student implements Examable {
      
        @Override
        public void exam() {
            System.out.println("奋笔疾书,完成考题。。。");
        }
    
    }

    创键Cheater(代理)对象,同时也去实现Examable接口 

    public class Cheater implements Examable {
    
      //被代理对象
    private final Examable student; public Cheater(Examable student){ this.student=student; } @Override public void exam() { System.out.println("在现场唱了一首歌,差点被劝退"); student.exam();//调用Student类的方法 } }

    测试:

    public class Main {
    //组合优于继承
    public static void main(String[] args) {
    //cheater 就是一个代理对象
    //因为它是受student 委托,完成某个功能
    //它要完成的主要功能,来自student
    //除了委托做的事情,可能还会扩展一些行为


    Examable xiaoming = new Student();//原来的行为
    xiaoming.exam();

    System.out.println("------下面是代理行为------");

    Examable cheater = new Cheater(xiaoming);
    cheater.exam();

    }
    }

    结果:

    奋笔疾书,完成考题。。。
    ------下面是代理行为------
    在现场唱了一首歌,差点被劝退
    奋笔疾书,完成考题。。。

    动态代理:

    使用JDK内置的Proxy实现

    接着拿上面作为例子

    下面创建 考试接口

    public interface Examable {
        void exam();
    }

    创建Student学生实现这个考试接口(被代理)

    public class Student implements Examable {
      
        @Override
        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 执行方法时需要的参数
         */
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) {
            Object invoke = null;
            try {
                invoke = method.invoke(object, args);
            } catch (Exception e) {
                System.out.println("异常的信息" + e.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();
    
    
            Examable o = (Examable) Proxy.newProxyInstance(
                    cl,//类加载器
                    new Class[]{Examable.class},//获取被代理对象的所有接口
                    new JdkProxy(new Student())//InvocationHandler对象
            );
            o.exam();//代理后的行为
    
        }

    }

    结果:

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

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

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

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

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

    Student类

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

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

    package com.nf147.sim.proxy.p5;
    
    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 从代理实例的方法调用返回的值
         * */
    
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            return methodProxy.invokeSuper(o, objects);
        }
    }

    测试:

    package com.nf147.sim.proxy.p5;
    
    
    import com.nf147.sim.proxy.p1.Student;
    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();
    
            System.out.println("-----------");
    
            //第二种方式
            //这里是简化写法
            //第一个参数 设置 生成类 的父类 ,第二参数 被代理类的所有接口 ,回调函数
            Student student = (Student) Enhancer.create(Student.class, null, new CglibProxy());
            student.exam();
    
        }
    }
    

     结果:

  • 相关阅读:
    Postman初探
    web页面和本地数据对比问题
    Katalon Recorder初探
    Flask入门
    自我实现预言
    gulp 安装 依赖
    maven环境
    加解密 生成 X.509格式,DER编码,后缀名.cer。加密公钥证书
    我的魔法 公式找回中
    gulp 自动ftp至服务器时,处理开发 测试服务器地址问题
  • 原文地址:https://www.cnblogs.com/nongzihong/p/10139247.html
Copyright © 2020-2023  润新知