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