什么是动态代理?有什么用?Java中可以怎么样实现动态代理?

参考回答**
动态代理是一种设计模式,它允许在运行时动态创建代理对象,并决定其行为。动态代理主要用于拦截方法调用,对方法的执行进行增强,例如日志记录、权限校验、事务管理等。

动态代理的作用
解耦代码逻辑:将核心业务逻辑和非核心功能(如日志、权限检查等)分离。

提高代码复用性:同样的代理逻辑可以应用于多个目标对象。

增强功能:对方法执行前后添加额外的操作。

Java中实现动态代理的方式
Java 中有两种主要方式实现动态代理:

基于 JDK 的动态代理(仅支持接口代理)

基于 CGLIB 的动态代理(支持类代理)

详细讲解与拓展
1. 动态代理的定义与静态代理的对比
静态代理是在编译期就确定了代理类,开发者需要手动创建代理类并实现对应的方法。这种方式耦合度高,代码冗余。

动态代理则是在运行时通过反射动态生成代理类,开发者无需手动创建代理类。

  1. 动态代理的实现方式
    (1) 基于 JDK 的动态代理
    JDK 提供了 java.lang.reflect.Proxy 类和 InvocationHandler 接口来实现动态代理。
    特点:只能代理实现了接口的类。

实现步骤:

定义一个接口及其实现类。

创建一个自定义的 InvocationHandler,在其中增强方法逻辑。

使用 Proxy.newProxyInstance 创建代理对象。

示例代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 1. 定义接口
interface Service {
void performTask();
}

// 2. 接口实现类
class ServiceImpl implements Service {
@Override
public void performTask() {
System.out.println(“Executing core task…”);
}
}

// 3. 自定义 InvocationHandler
class ServiceProxyHandler implements InvocationHandler {
private Object target;

public ServiceProxyHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(“Before method call: Logging…”);
Object result = method.invoke(target, args); // 调用目标对象的方法
System.out.println(“After method call: Logging…”);
return result;
}
}

// 4. 使用动态代理
public class JdkDynamicProxyExample {
public static void main(String[] args) {
Service service = new ServiceImpl();

// 创建代理对象
Service proxy = (Service) Proxy.newProxyInstance(
service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
new ServiceProxyHandler(service)
);

// 调用代理方法
proxy.performTask();
}
}
输出结果:

Before method call: Logging…
Executing core task…
After method call: Logging…
(2) 基于 CGLIB 的动态代理
CGLIB(Code Generation Library)是一个开源的字节码生成框架,允许动态生成类。
特点:可以代理普通类(不需要实现接口),通过继承目标类并重写其方法来实现代理。

实现步骤:

添加 CGLIB 的依赖(如果是 Spring 项目,可以直接用内置的 CGLIB 功能)。

创建一个自定义 MethodInterceptor,在其中增强方法逻辑。

使用 Enhancer 创建代理对象。

示例代码:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 1. 定义目标类
class Service {
public void performTask() {
System.out.println(“Executing core task…”);
}
}

// 2. 创建自定义 MethodInterceptor
class ServiceInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println(“Before method call: Logging…”);
Object result = proxy.invokeSuper(obj, args); // 调用目标方法
System.out.println(“After method call: Logging…”);
return result;
}
}

// 3. 使用 CGLIB 动态代理
public class CglibDynamicProxyExample {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service.class);
enhancer.setCallback(new ServiceInterceptor());

    Service proxy = (Service) enhancer.create();
    proxy.performTask();
}

}
输出结果:

Before method call: Logging…
Executing core task…
After method call: Logging…
3. 动态代理的常见应用
Spring AOP:Spring 的面向切面编程(AOP)大量使用了动态代理(JDK 和 CGLIB)。

MyBatis:MyBatis 的 Mapper 接口代理也是通过动态代理实现的。

日志记录:在方法调用前后插入日志。

权限校验:检查用户是否有权限执行某个操作。

事务管理:在方法执行前后添加事务的开始和提交逻辑。

  1. 动态代理的优缺点
    优点:

代码简洁:减少了编写代理类的工作量。

灵活性高:可以动态代理任意类,在运行时动态生成代理对象。

增强功能:在不修改原有代码的情况下,实现功能扩展。

缺点:

性能开销:由于动态代理依赖于反射,性能比直接调用方法略低。

复杂性:相比静态代理,动态代理的代码更抽象,不易理解。

  1. JDK 动态代理与 CGLIB 动态代理的对比
    特性 JDK 动态代理 CGLIB 动态代理
    实现方式 基于接口实现 基于继承实现
    是否需要接口 必须实现接口 不需要实现接口
    性能 较低(依赖反射) 较高(字节码操作)
    适用场景 代理接口 代理普通类
    Spring 中的使用 默认方式(有接口时) 无接口时默认使用 CGLIB
  2. 总结与最佳实践
    优先使用 JDK 动态代理:如果目标类有接口,优先使用 JDK 动态代理,简单易用。

使用 CGLIB 动态代理:如果目标类没有接口,或需要更高性能时使用 CGLIB。

结合 AOP 使用:在实际开发中,可以使用 Spring 的 AOP 功能来简化动态代理的实现,专注于业务逻辑。

发表评论

后才能评论