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的代理;

posted @ 2021-04-06 23:02  Milford  阅读(757)  评论(0编辑  收藏  举报