目的

了解java8 函数式接口,了解其在企业中的实践

一、企业级 Service 层的痛点

在企业级 Spring Boot 项目中,Service 层往往承担了过多非业务职责

  • 参数校验
  • 权限控制
  • 日志记录(这边用的)
  • 异常包装
  • 耗时统计 (这边用的)
  • 重试与容错(这边用的)
  • 核心业务执行

典型代码如下:

public void doBusiness() {
    long start = System.currentTimeMillis();
    try {
        checkParam();
        checkPermission();
        doBusinessLogic();
    } catch (Exception e) {
        log.error("error", e);
        throw e;
    } finally {
        log.info("cost {} ms", System.currentTimeMillis() - start);
    }
}

你会发现,真正的业务逻辑可能只有一行,却被大量重复模板逻辑包裹。

这并不是开发能力的问题,而是Service 层缺少统一执行模型的必然结果。


二、Java 8 的革命性能力:行为参数化

Java 8 之前:

  • 方法只能接收「数据」

Java 8 之后:

  • 方法可以接收「行为」
Runnable action = () -> doBusinessLogic();
Supplier<Order> supplier = () -> queryOrder();

这意味着:

  • 可变部分 = 业务逻辑
  • 稳定部分 = 执行流程

这正是模板方法模式的理想落地形式。


三、函数式接口在企业项目中的角色

在企业 Service/Domain 层,高频使用的函数式接口非常有限:

接口 含义
Runnable 无返回值执行
Supplier<T> 返回结果执行
Consumer<T> 带参数执行
Function<T,R> 转换型执行
Predicate<T> 条件判断

它们的本质非常简单:

() -> void
() -> T
(T) -> void
(T) -> R
(T) -> boolean

简单 = 企业级可复用设计的前提


四、函数式接口 = 轻量级策略模式

传统策略模式需要:

  • 接口 + 多实现类
  • Spring 注入
  • 包结构膨胀

对于一次性或少量使用的策略,成本过高。

而函数式接口可以直接用 Lambda 即写即用:

execute("确认订单", () -> orderService.confirm(orderId));
  • 无额外类
  • 无配置成本
  • 即写即用

本质上是轻量级策略模式。


五、模板方法核心思想

模板方法的核心:

固定执行流程 + 可插入业务钩子

最简单的形式:

public final class ExecTemplate {
    public static void run(String name, Runnable action) {
        long start = System.currentTimeMillis();
        try {
            before(name);
            action.run();
            after(name);
        } catch (Exception e) {
            onError(name, e);
            throw e;
        } finally {
            finallyDo(name, System.currentTimeMillis() - start);
        }
    }

    private static void before(String name) { log.info("开始执行:{}", name); }
    private static void after(String name) { log.info("执行成功:{}", name); }
    private static void onError(String name, Exception e) { log.error("执行失败:{}", name, e); }
    private static void finallyDo(String name, long cost) { log.info("{} 耗时 {} ms", name, cost); }
}

使用方式:

ExecTemplate.run("确认订单", () -> orderService.confirm(orderId));

Service 层完全不再关注重复逻辑,只关心业务“做什么”。


六、企业级“大而全”模板方法示例(Spring Retry 集成)

启用 Spring Retry:

@EnableRetry
@Configuration
public class RetryConfig {}

模板方法:

@Slf4j
@Component
public class ExecTemplate {

    @Retryable(retryFor = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 500))
    public <T> T execute(String name, Supplier<T> supplier) {
        long start = System.currentTimeMillis();
        before(name);
        try {
            T result = supplier.get();
            after(name);
            return result;
        } catch (Exception e) {
            onError(name, e);
            throw e;
        } finally {
            finallyDo(name, System.currentTimeMillis() - start);
        }
    }

    @Retryable(retryFor = Exception.class, maxAttempts = 3)
    public void run(String name, Runnable action) {
        execute(name, () -> { action.run(); return null; });
    }

    protected void before(String name) { log.info("▶ 开始执行:{}", name); }
    protected void after(String name) { log.info("✔ 执行成功:{}", name); }
    protected void onError(String name, Exception e) { log.error("✘ 执行失败:{}", name, e); }
    protected void finallyDo(String name, long cost) { log.info("⏱ {} 耗时 {} ms", name, cost); }

    @Recover
    public <T> T recover(Exception e, String name, Supplier<T> supplier) {
        log.error("‼ {} 重试失败,进入兜底处理", name, e);
        throw e;
    }
}

七、企业项目中模板方法的典型应用场景

1️⃣ 统一 Service 执行模板

  • 统一日志、异常、耗时统计
  • Service 方法只关心“做什么”
execTemplate.execute("创建订单", () -> orderService.create(cmd));

2️⃣ 远程调用保护模板

  • RPC/HTTP/MQ 异常统一处理
  • 内置重试、熔断、降级
OrderDTO dto = execTemplate.execute("调用订单中心", () -> orderClient.query(orderId));

3️⃣ 缓存加载模板(Cache-Aside)

  • 避免重复代码
  • 自动处理缓存穿透、空值缓存
Order order = cacheTemplate.getOrLoad("order:" + id, () -> orderRepository.findById(id));

4️⃣ 幂等执行模板

  • 防止重复点击、MQ 重复消费
  • 幂等标识 + 并发控制
idempotentTemplate.execute("pay:" + orderId, () -> paymentService.pay(orderId));

5️⃣ 状态流转校验模板

  • 状态机前置校验
  • 提高业务执行安全性
stateTemplate.execute(order.getStatus(), s -> s == CREATED, () -> order.pay());

6️⃣ 事务包裹模板

  • 多步操作统一事务管理
  • 避免每个 Service 写 @Transactional
transactionTemplate.execute(() -> {
    orderRepo.save(order);
    inventoryRepo.lock(order);
    return order;
});

7️⃣ 数据权限过滤模板

  • 查询自动带用户权限
  • 避免重复拼接 SQL 或 QueryWrapper
List<Order> orders = permissionTemplate.query(() -> orderRepo.findAll());

8️⃣ 规则执行模板

  • 统一规则执行框架
  • 保证执行流程稳定
rulesTemplate.executeRules(rules, context -> apply(context));

八、为什么模板方法优于 AOP

维度 AOP 模板方法
可读性 隐式 显式
调试 困难 直观
粒度 方法级 代码块级
业务编排
重试/降级 复杂 自然

AOP 适合横切关注点
模板方法适合业务执行流程治理


九、设计收益总结

  • ✔ Service 层代码高度聚焦业务
  • ✔ 执行流程统一可治理
  • ✔ 重试、日志、监控集中管理
  • ✔ 极低学习成本
  • ✔ 易扩展,多业务场景复用

十、一句话总结

函数式接口不是 Stream 的附属品,它是企业级 Service 层模板方法模式的最佳实现载体

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐