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

参考回答**

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

动态代理的作用

  1. 解耦代码逻辑:将核心业务逻辑和非核心功能(如日志、权限检查等)分离。
  2. 提高代码复用性:同样的代理逻辑可以应用于多个目标对象。
  3. 增强功能:对方法执行前后添加额外的操作。

Java中实现动态代理的方式

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

  1. 基于 JDK 的动态代理(仅支持接口代理)
  2. 基于 CGLIB 的动态代理(支持类代理)

详细讲解与拓展

1. 动态代理的定义与静态代理的对比

静态代理是在编译期就确定了代理类,开发者需要手动创建代理类并实现对应的方法。这种方式耦合度高,代码冗余。

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


2. 动态代理的实现方式

(1) 基于 JDK 的动态代理

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

实现步骤

  1. 定义一个接口及其实现类。
  2. 创建一个自定义的 InvocationHandler,在其中增强方法逻辑。
  3. 使用 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)是一个开源的字节码生成框架,允许动态生成类。
特点:可以代理普通类(不需要实现接口),通过继承目标类并重写其方法来实现代理。

实现步骤

  1. 添加 CGLIB 的依赖(如果是 Spring 项目,可以直接用内置的 CGLIB 功能)。
  2. 创建一个自定义 MethodInterceptor,在其中增强方法逻辑。
  3. 使用 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. 动态代理的常见应用

  1. Spring AOP:Spring 的面向切面编程(AOP)大量使用了动态代理(JDK 和 CGLIB)。
  2. MyBatis:MyBatis 的 Mapper 接口代理也是通过动态代理实现的。
  3. 日志记录:在方法调用前后插入日志。
  4. 权限校验:检查用户是否有权限执行某个操作。
  5. 事务管理:在方法执行前后添加事务的开始和提交逻辑。

4. 动态代理的优缺点

优点

  1. 代码简洁:减少了编写代理类的工作量。
  2. 灵活性高:可以动态代理任意类,在运行时动态生成代理对象。
  3. 增强功能:在不修改原有代码的情况下,实现功能扩展。

缺点

  1. 性能开销:由于动态代理依赖于反射,性能比直接调用方法略低。
  2. 复杂性:相比静态代理,动态代理的代码更抽象,不易理解。

5. JDK 动态代理与 CGLIB 动态代理的对比

特性 JDK 动态代理 CGLIB 动态代理
实现方式 基于接口实现 基于继承实现
是否需要接口 必须实现接口 不需要实现接口
性能 较低(依赖反射) 较高(字节码操作)
适用场景 代理接口 代理普通类
Spring 中的使用 默认方式(有接口时) 无接口时默认使用 CGLIB

6. 总结与最佳实践

  • 优先使用 JDK 动态代理:如果目标类有接口,优先使用 JDK 动态代理,简单易用。
  • 使用 CGLIB 动态代理:如果目标类没有接口,或需要更高性能时使用 CGLIB。
  • 结合 AOP 使用:在实际开发中,可以使用 Spring 的 AOP 功能来简化动态代理的实现,专注于业务逻辑。

发表回复

后才能评论