[杂谈-1]动态代理-JDK与Cglib的实战分析

82 阅读3分钟

1.二者的区别:

1. 接口 vs. 类

  • JDK 动态代理

    • 只支持接口代理,目标类必须实现一个或多个接口。
    • 通过 java.lang.reflect.Proxy 类创建代理实例。
  • CGLIB 动态代理

    • 支持类代理,可以代理没有实现任何接口的普通类。
    • 通过字节码生成技术生成目标类的子类来实现代理。

2. 性能

  • JDK 动态代理

    • 相对来说性能较,因为调用时需要通过反射机制
  • CGLIB 动态代理

    • 通常性能更,因为它直接生成子类的字节码,减少了反射带来的开销。

3. 生成的代理对象

  • JDK 动态代理

    • 生成的代理对象是基于接口的,因此只能通过接口调用方法。
  • CGLIB 动态代理

    • 生成的代理对象是目标类的子类,可以直接访问目标类的非公共方法和属性。

4. 适用场景

  • JDK 动态代理

    • 更适合于接口设计良好的项目,尤其是在 Spring 框架中,通常使用 JDK 动态代理处理具有接口的服务。
  • CGLIB 动态代理

    • 在没有接口的情况下,或者需要代理具体类的场景下使用更为合适,比如需要对某些框架类进行增强时。

5. 复杂性

  • JDK 动态代理

    • 使用相对简单,只需实现接口和相应的 InvocationHandler
  • CGLIB 动态代理

    • 设置和使用相对复杂,需要依赖外部库(如 ASM)进行字节码操作。

6. 继承限制

  • JDK 动态代理

    • 不存在继承问题,只要实现接口即可。
  • CGLIB 动态代理

    • 由于是通过继承生成子类,若目标类是 final 或者有 final 方法,则无法代理。

总结

选择使用 JDK 动态代理还是 CGLIB 动态代理取决于具体需求。如果目标类实现了接口并且希望简化使用,JDK 动态代理是一个不错的选择;如果需要代理没有实现接口的类,或者希望提高性能,CGLIB 动态代理更为合适。每种方式都有其适用场景,开发者应根据具体情况进行选择。

2.代码实战

1.JDK动态代理实战

接口与实现类

// UserService.java
public interface UserService {
    void addUser(String username);
}
​
// UserServiceImpl.java
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        System.out.println("User added: " + username);
    }
}
​

JDK动态代理实现

// MyInvocationHandler.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
​
public class MyInvocationHandler implements InvocationHandler {
    private final Object target;
​
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
​
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method: " + method.getName());
        Object result = method.invoke(target, args); // 调用目标方法
        System.out.println("After method: " + method.getName());
        return result;
    }
}
​
// Main.java
import java.lang.reflect.Proxy;
​
public class Main {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserService proxy = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                new MyInvocationHandler(userService)//放入Handler的是一个接口,这个接口做为target对象
        );
​
        proxy.addUser("Alice");
    }
}
​
  • 写InvocationHandler

  • Proxy.newProxyInstance的结果是你要生成的对象,但是注意转型和三个参数

    • 接口的类加载器
    • 接口实现的接口
    • 传入了接口的InvocationHandler

2.CGLib实现

// UserServiceImpl.java (same as above)
public class UserServiceImpl {
    public void addUser(String username) {
        System.out.println("User added: " + username);
    }
}
​
// MyMethodInterceptor.java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
​
public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method: " + method.getName());
        Object result = proxy.invokeSuper(obj, args); // 调用目标方法
        System.out.println("After method: " + method.getName());
        return result;
    }
}
​
// Main.java
public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);//此时拿的是实现类,而非是接口
        enhancer.setCallback(new MyMethodInterceptor());
​
        UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
        proxy.addUser("Bob");
    }
}
​
  • 写Interceptor
  • 创建Enchancer
  • Enhancer.setSuperclass(UserServiceImpl.class);//此时拿的是实现类,而非是接口
  • enhancer.setCallback(new MyMethodInterceptor());//此时将刚刚的Interceptor注册为回调
  • UserServiceImpl proxy = (UserServiceImpl) enhancer.create();//注意:此时我们用的enchancer来创建的对象,并且要记得转型为UserServiceImpl
  • 一个有意思的一点:JDK中最后创建对象是Proxy.newProxyInstance(),而Cglib是enchancer.create();