Java的动态代理
Java的动态代理
首先理解代理模式的场景:在调用目标方法的前后做一些增强操作(例如参数校验、日志记录、事务管理等)
代理模式的实现方式有两种,一种是静态代理(通过编写代码实现),一种是动态代理(JDK动态代理:通过反射实现、CGLIB:开源第三方工具库,通过继承实现对目标类的增强)
实现方式:JDK动态代理是通过反射实现的(实现InvocationHandler接口及其接口方法),而CGLIB动态代理是通过继承目标类来实现的(如果CGLIB要通过继承方式实现对目标类增强,则目标类和类方法不同用final修饰)
依赖库:JDK动态代理是Java自带的库,不需要额外的依赖,而CGLIB动态代理需要依赖cglib库。
在使用动态代理时,可以根据需要和具体的场景选择合适的实现方式,JDK动态代理适用于接口代理的场景,而CGLIB动态代理适用于类代理的场景
JDK动态代理
场景介绍
所谓代理,就是在目标方法执行前后做一些增强操作。此处以User操作为例,模拟用户的新增、修改前后做一些业务操作
【1】构建核心操作:UserService、UserServiceImpl,模拟addUser、updateUser方法
【2】定义代理实现类UserProxy:实现InvocationHandler方法(实现invoke方法),编写增强的业务逻辑,调用代理对象方法
【3】编写测试类测试代理
【1】定义业务逻辑操作(定义目标类操作)
public interface UserService {
// 添加用户
void addUser();
// 修改用户
void updateUser(String username);
}
public class UserServiceImpl implements UserService{
@Override
public void addUser() {
System.out.println("添加用户");
}
@Override
public void updateUser(String username) {
System.out.println("修改用户:"+username);
}
}
【2】定义代理类
/**
* 构建一个UserProxy代理类,实现InvocationHandler的invoke方法
*/
public class UserProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
/**
* 构建一个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 {
// 自定义业务逻辑实现方法增强
System.out.println("JDK代理增强......before......");
// 调用代理对象方法
Object res = method.invoke(target,args);
System.out.println("JDK代理增强......after......");
// 返回代理对象
return res;
}
}
【3】定义测试类测试代理执行
public class JdkDynamicProxyTest {
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(":hello noob");
}
}
CGLIB动态代理
CGLIB动态代理实现步骤
【1】构建核心操作:UserService、UserServiceImpl,模拟addUser、updateUser方法(也可以自定义一个目标类执行一些操作,例如一个普通类UserAction)
【2】定义代理实现类UserServiceCGlib:实现MethodInterceptor方法(实现intercept方法),编写增强的业务逻辑,调用代理对象方法
【3】编写测试类测试代理
<dependencies>
<!-- 引入CGLIB代理 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
【1】定义业务逻辑操作(定义目标类操作)
/**
* 定义业务操作类
*/
public class UserAction {
void doSomething(){
System.out.println("用户执行操作中.......");
}
}
【2】定义代理类
/**
* 定义UserAction代理类,基于CGLIB实现,实现MethodInterceptor
*/
public class UserActionCGlib implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return null;
}
}
/**
* 定义UserAction代理类,基于CGLIB实现,实现MethodInterceptor
*/
public class UserActionCGlib implements MethodInterceptor {
// 定义代理目标
private Object target;
// 构造函数初始化代理目标
public UserActionCGlib(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("===CGLIB增强 before===");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("===CGLIB增强 after===");
return result;
}
}
【3】定义测试类测试代理执行
public class UserActionCGlibTest {
public static void main(String[] args) {
// 定义代理
UserActionCGlib userActionCGlib = new UserActionCGlib(new UserAction());
// 获取代理后的目标对象
UserAction userAction = (UserAction) userActionCGlib.getProxyInstance();
// 指定目标方法
userAction.doSomething();
}
}
JDK动态代理 VS CGLIB动态代理
JDK动态代理 VS CGLIB动态代理
动态代理方式 | JDK动态代理 | CGLIB动态代理 |
---|---|---|
代理对象 | 代理实现了接口的类 | 可代理没有实现接口的类,通过继承目标类生成子类的方式来创建代理对象 |
实现机制 | 实现 InvocationHandler 接口 | 实现 MethodInterceptor 接口 |
性能差异 | 基于反射机制实现代理,调用性能不如CGLIB | 直接操作字节码生成新的类,避免反射的开销 |
使用场景 | 适用于接口驱动的代理场景,不涉及具体类、只关心接口定义时适用 | 可代理没有实现接口的类,通过继承增强功能的场景适用 |
关联依赖 | Java核心API的一部分,不需引入任何依赖 | 需引入第三方cglib包 |
扩展性、复杂性 | 使用简单 | 提供更多的控制(方法拦截、回调等),使用复杂 |
当使用JDK动态代理时,Java运行时会动态生成代理类的字节码,并直接加载到JVM。这个过程是在内存中完成的,通常不会输出为磁盘上的字节码文件(即.class文件)。JDK动态代理使用java.lang.reflect.Proxy类的newProxyInstance方法来在运行时动态创建代理对象。其会根据传入的接口信息,使用反射机制生成一个实现了指定接口的代理类,并将其加载到JVM中
CGLIB(Code Generation Library)也会在运行时生成新的类的字节码,但它是通过操作底层字节码(使用第三方库如ASM)来创建新的类。与JDK动态代理类似,CGLIB通常会将生成的字节码直接加载到JVM中,而不是写入到文件系统。CGLIB是通过其Enhancer类来生成新的代理对象,这些对象是原始类的子类。
Spring框架的选择
Spring框架优选选择JDK动态代理模式,主要是基于其兼容性(JDK动态代理是Java核心的一部分,不依赖第三方JAR)、简洁性、避免类加载问题(CGLIB会为目标对象创建一个子类,在一些复杂的类加载环境可能存在一点问题)
JDK动态代理和CGLIB动态代理都被广泛地应用在各种Java框架中。Spring可以根据情况选择使用JDK动态代理还是CGLIB动态代理。默认情况下,Spring会优先使用JDK动态代理,如果要代理的对象没有实现接口,则会使用CGLIB动态代理