zoukankan      html  css  js  c++  java
  • JDK和CGLIB动态代理区别

    今天阿里一面没有回答好这个问题,只达到了CGLIB是用的字节码实现,JDK是用接口实现,现在补齐知识点.

    知识补充
    JDK动态代理实现原理(jdk8):https://blog.csdn.net/yhl_jxy/article/details/80586785

    CGLIB动态代理实现原理:https://blog.csdn.net/yhl_jxy/article/details/80633194

    一 JDK和CGLIB动态代理原理

    1.JDK动态代理

    利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,

    在调用具体方法前调用InvokeHandler来处理。

    2.CGLIB动态代理

    利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    3.合适使用JDK还是CGLIB?

    1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。

    2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP。

    3)如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。

    4.如何强制使用CGLIB实现AOP

    1)添加CGLIB库(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)

    2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

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

    1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类。

    2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,

    并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,

    对于final类或方法,是无法继承的。

    6.CGLIB比JDK快?

    1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,

    在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,

    因为CGLib原理是动态生成被代理类的子类。

    2)在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,

    只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理,

    总之,每一次jdk版本升级,jdk代理效率都得到提升,而CGLIB代理消息确有点跟不上步伐。

    7.Spring如何选择用JDK还是CGLIB?

    1)当Bean实现接口时,Spring就会用JDK的动态代理。

    2)当Bean没有实现接口时,Spring使用CGlib是实现。

    3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。

    二 代码示范

    接口:

    public interface UserService {
        void addUser();
        void updateUser();
        void deleteUser();
    }
    

    实现类:

    public class UserServiceImpl implements UserService {
        @Override
        public void addUser() {
            System.out.println("add_user");
        }
    
        @Override
        public void updateUser() {
            System.out.println("update_user");
        }
    
        @Override
        public void deleteUser() {
            System.out.println("delete_user");
        }
    }
    

    切面类:

    public class MyAspect {
        public void before(){
            System.out.println("鸡首");
        }
    
        public void after(){
            System.out.println("牛后");
        }
    }
    

    JDK代理实现:

    public class MyBeanFactory {
    
        public static UserService createUserService(){
            //1 目标类
            final UserService userService = new UserServiceImpl();
            //2切面类
            final MyAspect myAspect = new MyAspect();
    		/* 3 代理类:将目标类(切入点)和 切面类(通知) 结合 --> 切面
    		 * 	Proxy.newProxyInstance
    		 * 		参数1:loader ,类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。
    		 * 			一般情况:当前类.class.getClassLoader();
    		 * 					目标类实例.getClass().get...
    		 * 		参数2:Class[] interfaces 代理类需要实现的所有接口
    		 * 			方式1:目标类实例.getClass().getInterfaces()  ;注意:只能获得自己接口,不能获得父元素接口
    		 * 			方式2:new Class[]{UserService.class}
    		 * 			例如:jdbc 驱动  --> DriverManager  获得接口 Connection
    		 * 		参数3:InvocationHandler  处理类,接口,必须进行实现类,一般采用匿名内部
    		 * 			提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke
    		 * 				参数31:Object proxy :代理对象
    		 * 				参数32:Method method : 代理对象当前执行的方法的描述对象(反射)
    		 * 					执行方法名:method.getName()
    		 * 					执行方法:method.invoke(对象,实际参数)
    		 * 				参数33:Object[] args :方法实际参数
    		 *
    		 */
    
            UserService proxyUserService = (UserService) Proxy.newProxyInstance(
                    MyBeanFactory.class.getClassLoader(),
                    userService.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            myAspect.before();
                            Object obj = method.invoke(userService,args);
                            myAspect.after();
                            return obj;
                        }
                    }
            );
            return proxyUserService;
        }
    }
    

    测试类:

    public class TestProxy {
    
        @Test
        public void testproxy(){
            UserService userService = MyBeanFactory.createUserService();
            userService.addUser();
            userService.updateUser();
            userService.deleteUser();
        }
    
    }
    

    CGLIB代理实现:
    目标类:

    public class UserServcie {
        public void addUser() {
            System.out.println("b_cglib addUser");
        }
    
        public void updateUser() {
            System.out.println("b_cglib updateUser");
        }
    
        public void deleteUser() {
            System.out.println("b_cglib deleteUser");
        }
    
    }
    

    切面类:

    public class MyAspect {
        public void before(){
            System.out.println("鸡首2");
        }
    
        public void after(){
            System.out.println("牛后2");
        }
    }
    

    代理类:

    public class MyBeanFactory {
    
        public static UserServcie createuserService(){
            //1 目标类
            final UserServcie userServcie = new UserServcie();
            //2切面类
            final MyAspect myAspect = new MyAspect();
            //3.代理类 ,采用cglib,底层创建目标类的子类
            //3.1 核心类
            Enhancer enhancer = new Enhancer();
            //3.2 确定父类
            enhancer.setSuperclass(userServcie.getClass());
            /* 3.3 设置回调函数 , MethodInterceptor接口 等效 jdk InvocationHandler接口
    		 * 	intercept() 等效 jdk  invoke()
    		 * 		参数1、参数2、参数3:以invoke一样
    		 * 		参数4:methodProxy 方法的代理
    		 */
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    myAspect.before();
                    //执行目标类的方法
                    Object obj = method.invoke(userServcie,args);
                    // * 执行代理类的父类 ,执行目标类 (目标类和代理类 父子关系)
                    methodProxy.invokeSuper(proxy,args);
                    myAspect.after();
                    return obj;
                }
            });
            UserServcie proxyService = (UserServcie) enhancer.create();
            return proxyService;
        }
    
    }
    

    测试类:

    public class TestCglib {
    
        @Test
        public void testchlib(){
            UserServcie userServcie = MyBeanFactory.createuserService();
            userServcie.addUser();
            userServcie.updateUser();
            userServcie.deleteUser();
        }
    }
    

    JDK和CGLIB动态代理总结

    JDK代理是不需要第三方库支持,只需要JDK环境就可以进行代理,使用条件:

    1)实现InvocationHandler

    2)使用Proxy.newProxyInstance产生代理对象

    3)被代理的对象必须要实现接口

    CGLib必须依赖于CGLib的类库,但是它需要类来实现任何接口代理的是指定的类生成一个子类,

    覆盖其中的方法,是一种继承但是针对接口编程的环境下推荐使用JDK的代理;

  • 相关阅读:
    咖啡豆(JavaBean)•香
    SOC FPGA篇之 如何使用VNC 扩展桌面
    C指针地址运算
    nasm 指令学习
    CPL DPL RPL 及特权间的跳转
    ubuntu终端命令
    自动动手写操作系统 中 _disp_int 堆栈保护错误
    makefile 详解1
    [ 转载] Linux/Unix环境下的make和makefile详解2
    汇编指令: LGDT、LIDT、LLDT、LMSW、LOADALL、LOADALL286、LOCK、LODSB、LODSW、LODSD
  • 原文地址:https://www.cnblogs.com/gongcheng-/p/14624503.html
Copyright © 2011-2022 走看看