写一个最简单的查询接口,你要建多少文件?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>
关键点:
path和method直接声明路由,不需要写 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 吗
在决定之前,快速过一遍:
- 接口中 CRUD 占比超过 60%? → 适合,模板代码削减效果明显
- 每个接口平均涉及的 Java 文件超过 5 个? → 适合,XML 声明能集中定义
- 有大量接口只是换表名、换字段,逻辑几乎一样? → 适合,可以抽象出 XML 模板复用
- 核心接口包含复杂事务或多服务编排? → 不适合纯 XML,保留 Java 分层
- 团队对 JFinal 不熟悉? → 学习成本不高,但需要一周适应期
Lite API 不是要消灭分层架构,而是把分层里最无聊的那部分用声明式替代。剩下的精力,留给真正需要思考的业务代码。