Java静态代理案例怎么实操

wen java案例 29

Java静态代理案例实操:从入门到项目落地的完整指南

目录导读

  1. 静态代理是什么?核心概念速览
  2. 为什么要用静态代理?3大场景剖析
  3. 手把手案例实操:用户权限校验系统
  4. 静态代理的优缺点与常见坑点
  5. 问答环节:新手高频问题解答
  6. 静态代理 vs 动态代理:何时选谁?

静态代理是什么?核心概念速览

静态代理是Java设计模式中代理模式的一种实现方式,它在编译期就确定了代理类与目标类的关系,由开发人员手动编写代理类代码。

Java静态代理案例怎么实操

核心三要素:

  • 目标类(RealSubject):真正执行业务逻辑的类
  • 代理类(Proxy):持有目标类引用,并在调用前后添加额外功能
  • 公共接口(Subject):目标类和代理类共同实现的接口,保证替换透明性

关键区别:静态代理的代理类在代码编译时已经存在,而动态代理的代理类在运行时才生成。


为什么要用静态代理?3大场景剖析

场景1:日志记录与监控

在不修改原有业务代码的前提下,为每个方法调用增加日志输出。

场景2:权限控制

在方法执行前检查用户是否有操作权限,避免将权限逻辑侵入业务代码。

场景3:延迟加载

当目标对象创建成本高(如数据库连接),通过代理控制实际初始化时机。

现实类比:你在淘宝买东西,快递员(代理)帮你完成验货、签收(增强功能),而商家(目标类)只需要负责发货。


手把手案例实操:用户权限校验系统

假设我们需要为一个用户管理系统添加操作日志记录功能,但又不允许修改原有UserService代码。

步骤1:定义公共接口

public interface UserService {
    void addUser(String username);
    void deleteUser(int userId);
}

步骤2:编写目标类

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        System.out.println("添加用户:" + username);
        // 实际数据库操作...
    }
    @Override
    public void deleteUser(int userId) {
        System.out.println("删除用户ID:" + userId);
    }
}

步骤3:创建代理类

public class UserServiceProxy implements UserService {
    private UserService target;  // 持有目标对象
    public UserServiceProxy(UserService target) {
        this.target = target;
    }
    @Override
    public void addUser(String username) {
        System.out.println("[日志] 开始添加用户:" + username);
        long start = System.currentTimeMillis();
        target.addUser(username);  // 调用真实方法
        long end = System.currentTimeMillis();
        System.out.println("[日志] 添加用户完成,耗时:" + (end - start) + "ms");
    }
    @Override
    public void deleteUser(int userId) {
        System.out.println("[日志] 开始删除用户ID:" + userId);
        target.deleteUser(userId);
        System.out.println("[日志] 删除用户完成");
    }
}

步骤4:客户端调用

public class Client {
    public static void main(String[] args) {
        // 创建目标对象
        UserService realService = new UserServiceImpl();
        // 创建代理对象,将目标对象注入
        UserService proxy = new UserServiceProxy(realService);
        // 通过代理调用,自动添加日志
        proxy.addUser("张三");
        proxy.deleteUser(1001);
    }
}

运行结果

[日志] 开始添加用户:张三
添加用户:张三
[日志] 添加用户完成,耗时:2ms
[日志] 开始删除用户ID:1001
删除用户ID:1001
[日志] 删除用户完成

注意:如果接口中新增方法,代理类也必须同步添加对应方法,这是静态代理的主要维护成本。


静态代理的优缺点与常见坑点

优点

  • 简单直观:代码逻辑清晰,容易理解和调试
  • 符合开闭原则:扩展功能时无需修改目标类
  • 运行效率高:相比动态代理,无需反射调用

缺点

  • 代码冗余:每个被代理的类都需要手动编写代理类
  • 维护成本高:接口方法增加时,所有代理类都要修改
  • 代理类膨胀:项目中出现大量代理类,管理困难

常见坑点

  • 忘记实现接口:静态代理必须基于接口,否则无法替换
  • 代理对象管理不当:直接new代理类,没有统一管理或依赖注入
  • 循环代理:代理类中又调用了自身,导致无限递归

问答环节:新手高频问题解答

Q1:静态代理一定要有接口吗? A:不一定,但如果没有接口,就需要继承目标类(CGLIB方式),标准的静态代理模式推荐基于接口,因为接口能保证代理类和目标类行为一致性。

Q2:代理类和方法名必须和目标类完全一样吗? A:必须!因为代理类和目标类实现同一个接口,方法签名(方法名+参数)完全一致。

Q3:一个代理类可以代理多个目标类吗? A:可以,前提是这些目标类实现了相同接口,但通常一个代理类只代理一种接口类型,否则逻辑会混乱。

Q4:静态代理能代理final类吗? A:不能,如果目标类是final类,无法通过接口代理(因为final类不能实现接口?不,接口实现没有final限制),但是如果你使用继承方式,final类不能被继承,所以最稳妥的方式始终是面向接口编程。

Q5:实际项目中很少用静态代理吧? A:是的,生产环境更多用Spring AOP(基于动态代理)或lombok的@Slf4j等方案,但静态代理是理解AOP和动态代理的基础,面试中常被问到“代理模式实现”。


静态代理 vs 动态代理:何时选谁?

对比维度 静态代理 JDK动态代理 CGLIB动态代理
原理 编译期手动编写 运行时生成代理类 运行时生成子类
接口要求 必须实现接口 必须实现接口 无需接口,可代理类
性能 较高(无反射) 有反射开销 有字节码增强开销
维护成本 高(类多) 低(只需InvocationHandler) 低(依赖库)
适用场景 小项目或学习 企业级应用(如Spring AOP) 无法改接口的遗留系统

选择建议

  • 如果你只需要代理1-2个类且业务稳定 → 静态代理
  • 如果你需要代理大量类或业务变化快 → 首选JDK动态代理
  • 如果你的目标类没有实现接口 → 使用CGLIB

静态代理是理解代理模式的第一步,虽然在实际大型项目中已逐渐被动态代理替代,但它“增强方法而不入侵业务”的思想是所有AOP框架的基石,通过今天的手把手案例,你应该能独立完成日志记录、权限校验等增强功能的静态代理实现。

下一步行动:尝试将上述代码转换成JDK动态代理实现,对比两者的代码量差异,你会发现在动态代理中,一个InvocationHandler就能代理所有接口方法,而不需要为每个类编写代理类。

抱歉,评论功能暂时关闭!