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();