Java设计模式之结构型—代理模式

发布于:2025-09-05 ⋅ 阅读:(22) ⋅ 点赞:(0)

Java中最常用的设计模式-CSDN博客

 静态代理

“在编译期就写死代理类,实现/继承同一个接口/父类,把真实对象包一层,手动加逻辑。”

  1. 场景

  • 对真实对象做 日志、权限、缓存 等横切增强

  • 真实对象不许改动(第三方、旧代码)

  • 代理类数量固定 → 适合 接口少、实现类少 的场景

代码块

// 1) 公共接口
public interface UserService {
    void save(String name);
}

// 2) 真实对象
public class UserServiceImpl implements UserService {
    public void save(String name) {
        System.out.println("保存用户:" + name);
    }
}

// 3) 代理类(编译期手写)
public class UserServiceProxy implements UserService {
    private final UserService real;   // 组合真实对象

    public UserServiceProxy(UserService real) {
        this.real = real;
    }

    public void save(String name) {
        System.out.println("前置日志:准备保存");
        real.save(name);              // 调用真实对象
        System.out.println("后置日志:保存完成");
    }
}

// 4) 使用
UserService service = new UserServiceProxy(new UserServiceImpl());
service.save("Alice");

优缺点

优点 缺点
简单直观,无运行时开销 每多一个接口/实现,就要手写一个代理类 → 类爆炸
编译期即可检查类型 无法代理 未实现接口的方法

动态代理

Dynamic Proxy

方案 技术 代理目标 关键类
JDK 动态代理 java.lang.reflect.Proxy + InvocationHandler 只能代理接口 Proxy.newProxyInstance()
CGLIB/ByteBuddy 字节码生成库 接口 + 普通类 Enhancer.create()

JDK 动态代理

// 订单服务接口
public interface OrderService {
    void create();          // 创建订单的业务方法
}

// 订单服务实现类
public class OrderServiceImpl implements OrderService {
    public void create() {
        System.out.println("创建订单");  // 真正的业务逻辑
    }
}

// 日志处理器:实现 InvocationHandler 接口,用来在真实方法前后附加日志
public class LogHandler implements InvocationHandler {
    private final Object target;   // 被代理的“真实对象”

    public LogHandler(Object target) {
        this.target = target;      // 构造时把真实对象传进来
    }

    // 每当代理对象上的任何方法被调用时,都会走到这里
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置日志");                // 1. 前置增强(如记录开始时间、打印参数等)
        Object result = method.invoke(target, args);  // 2. 通过反射调用真实对象的方法
        System.out.println("后置日志");                // 3. 后置增强(如记录结束时间、打印返回值等)
        return result;                               // 4. 把真实方法的返回值原样返回
    }
}

// ---------------- 使用示例 ----------------
public class Main {
    public static void main(String[] args) {
        // 1. 用 JDK 的 Proxy 工具生成一个“代理对象”
        OrderService proxy = (OrderService) Proxy.newProxyInstance(
                OrderService.class.getClassLoader(),   // 类加载器:告诉 JVM 把代理类加载到哪个命名空间
                new Class<?>[]{OrderService.class},    // 需要实现的接口列表(可多个)
                new LogHandler(new OrderServiceImpl()) // 调用处理器:真正干活儿的 LogHandler
        );

        // 2. 通过代理对象调用方法
        proxy.create();  // 控制台会依次打印:
                         //   前置日志
                         //   创建订单
                         //   后置日志
    }
}

public Object invoke(Object proxy, Method method, Object[] args)参数含义

  1. Object proxy
    这是 代理对象本身(就是 Proxy.newProxyInstance() 返回的那个对象)。

    • 如果你在这里面再调用 proxy.toString()proxy.create() 之类的方法,会再次进入 invoke,极易造成无限递归,所以通常不会直接用它。

    • 主要用途:在需要判断 proxy == 某个代理实例 或打印调试信息时才会用到。

  2. Method method
    本次被调用的方法对象

    • 通过 method.getName() 可以知道调的是哪个方法(如 create)。

    • 通过 method.getParameterTypes()method.getReturnType() 等可以拿到签名信息,用来做通用逻辑(例如所有 get* 方法做缓存、所有 save* 方法做事务等)。

  3. Object[] args
    本次方法调用时传进来的实参数组,按声明顺序排列。

    • 如果方法无参,它就是 null 或空数组。

    • 可以通过 args[i] 读取或修改参数值,实现诸如统一参数校验、脱敏、记录日志等横切逻辑。

CGLIB 最简示例(可代理类)

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
    System.out.println("前置");
    Object result = proxy.invokeSuper(obj, args);
    System.out.println("后置");
    return result;
});
UserService proxy = (UserService) enhancer.create();
proxy.save("Bob");

动态代理 vs 静态代理

维度 动态代理 静态代理
代码量 1 个 InvocationHandler 即可 每接口/实现都要手写
代理范围 任意接口/类(CGLIB) 只能固定接口
性能 反射调用略慢(JDK)/ ASM 接近原生(CGLIB) 直接调用,零反射
生成时机 运行时 编译期

网站公告

今日签到

点亮在社区的每一天
去签到