【Java面试题】动态代理的几种实现方式及优缺点

一、JDK 动态代理(基于接口)

核心实现
import java.lang.reflect.*;

// 1. 定义接口
interface UserService {
    void saveUser(String name);
}

// 2. 目标类(实现接口)
class UserServiceImpl implements UserService {
    @Override
    public void saveUser(String name) {
        System.out.println("保存用户: " + name);
    }
}

// 3. 实现 InvocationHandler
class LogHandler implements InvocationHandler {
    private final Object target; // 目标对象

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("[JDK代理] 方法调用前: " + method.getName());
        Object result = method.invoke(target, args); // 反射调用目标方法
        System.out.println("[JDK代理] 方法调用后");
        return result;
    }
}

// 4. 使用示例
public class JdkProxyDemo {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new LogHandler(target)
        );
        proxy.saveUser("张三"); // 输出代理增强后的逻辑
    }
}

关键点

  • 通过 Proxy.newProxyInstance() 动态生成代理类字节码。
  • InvocationHandler.invoke() 拦截所有方法调用,插入增强逻辑。
优缺点
优点 缺点
原生支持,无需第三方库 只能代理接口实现类
与目标接口解耦 无法代理 final/private 方法
Java 8+ 反射性能优化较好 对未实现接口的类无效

二、CGLIB 动态代理(基于继承)

核心实现
import net.sf.cglib.proxy.*;

// 1. 目标类(无需接口)
class OrderService {
    public void createOrder(String orderId) {
        System.out.println("创建订单: " + orderId);
    }
}

// 2. 实现 MethodInterceptor
class LogInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("[CGLIB代理] 方法调用前: " + method.getName());
        Object result = proxy.invokeSuper(obj, args); // 调用父类(目标类)方法
        System.out.println("[CGLIB代理] 方法调用后");
        return result;
    }
}

// 3. 使用示例
public class CglibProxyDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderService.class); // 设置父类(目标类)
        enhancer.setCallback(new LogInterceptor()); // 设置回调
        OrderService proxy = (OrderService) enhancer.create(); // 生成代理对象
        proxy.createOrder("ORDER_001"); // 输出增强逻辑
    }
}

关键点

  • 通过 Enhancer 生成目标类的子类字节码。
  • MethodInterceptor.intercept() 拦截方法,MethodProxy.invokeSuper() 调用父类方法。
优缺点
优点 缺点
可代理任意类(非final) 需引入第三方库(CGLIB)
支持代理普通方法和部分protected方法 无法代理 final 类或方法
执行效率高(FastClass 机制跳过反射) 初始化慢(需生成字节码)

三、对比与选型建议

特性 JDK 动态代理 CGLIB 动态代理
代理方式 基于接口 基于继承
性能 Java 8+ 反射优化后接近 CGLIB 方法调用更快(无反射开销)
限制 依赖接口 无法代理 final 类/方法
框架应用 Spring AOP(有接口时) Spring Boot 2.x+ 默认
选型场景
  • 优先 JDK 代理:目标类已实现接口,且需轻量级无依赖(如简单日志增强)。
  • 优先 CGLIB:目标类无接口,或需代理非 final 方法(如 Spring 的类级别代理)。
  • 混合使用:Spring AOP 自动根据目标类选择代理方式,支持配置强制指定。

⚠️ 四、注意事项

  1. 代理失效场景
    • 目标对象内部调用(如 this.method())无法被代理拦截。
    • 静态方法或私有方法无法代理(CGLIB 仅重写非 final 的实例方法)。
  2. 性能优化
    • 高频调用场景用 CGLIB(FastClass 机制)。
    • JDK 代理可通过缓存代理类减少生成开销。
  3. JDK 17+ 兼容性
    • CGLIB 需添加 JVM 参数 --add-opens 以突破模块访问限制。

总结:动态代理通过解耦增强逻辑与业务代码,是 AOP 和框架设计的核心。JDK 代理轻量但受限于接口,CGLIB 灵活高效但需权衡依赖与初始化成本。实际选型需结合目标类结构、性能需求及框架支持综合判断。

你可能感兴趣的:(Java面试题,java,面试,动态代理)