• 动态代理两种方式


    1.动态代理两种方式简述

    JDK动态代理:利用反射机制生成一个实现代理接口匿名类,在调用具体方法前调用InvokeHandler来处理。

    CGLib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。

    2.动态代理的特点

    1.字节码随用随创建,随用随加载。
    2.它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。
    3.装饰者模式就是静态代理的一种体现。

    3.动态代理常用的有两种方式

    3.1 基于接口的动态代理

    提供者:JDK 官方的 Proxy 类。
    要求:被代理类最少实现一个接口。

    3.2 基于子类的动态代理

    提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
    要求:被代理类不能用 final 修饰的类(最终类)。

    4.使用JDK官方的Porxy类创建对象

    实体类:

    package com.jh.spring13jdk动态代理;
    
    import lombok.Data;
    
    @Data
    public class Game implements Open{
        //游戏的网速
        private int ms = 460;
    
        @Override
        public int openApp() {
            System.out.println("打开游戏后的网速是:" + this.getMs());
            return this.getMs();
        }
    }

    接口:

    package com.jh.spring13jdk动态代理;
    
    public interface Open {
        int openApp();
    }

    测试类:

    package com.jh.spring13jdk动态代理;
    
    import org.junit.Test;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    
    public class Spring13Test {
        //JDK动态代理 省略了代理对象!!! 直接有jdk中的代理类来实现,但是目标类必须有接口
        @Test
        public void test01() {
            final Game game = new Game();
            /**
             * 代理:
             * 间接。
             * 获取代理对象:
             * 要求:
             * 被代理类最少实现一个接口
             * 创建的方式
             * Proxy.newProxyInstance(三个参数)
             * 参数含义:
             * ClassLoader:和被代理对象使用相同的类加载器。
             * Interfaces:和被代理对象具有相同的行为。实现相同的接口。
             * InvocationHandler:如何代理。
             * 策略模式:使用场景是:
             * 数据有了,目的明确。
             * 如何达成目标,就是策略。
             *
             */
            //使用JDK动态类对象,当作迅游加速器的类,代替了静态的代理类
            //Proxy:代理的意思。 newProxyInstancece创建代理对象
            Open jdkProxy = (Open) Proxy.newProxyInstance(
                    game.getClass().getClassLoader(), //类的加载器
                    game.getClass().getInterfaces(), //类的所有接口
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Integer ms = (Integer) method.invoke(game, args);
                            if (ms != null) {
                                ms = ms - 400;
                            }
                            return ms;
                        }
                    }
            );
            int i = jdkProxy.openApp();
            System.out.println("i=" + i);
        }
    }

    5.使用CGLib的Enhancer类创建代理对象

    对于没有接口的类,如何实现动态代理呢,这就需要CGLib了,CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承。所以不能对final修饰的类进行代理。

    实体类:

    package com.jh.spring14cjlib动态代理;
    
    import lombok.Data;
    
    /**
     * 目标类
     * 父母
     */
    @Data
    public class Parents {
        //成绩
        private int score = 599;
    
        //高考
        public int gaoKao(){
            System.out.println("父母参加高考,分数是:"+this.getScore());
            return this.getScore();
        }
    }

    工厂类:

    package com.jh.spring14cjlib动态代理;
    
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    //MethodInterceptor:方法上的拦截
    public class ParentsFactory implements MethodInterceptor {
        private Parents parents;
    
        public ParentsFactory() {
            parents = new Parents();
        }
    
    
        /**
         * 基于子类的动态代理
         * 要求:
         * 被代理对象不能是最终类
         * 用到的类:
         * Enhancer
         * 用到的方法:
         * create(Class, Callback)
         * 方法的参数:
         * Class:被代理对象的字节码
         * Callback:如何代理
         */
        //增强器 ,把parents创造一个子类
        public Parents createParentsSon() {
            //使用字节码增强器,去增强我们的父类
            Enhancer enhancer = new Enhancer(); //字节码增强器,可以读懂class文件
            //enhancer 指定一个对象
            enhancer.setSuperclass(Parents.class);//反射
            //使用工厂,换行(创建子类)
            enhancer.setCallback(this);
            //创建子类
            Parents son = (Parents) enhancer.create(); //多态
            return son;
        }
    
        /**
         * 执行被代理对象的任何方法,都会经过该方法。在此方法内部就可以对被代理对象的任何
         方法进行增强。
         *
         * 参数:
         * 前三个和基于接口的动态代理是一样的。
         * MethodProxy:当前执行方法的代理对象。
         * 返回值:
         * 当前执行方法的返回值
         */
        //方法的拦截
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            Integer score = (Integer) method.invoke(parents, objects);
            if (score != null) {
                score = score + 30;
            }
            return score;
        }
    }

    测试类:

    package com.jh.spring14cjlib动态代理;
    
    import org.junit.Test;
    
    public class Spring14Test {
        @Test
        public void test01(){
            ParentsFactory parentsFactory = new ParentsFactory();
            Parents parentsSon = parentsFactory.createParentsSon();
            int score = parentsSon.getScore();
            System.out.println(score);
        }
    }

    6.总结:

    CGLib创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLib创建对象时所花费的时间却比JDK多的的。

    7.问题:

    为什么,要在不改变源代码的基础上,去写一个代理类增强一些功能呢?

    因为项目大了,就有主要的功能和次要的功能,要想主要功能和次要功能一起运行,必须用AOP

    我再解释下面向对象和面向切面,面向对象OOP,面向的是主要功能的对象,而AOP是面向对象OOP的一个补充,面向次要功能的对象

    目的是为了降低耦合度,提高代码的复用性。(自己总结的,仅供参考,你懂的,嘻嘻)。

  • 相关阅读:
    19面向对象--特殊成员
    18面向对象--成员和组合
    17面向对象--三大特性
    16异常处理
    15(os、random、sys、)(json、pickle )
    14python模块之re
    13正则表达式
    12内置函数补充,递归以及二分法。
    Linux环境ftp搭建及问题总结
    运维之路—linux忘记密码修改(rd.breake 方式)
  • 原文地址:https://www.cnblogs.com/LEPENGYANG/p/15634870.html
Copyright © 2020-2023  润新知