Lite API:用 XML 声明接口,把 Java CRUD 的模板代码砍掉大半

2026-06-02 31 预计阅读时间:1 分钟
来源:oschina.net AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:9 分钟

写一个最简单的查询接口,你要建多少文件?Controller、Service、ServiceImpl、Mapper、Entity、DTO、VO……还没写一行业务逻辑,项目结构已经铺了八层。Swagger 注解再补一圈,半天过去了。真正决定接口行为的代码,可能就三五行。

Lite API 换了个思路:用 XML 声明接口行为,JFinal 做底层执行,把那些重复的分层和对象转换尽量省掉。

Java Web 到底在哪儿变重了

传统分层架构的初衷是职责分离,但实际落地时,大部分接口的逻辑就是"接收参数 → 查库 → 返回结果"。为了这个流程,你不得不:

  • 在 Controller 里手写参数校验和路由
  • 在 Service 接口和 ServiceImpl 之间做一层几乎无意义的委托
  • 在 Entity 和 DTO/VO 之间反复转换字段
  • 在 Mapper XML 里写 SQL,再在 Java 里写对应的注解或方法签名

每一层都有道理,但叠在一起,模板代码的体积远超业务代码。改一个字段,可能要同时动 Entity、DTO、VO、Mapper、Swagger 五个地方。

Lite API 的核心判断:大部分 CRUD 接口的行为是可以声明出来的,不需要逐层手写

XML 声明接口:把路由、参数、SQL 写在一处

Lite API 用 XML 文件作为接口的定义中心。一个 XML 节点描述一个接口——路由路径、入参结构、SQL 逻辑、返回字段,全部集中在一起,不再分散到多个 Java 文件。

下面是一个可运行的示例,展示如何用 Lite API 定义一个用户查询接口:

<!-- src/main/resources/api/user_query.xml -->
<api name="userQuery" path="/api/user/query" method="GET">
    <params>
        <param name="userId" type="Integer" required="true" comment="用户ID"/>
        <param name="status" type="String" required="false" comment="状态筛选"/>
    </params>

    <sql>
        SELECT id, name, email, status, created_at
        FROM t_user
        WHERE id = #userId#
        <if test="status != null">
            AND status = #status#
        </if>
    </sql>

    <result>
        <field name="id"       type="Integer" comment="用户ID"/>
        <field name="name"     type="String"  comment="姓名"/>
        <field name="email"    type="String"  comment="邮箱"/>
        <field name="status"   type="String"  comment="状态"/>
        <field name="createdAt" type="String"  comment="创建时间"/>
    </result>
</api>

关键点:

  • pathmethod 直接声明路由,不需要写 Controller 类
  • params 同时完成参数定义和校验(required="true"),省掉手写校验代码
  • sql 节点内写 SQL,#userId# 是参数占位符,<if test> 支持动态条件——类似 MyBatis 的逻辑,但不需要额外的 Mapper 文件
  • result 显式声明返回字段,框架自动做字段映射,不需要单独的 VO 类

JFinal 加载与启动配置

Lite API 基于 JFinal,启动方式遵循 JFinal 的常规模式。以下是一个最小可运行的 JFinal 配置,加载 Lite API 的 XML 路由:

// src/main/java/com/example/LiteApiConfig.java
import com.jfinal.config.JFinalConfig;
import com.jfinal.config.Routes;
import com.jfinal.core.JFinal;

public class LiteApiConfig extends JFinalConfig {

    @Override
    public void configRoute(Routes routes) {
        // Lite API 会扫描指定目录下的 XML 文件,自动注册路由
        // 无需手动添加 Controller 路由
    }

    @Override
    public void configPlugin(com.jfinal.plugin.IPlugin[] plugins) {
        // 配置数据库连接池(以 Druid 为例)
        com.jfinal.plugin.activerecord.ActiveRecordPlugin arp =
            new com.jfinal.plugin.activerecord.ActiveRecordPlugin(
                new com.jfinal.plugin.druid.DruidPlugin(
                    "jdbc:mysql://localhost:3306/mydb",
                    "root", "123456"
                )
            );
        arp.addSqlTemplate("api/user_query.xml"); // 加载 XML
        plugins[0] = arp;
    }

    @Override
    public void configConstant(com.jfinal.config.Constants me) {
        me.setDevMode(true);
    }

    // configHandler、configInterceptor 按需补充,此处省略

    public static void main(String[] args) {
        JFinal.start("src/main/webapp", 8080, "/", 5);
    }
}

启动后,访问 GET /api/user/query?userId=1 即可返回查询结果。没有 Controller 类、没有 Service 层、没有 VO 类——一个 XML 文件搞定整个接口。

动态条件与批量操作

实际业务中,条件查询和批量操作是高频场景。Lite API 的 XML 声明也能覆盖:

<!-- src/main/resources/api/user_list.xml -->
<api name="userList" path="/api/user/list" method="GET">
    <params>
        <param name="keyword"  type="String"  required="false" comment="姓名模糊搜索"/>
        <param name="status"   type="String"  required="false" comment="状态筛选"/>
        <param name="pageNo"   type="Integer" required="false" defaultValue="1"  comment="页码"/>
        <param name="pageSize" type="Integer" required="false" defaultValue="20" comment="每页条数"/>
    </params>

    <sql>
        SELECT id, name, email, status
        FROM t_user
        WHERE 1 = 1
        <if test="keyword != null">
            AND name LIKE CONCAT('%', #keyword#, '%')
        </if>
        <if test="status != null">
            AND status = #status#
        </if>
        ORDER BY id DESC
        LIMIT #pageSize# OFFSET (#pageNo# - 1) * #pageSize#
    </sql>

    <result>
        <field name="id"     type="Integer"/>
        <field name="name"   type="String"/>
        <field name="email"  type="String"/>
        <field name="status" type="String"/>
    </result>
</api>

注意 defaultValue 属性——分页参数有了默认值,前端不传也不会报错。动态条件 <if test> 让一条 SQL 覆盖多种查询组合,不再需要为每种筛选写单独的接口。

什么时候该用,什么时候不该用

Lite API 的优势场景很明确:

  • 标准 CRUD 接口:查询、新增、修改、删除,逻辑简单、重复度高
  • 报表类接口:SQL 为主,返回结构固定,几乎没有业务计算
  • 快速出活的项目:接口数量多但单个逻辑不复杂,模板代码占比高

但也有边界:

  • 复杂业务逻辑:如果接口里有大量计算、多表事务协调、外部服务调用,纯声明式 XML 会变得笨拙。此时应该退回传统分层,把复杂逻辑写在 Java 里
  • 需要细粒度拦截:权限校验、限流、日志等横切关注点,Lite API 需要配合 JFinal 的拦截器机制,不能完全靠 XML 解决
  • 团队习惯:如果团队对 MyBatis + Spring MVC 的分层已经非常熟练,切换成本需要评估

一个务实的混合策略:简单接口用 Lite API 的 XML 声明,复杂接口用传统分层写 Java。两者在同一个 JFinal 项目里可以共存——XML 路由和手动注册的 Controller 路由互不冲突。

检查清单:你的项目适合引入 Lite API 吗

在决定之前,快速过一遍:

  1. 接口中 CRUD 占比超过 60%? → 适合,模板代码削减效果明显
  2. 每个接口平均涉及的 Java 文件超过 5 个? → 适合,XML 声明能集中定义
  3. 有大量接口只是换表名、换字段,逻辑几乎一样? → 适合,可以抽象出 XML 模板复用
  4. 核心接口包含复杂事务或多服务编排? → 不适合纯 XML,保留 Java 分层
  5. 团队对 JFinal 不熟悉? → 学习成本不高,但需要一周适应期

Lite API 不是要消灭分层架构,而是把分层里最无聊的那部分用声明式替代。剩下的精力,留给真正需要思考的业务代码。


相关推荐