Java案例如何实现委派?

wen python案例 10

Java委派模式精讲:从原理到实战案例的深度剖析

目录导读

  • 委派模式的核心定义与本质
  • Java中委派的三种实现方式
  • 实际案例1:ClassLoader中的委派模型
  • 实际案例2:Spring框架中的委派实践
  • 实际案例3:自定义委派调度器实现
  • 委派与代理、组合模式的本质区别
  • 高频问答与最佳实践总结

委派模式的核心定义与本质

委派(Delegation) 是一种将任务执行权转移给其他对象的设计思想,在Java中,委派模式通过让一个对象持有另一个对象的引用,并将本应自己处理的任务全权交给被委派者完成。

Java案例如何实现委派?

核心特征:调用者不直接实现功能,而是选择一个合适的执行者来完成。

与继承的“is-a”关系和组合的“has-a”关系不同,委派强调的是 “uses-a” ——当前对象使用另一个对象的能力。

典型案例:Java的java.util.EventListener机制中,事件源将事件处理委派给注册的监听器。


Java中委派的三种实现方式

静态委派(通过接口+组合)

// 定义执行接口
interface TaskExecutor {
    void execute(String task);
}
// 具体执行者A
class ExecutorA implements TaskExecutor {
    @Override
    public void execute(String task) {
        System.out.println("ExecutorA 正在处理: " + task);
    }
}
// 委派者
class Delegator {
    private TaskExecutor executor; // 持有被委派者引用
    public Delegator(TaskExecutor executor) {
        this.executor = executor;
    }
    public void handleTask(String task) {
        // 完全委派给executor
        executor.execute(task);
    }
}

动态委派(使用反射)

通过java.lang.reflect.ProxyMethod.invoke()实现运行时动态选择执行者。

双亲委派模型(虚拟机级别)

这是Java最著名、最基础的委派实现——类加载器。


实际案例1:ClassLoader中的委派模型

问题:为什么Java类加载器要使用双亲委派?
答案:为了防止核心API被篡改,保证类的唯一性。

实现机制

加载一个类时 -> 先问父加载器是否已加载 -> 父加载器再问自己的父加载器
-> 直到顶层Bootstrap ClassLoader -> 如果都没加载,当前加载器才尝试自己加载

关键源码(精简版)

protected Class<?> loadClass(String name, boolean resolve) {
    synchronized (getClassLoadingLock(name)) {
        // 1. 检查是否已加载
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            try {
                // 2. 委派给父加载器
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // 3. 顶层委派给Bootstrap
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // 父加载器无法加载时...
            }
            if (c == null) {
                // 4. 自己加载
                c = findClass(name);
            }
        }
        return c;
    }
}

执行流程图示

用户自定义类加载器 -> 委派给AppClassLoader -> 委派给ExtClassLoader
                                  ^                      |
                                  |                      v
                              无法加载                 启动类加载器(Bootstrap)

实际案例2:Spring框架中的委派实践

问题:Spring的DispatcherServlet如何处理请求?
答案:通过委派模式将请求分发给具体的处理器。

实现方式:委派给HandlerAdapter

// DispatcherServlet核心委派逻辑
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
    // 1. 查找处理器(委派给HandlerMapping)
    HandlerExecutionChain mappedHandler = getHandler(request);
    // 2. 获取适配器(委派给HandlerAdapter)
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    // 3. 执行处理器(委派给适配器)
    ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());
}

关键点

  • DispatcherServlet不直接处理请求,而是委派HandlerMapping查找Handler
  • 委派HandlerAdapter执行具体逻辑
  • 每个组件只负责单一职责,通过委派实现松耦合

实际案例3:自定义委派调度器实现

需求:在电商系统中,订单支付成功后需执行一系列动作(发送邮件、更新库存、记录日志),要求可动态增删执行者。

完整实现:

// 1. 定义委派接口
interface PostPaymentAction {
    void execute(PaymentOrder order);
}
// 2. 具体执行者
class EmailAction implements PostPaymentAction {
    @Override
    public void execute(PaymentOrder order) {
        System.out.println("发送支付成功邮件给: " + order.getUserId());
    }
}
class InventoryAction implements PostPaymentAction {
    @Override
    public void execute(PaymentOrder order) {
        System.out.println("扣减商品库存: " + order.getProductId());
    }
}
// 3. 委派调度器(关键)
class PaymentActionDelegator {
    private List<PostPaymentAction> actions = new ArrayList<>();
    public void addAction(PostPaymentAction action) {
        actions.add(action);
    }
    public void removeAction(PostPaymentAction action) {
        actions.remove(action);
    }
    // 核心委派方法
    public void delegateActions(PaymentOrder order) {
        for (PostPaymentAction action : actions) {
            action.execute(order); // 全权委派
        }
    }
}
// 4. 使用场景
public class PaymentService {
    private PaymentActionDelegator delegator;
    public void completePayment(PaymentOrder order) {
        // ... 支付处理逻辑
        delegator.delegateActions(order); // 委派给调度器
    }
}

优点

  • 支付服务不需要知道具体有哪些后续动作
  • 新动作只需实现接口并注册到委派器即可

高频问答Q&A

Q1:委派模式与代理模式有何区别?

  • 代理模式:代理类控制对被代理对象的访问,通常添加额外逻辑(权限、延迟加载)
  • 委派模式:单纯将任务转移给另一个对象执行,不添加额外控制逻辑
  • 关键区别:代理强调“控制”,委派强调“转交”

Q2:委派模式与职责链模式的区别?

  • 职责链:每个节点都有机会处理,处理不完传给下一个
  • 委派:通常一个对象全权处理,不形成“链”
  • 例子:Spring的HandlerInterceptor是职责链,DispatcherServlet是委派

Q3:静态委派与动态委派如何选择?

  • 静态委派:编译时确定被委派者,性能高,适合固定场景
  • 动态委派:运行时通过反射决定,灵活但性能略低
  • 推荐:大多数业务使用静态委派,需要插件化扩展时使用动态

Q4:双亲委派模型能否打破?


可以,通过覆写loadClass()方法不再委派父加载器,典型场景:

  • Tomcat:为了隔离Web应用,使用自己的WebAppClassLoader
  • JDBC:通过Thread.currentThread().getContextClassLoader()绕开双亲委派

最佳实践总结

  1. 明确委派场景:当方法的全部逻辑可以由另一个对象独立完成时,考虑委派
  2. 接口驱动设计:被委派者应该面向接口编程,而非具体实现
  3. 避免过度委派:如果一个方法需要委派多个对象,考虑组合模式或职责链
  4. 性能考量:高频调用场景使用静态委派,避免动态反射
  5. 线程安全:委派器中的被委派者如果是共享资源,需考虑并发控制

最后提醒:委派不是设计模式中的一个“官方”GoF模式,但它是Java底层和主流框架(Spring、MyBatis)中最广泛使用的设计思想之一,掌握委派,等于掌握了Java生态的底层沟通语言。

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