写点什么

JDK 动态代理与 CGLIB 动态代理,它俩真的不一样

  • 2021 年 12 月 02 日
  • 本文字数:2544 字

    阅读完需:约 8 分钟

摘要:一文带你搞懂 JDK 动态代理与 CGLIB 动态代理。

 

本文分享自华为云社区《一文带你搞懂JDK 动态代理与 CGLIB 动态代理》,作者: Code 皮皮虾 。

两者有何区别


1、Jdk 动态代理:利用拦截器(必须实现 InvocationHandler 接口)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理

2、 Cglib 动态代理:利用 ASM 框架,对代理对象类生成的 class 文件加载进来,通过修改其字节码生成子类来进行代理


所以:

如果想要实现 JDK 动态代理那么代理类必须实现接口,否则不能使用;

如果想要使用 CGlib 动态代理,那么代理类不能使用 final 修饰类和方法;

还有: 在 jdk6、jdk7、jdk8 逐步对 JDK 动态代理优化之后,在调用次数较少的情况下,JDK 代理效率高于 CGLIB 代理效率,只有当进行大量调用的时候,jdk6 和 jdk7 比 CGLIB 代理效率低一点,但是到 jdk8 的时候,jdk 代理效率高于 CGLIB 代理。

如何实现

JDK 动态代理



​UserService 接口


public interface UserService {
void addUser();
void updateUser(String str);
}
复制代码


​UserServiceImpl 实现类


public class UserServiceImpl implements UserService {    @Override    public void addUser() {        System.out.println("添加用户");    }
@Override public void updateUser(String str) { System.out.println("更新用户信息" + str); }}
复制代码


​UserProxy 代理类,实现 InvocationHandler 接口重写 invoke 方法


public class UserProxy implements InvocationHandler {    private Object target;
public UserProxy(Object target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = method.invoke(target, args);
System.out.println("记录日志");
return res; }}
复制代码


test 测试类


public class test {    public static void main(String[] args) {
UserServiceImpl impl = new UserServiceImpl(); UserProxy userProxy = new UserProxy(impl); UserService userService = (UserService) Proxy.newProxyInstance(impl.getClass().getClassLoader(),impl.getClass().getInterfaces(),userProxy); userService.addUser(); userService.updateUser(":我是皮皮虾"); }
}
复制代码


可见实现了增强,打印出记录日志



CGlib 动态代理


CGlib 不像是 JDK 动态代理,CGlib 需要导入 Jar 包,那么我用 SpringBoot 直接导入依赖


<dependency>    <groupId>cglib</groupId>    <artifactId>cglib</artifactId>    <version>3.3.0</version></dependency>
复制代码


​UserServiceImpl 被代理类


public class UserServiceImpl {
public void addUser() { System.out.println("添加了一个用户"); }
public void deleteUser() { System.out.println("删除了一个用户"); }
}
复制代码


​UserServiceCGlib 代理


public class UserServiceCGlib implements MethodInterceptor {    private Object target;
public UserServiceCGlib() { }
public UserServiceCGlib(Object target) { this.target = target; }
//返回一个代理对象: 是 target对象的代理对象 public Object getProxyInstance() { //1. 创建一个工具类 Enhancer enhancer = new Enhancer(); //2. 设置父类 enhancer.setSuperclass(target.getClass()); //3. 设置回调函数 enhancer.setCallback(this); //4. 创建子类对象,即代理对象 return enhancer.create(); }
@Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("增强开始~~~"); Object result = methodProxy.invokeSuper(o, objects); System.out.println("增强结束~~~"); return result; }
}
复制代码


​test 测试类


public class test {
public static void main(String[] args) { UserServiceCGlib serviceCGlib = new UserServiceCGlib(new UserServiceImpl()); UserServiceImpl userService = (UserServiceImpl)serviceCGlib.getProxyInstance(); userService.addUser(); System.out.println(); userService.deleteUser(); }
}
复制代码


​可见实现了增强,打印出记录日志



使用场景


到这里相信各位小伙伴们已经基本掌握了 JDK 动态代理和 CGlib 动态代理的区别和实现

但是,如果是在面试过程中,除了要答出以上要点,你还要回答出它们的使用场景,这其实就是面试的加分项

那么,这两个动态代理的使用场景是什么呢???

答案:Spring AOP

以下是 Spring AOP 创建代理的方法


@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } //如果 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); }}
复制代码


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

2、如果目标对象实现了接口,也可以强制使用 CGLIB3、如果目标对象没有实现了接口,必须采用 CGLIB 库,spring 会自动在 JDK 动态代理和 CGLIB 之间转换


如果需要强制使用 CGLIB 来实现 AOP,需要配置 spring.aop.proxy-target-class=true 或 @EnableAspectJAutoProxy(proxyTargetClass= true

 

点击关注,第一时间了解华为云新鲜技术~

发布于: 3 小时前阅读数: 5
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
JDK 动态代理与 CGLIB 动态代理,它俩真的不一样