JDK 与 CGLIB 的区别及应用

JDK 与 CGLIB 的区别及应用

简介

动态代理是一种在运行时动态创建代理对象的机制,它可以在不修改源码的情况下为原始对象添加额外的功能。在软件开发中,动态代理被广泛应用于AOP(面向切面编程)、事务管理、远程调用等方面。

JDK动态代理和CGLIB动态代理是两种常见的动态代理实现方式。JDK动态代理是基于接口的代理,它要求目标对象实现一个接口,然后通过反射机制来创建代理对象。而CGLIB动态代理则是基于继承的代理,它可以代理没有实现接口的类,并通过生成目标类的子类来实现代理。

本文将对比JDK动态代理和CGLIB动态代理的特点、优缺点以及适用场景,以帮助读者更好地理解它们的差异和选择合适的动态代理实现方式。

两者有何区别

  1. 基于接口 vs 基于继承:
    ◦ JDK动态代理是通过反射机制来实现的,它要求目标对象必须实现一个接口。在运行时,JDK动态代理会创建一个实现了代理接口的代理类,并在代理类中调用目标对象的方法。
    ◦ CGLIB动态代理则是通过继承目标类来实现的,它可以代理没有实现接口的类。在运行时,CGLIB动态代理会生成目标类的子类,并在子类中重写目标对象的方法来实现代理。
  2. 性能:
    ◦ 一般情况下,JDK动态代理的性能要略优于CGLIB动态代理,因为JDK动态代理是基于接口的代理,而CGLIB动态代理是基于继承的代理,生成代理类的过程更为复杂。
  3. 适用场景:
    ◦ JDK动态代理适用于需要代理接口的情况,比如对服务层方法进行切面处理、事务管理等。
    ◦ CGLIB动态代理适用于代理没有实现接口的类的情况,比如对类的方法进行切面处理、性能监控等。
  4. 对目标对象的要求:
    ◦ JDK动态代理要求目标对象实现接口,而CGLIB动态代理则可以代理没有实现接口的类。
  5. 注意事项:
    ◦ 如果想要实现JDK动态代理那么代理类必须实现接口,否则不能使用。
    ◦ CGLIB通过生成继承自目标类的子类来实现代理,因此目标类和目标方法不能被声明为final,否则CGLIB无法生成代理类。

代码实现

JDK动态代理

JDK 与 CGLIB 的区别及应用_第1张图片

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代理类

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 {
        System.out.println("===增强开始===");
        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(":我是零陵上将军邢道荣");
    }
}

运行Test类main方法
JDK 与 CGLIB 的区别及应用_第2张图片

CGlib动态代理

JDK 与 CGLIB 的区别及应用_第3张图片
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() {
        // 创建一个工具类
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(target.getClass());
        // 设置回调函数
        enhancer.setCallback(this);
        // 创建子类对象,即代理对象
        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();
        userService.deleteUser();
    }
}

运行Test类main方法
JDK 与 CGLIB 的区别及应用_第4张图片

Spring AOP 创建代理

@Override
public 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. 如果目标对象实现了接口,也可以强制使用CGLIB

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

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

你可能感兴趣的:(java,开发语言,jvm)