☰
IDEA新建Java RESTful API模板:Maven+Jersey+fastjson实战解析
2026/10/12 15:28:24 网站建设 项目流程

简介:这份PDF教程(共1个文件,约59KB)面向刚接触Java Web后端开发的初学者,完整演示在IntelliJ IDEA中从零搭建一个基于Maven的RESTful API模板。内容覆盖Maven Webapp骨架创建、Jersey容器依赖引入、web.xml中JAX-RS Servlet映射配置、源码与资源目录规范,以及通过FastJson和Jersey编写返回JSON数据的Hello接口和Student实体类。每一步都配有具体代码与配置说明,可当作项目初始化时的速查手册,帮助避开依赖引入和包扫描路径等常见坑。教程虽精炼,但足以支撑读者理清RESTful API模板的骨架结构,后续可在此基础上扩展业务代码。已有1319人学习,适合想快速建立Java Web后端项目雏形的开发者参考。

1. 复刻这套 Java Web 后端模板:一次跑通 JSON 接口

很多人搜“IDEA新建Java resfulAPI模板”的时候,其实是想从零搭一个能立刻返回 JSON 的后端抽查。我最近完整重跑了一遍这套 Java Web 后端 RESTful API 模板:Maven 骨架、Jersey 2.22.2 做 JAX-RS 路由、fastjson 做序列化,最后在浏览器敲/api/hello/get直接看到学生列表 JSON。整个过程不需要 Spring 全家桶,比较适合刚入门、想把每层配置看透的同学;也适合熟手把它当基线模板,改几个@Path就开始填业务接口。下面从头拆,版本差异和踩坑点都会交代清楚。

2. 初始化 Maven WebApp 项目:先选对构建骨架再谈接口设计

2.1 为什么选 maven-archetype-webapp 而不是手搓目录

教程原版基于 IDEA 2018.1.4,当时在Create New Project里选 Maven、勾选Create from archetype,右侧列表能直接看到org.apache.maven.archetypes:maven-archetype-webapp。这个模板自带web.xml、index.jsp和war打包方式,是 Servlet 时代启动一个 Web 项目最轻的路。

现在的 IDEA 版本迭代后,Archetype 列表默认不展示这个模板,或者加载列表要等很久。常见做法是手动添加,或者干脆用命令行生成。我一般是直接在终端里敲:

mvn archetype:generate \ -DarchetypeGroupId=org.apache.maven.archetypes \ -DarchetypeArtifactId=maven-archetype-webapp \ -DarchetypeVersion=1.4 \ -DgroupId=com.example \ -DartifactId=restapi-demo \ -DinteractiveMode=false

这条命令里的-DarchetypeGroupId和-DarchetypeArtifactId指定了 webapp 骨架的坐标;-DgroupId和-DartifactId是你自己项目的坐标;-DinteractiveMode=false表示不进入交互问答,直接按参数生成。执行完会得到一个带src/main/webapp目录的标准 war 工程,然后用 IDEA 直接Open这个目录就行。

之所以不建议完全手搓目录,是因为 webapp 骨架除了目录结构外,还带了web.xml的初始版本和打包插件配置。后面你只需要替换web.xml内容,而不是从零猜测 Servlet 容器要在哪里找入口。和 Spring Initializr 相比,这套方案没有自动配置的黑匣子,JAX-RS 的 Jersey 是用 Servlet 方式显式注册的,每一步发生了什么都看得见,适合想搞懂原理的人。

2.2 GroupId 与 ArtifactId:决定依赖坐标和扫描包根路径

GroupId定义了项目所属组织,ArtifactId是项目本身的名字。教程里用了一个很直观的类比:拿 GitHub 来说,GroupId像用户名,ArtifactId像具体仓库名。比如 fastjson 这类开源库,Maven 坐标是com.alibaba:fastjson,前面的com.alibaba就是它的组织标识,fastjson是模块名。

这个看似不起眼的坐标,在 Jersey 配置里会直接影响包扫描。后面web.xml中jersey.config.server.provider.packages的值,填的就是你的GroupId对应的包路径。如果建项目时填了com.example,那接口类就要放在com.example下的某个包里,扫描参数也指向这里。

坐标段示例值作用
GroupIdcom.example包根路径,也是 Jersey 扫描根包
ArtifactIdrestapi-demo项目名,决定 war 包名与目录名
Version1.0-SNAPSHOT版本号,本地开发保持默认
Packagingwarwebapp 骨架默认值

实际使用中,很多人喜欢把GroupId写成公司域名反写,ArtifactId写成当前项目名。这样既能避免坐标冲突,也让 provider 扫描范围足够精准,不至于把第三方依赖里的类也扫进来。

2.3 写入 pom.xml:三个依赖与编译版本缺一不可

初始 pom.xml 只有骨架信息,要跑通 RESTful API,至少补三样东西。下面是补齐后的完整依赖区,我在原模板基础上加了javax.servlet-api和编译插件,这两个不加后面会出问题。

<properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <!-- Jersey 的 Servlet 容器适配,也是 JAX-RS 实现本体 --> <dependency> <groupId>org.glassfish.jersey.containers</groupId> <artifactId>jersey-container-servlet</artifactId> <version>2.22.2</version> </dependency> <!-- servlet-api 编译期需要,运行时由 Tomcat 提供 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- JSON 序列化工具 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.21</version> </dependency> </dependencies>

这里解释一下几个选择。jersey-container-servlet是关键依赖,它包含了 Jersey 运行时和 Servlet 容器接入层,同时在启动时托管 JAX-RS 的@Path资源类。servlet-api的 scope 必须写provided,因为它在编译期需要存在,但真正运行时 Tomcat 自己也带了一份,不打进 war 包里,避免类冲突。fastjson 负责把 Java 对象变成 JSON 字符串,1.2.21 是教程原版版本,JDK 8 下跑没问题。

常见误区是只加 Jersey 不加 servlet-api,然后javax.servlet相关类全部标红;或者不写编译插件版本,Maven 默认按 JDK 1.3 编译,Java 8 语法直接报错。教程原文没提这两处,我重跑时都补上了。

提示:如果你本机是 JDK 11 或更高,Jersey 2.22.2 在 javax 命名空间下仍能编译,但建议至少升到 2.35 再配套使用,方法和注解 API 基本一致。

3. 让 Servlet 容器认识 JAX-RS:web.xml 路由配置与目录标记

3.1 ServletContainer 注册的参数含义

war 工程里,src/main/webapp/WEB-INF/web.xml是 Servlet 容器的入口配置。archetype 生成的 web.xml 非常老,连版本头都没有。直接把下面这段整体替换进去:

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <servlet> <servlet-name>JAX-RS Servlet</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>jersey.config.server.provider.packages</param-name> <param-value>com.example</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>JAX-RS Servlet</servlet-name> <url-pattern>/api/*</url-pattern> </servlet-mapping> </web-app>

核心是jersey.config.server.provider.packages这个参数,它告诉 Jersey 启动时去哪扫描带@Path注解的类。教程里写的param-value是原作者的包名,你建项目时如果GroupId填的是com.example,这里就要改成com.example。load-on-startup设为 1,表示 Tomcat 启动时立刻初始化这个 Servlet,而不是等第一次请求才加载。

<url-pattern>/api/*</url-pattern>是容器级路由:所有以/api/开头的请求都会进入 Jersey。这一层和 Jersey 内部的@Path是两套路由,很多新手分不清,后面单独讲。

3.2 /api/* 前缀背后的 URL 拼接过程

当一个请求进来时,Tomcat 会先按web.xml的servlet-mapping匹配,再把剩余路径交给 Jersey 去匹配@Path。如果项目部署的 context path 是/restapi-demo,最终的完整请求链路是这样的:

URL 片段谁在解析对应配置
http://localhost:8080/restapi-demoTomcat contextDeployment 中的 Application context
/apiweb.xml servlet-mappingurl-pattern 前缀
/helloJersey 类上 @PathHello 类的@Path("/hello")
/getJersey 方法上 @Path方法上的@Path("get")

所以浏览器里要访问的地址是http://localhost:8080/restapi-demo/api/hello/get,少写/api、少写/restapi-demo都会 404。这一点在验证阶段最容易翻车,教程里只说了改路由为/api/hello/get,前提是你的 Application context 恰好是根路径/;如果部署时填了项目名,前面还要带上 context path。

3.3 手工补目录并标记 Sources

webapp 骨架只有src/main/webapp,没有 Java 源码目录。需要在src/main下新建java和resources两个目录,一个放 Java 源码,一个放配置文件等静态资源。命令行方式是这样:

mkdir -p src/main/java mkdir -p src/main/resources

目录建好只是第一步,还得让 IDEA 知道这两个目录的角色。打开File -> Project Structure(mac 上是Cmd + ;),选中模块里的java目录,点上面的Sources标记;选中resources目录,标记为Resources。这个标记的本质是把目录加入编译路径。

这是一个很微妙的坑:如果不标Sources,你写的包和类文件就像放在普通文件夹里,Maven 编译时根本不理会它们,Tomcat 启动后也找不到Hello类。很多人的报错不是代码写错,而是目录压根没进编译根。标记之后再点 Apply,项目的目录树里java会变成蓝色,resources会变成带咖啡杯图标的资源根,看到这两个视觉变化才算配置成功。

4. 实现第一个 REST 接口:JAX-RS 注解组合与 fastjson 序列化

4.1 Hello 类怎么读

在src/main/java/com/example下新建Hello.java,这是模板里的主要接口类。教程原版返回的是学生列表,我把示例数据换成了三个虚构学生,代码逐行说明:

package com.example; import com.alibaba.fastjson.JSONObject; import javax.ws.rs.GET; // HTTP GET 动词 import javax.ws.rs.Path; // 路由路径 import javax.ws.rs.Produces; // 响应媒体类型 import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.ArrayList; import java.util.List; @Path("/hello") // 类级路由,拼接后是 /hello public class Hello { @Path("get") // 方法级路由,完整路径是 /hello/get @GET // 只接受 GET 请求 @Produces(MediaType.APPLICATION_JSON) // 响应 Content-Type 设为 application/json public Response getStudent() { List<Student> lists = new ArrayList<Student>(); lists.add(new Student("1", "alice", 23)); lists.add(new Student("2", "bob", 24)); lists.add(new Student("3", "carol", 25)); // 用 fastjson 将 List 序列化为 JSON 数组字符串 JSONObject json = new JSONObject(); return Response.status(Response.Status.OK) .entity(json.toJSONString(lists)) .build(); } }

类上的@Path("/hello")和方法上的@Path("get")组合成/hello/get。@GET限定了 HTTP 方法,@Produces(MediaType.APPLICATION_JSON)告诉浏览器和前端这个接口返回的是 JSON。Response.status(...).entity(...).build()是手动构造一个 HTTP 响应,status 指定 200,entity 放的是序列化好的 JSON 字符串。

4.2 Student 模型与 JSON 字段映射

Student类是返回数据的载体,放在和Hello同一个包下,不需要额外 import:

package com.example; public class Student { private String id; private String name; private int age; public Student(String id, String name, int age) { this.id = id; this.name = name; this.age = age; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }

fastjson 序列化时,默认读取 JavaBean 的 getter 方法生成 JSON 字段名,所以这里必须有对应的getId()、getName()、getAge()。序列化结果就是{"age":23,"id":"1","name":"alice"}这样的对象,三个对象组成一个 JSON 数组返回前端。

字段名是直接拿 getter 去掉get后首字母小写的,如果你想在接口里用别的字段名,可以加@JSONField(name = "student_name")这种注解。模板里先保持最简单形态,不加这些装饰,跑通后再按需改。

4.3 返回字符串还是返回对象的抉择

这是整个模板里最值得留意的技术点。教程的写法是先用JSONObject.toJSONString(lists)把对象列表转成字符串,再把字符串塞进Response.entity()。这样做的原因是:JAX-RS 对响应对象的序列化依赖MessageBodyWriter,而 Jersey 默认认识 Jackson 的 Provider,不认 fastjson 的序列化器。

如果你直接把lists对象放进entity(),想着反正@Produces(MediaType.APPLICATION_JSON)会自动转 JSON,请求发过来大概率会得到一个 500,日志里写着MessageBodyWriter not found。因为缺少对应的序列化 Provider,Jersey 不知道拿什么把List<Student>变成 JSON。

所以教程这个写法是一种务实的绕过策略:手动先把对象变成字符串,Jersey 只需要把字符串原样输出即可。代价是字符集需要自己控制。用原写法MediaType.APPLICATION_JSON不指定 charset 时,Tomcat 会按默认编码输出,中文字段名或中文数据容易出现乱码。更稳的写法是这样:

return Response.ok(JSONObject.toJSONString(lists), MediaType.APPLICATION_JSON_TYPE.withCharset("UTF-8")).build();

这段和原写法区别在于:一是用Response.ok()替代status(Response.Status.OK),语义更短;二是把媒体类型换成APPLICATION_JSON_TYPE.withCharset("UTF-8"),显式把编码写进响应头,中文不会再乱。

如果你后续想在接口里直接返回对象而不是 JSON 字符串,就需要引入jersey-media-json-jackson依赖,或者自己写一个 fastjson 的MessageBodyWriter注册进 Jersey。模板阶段保持字符串方式最省心,跑通业务后再升级。

5. 常见问题与排查:五个典型翻车点一次讲透

5.1 编译报错找不到 javax.ws.rs.* 包

现象:Hello.java里所有javax.ws.rs开头的 import 全部标红,项目无法编译。

原因:jersey-container-servlet依赖没进 classpath,或者依赖下载后 IDEA 没有刷新 Maven 索引。前者发生在你只写了<dependency>但没等 Maven 下载完,后者发生在 IDEA 没有开启自动导入的情况。

解决:确认pom.xml里已添加 jersey 依赖,然后打开右侧Maven Projects面板,点一下刷新按钮让依赖重新导入。如果网络慢,可以看 Maven 面板里的下载进度;依赖下载完成后再编一次。这里很容易漏的是javax.servlet-api,虽然报错集中在javax.ws.rs,但 Servlet 容器适配的部分也需要 servlet 相关类在编译期存在。

5.2 访问 /api/hello/get 返回 404

现象:Tomcat 启动了,index.jsp能正常显示 Hello World,但敲接口地址返回白页 404。

原因:三层路由都可能出问题。web.xml的url-pattern没写/api/*;或者jersey.config.server.provider.packages的值和Hello类所在的包名不一致;再或者类上方法上的@Path拼出来不是/hello/get。

解决:先看 Tomcat 启动日志,确认有没有 JAX-RS Servlet 注册成功的字样。然后逐个核对:web.xml里的<url-pattern>是不是/api/*;param-value是不是com.example这个包根路径;Hello类和Student类是不是真的放在src/main/java/com/example目录下。最后用完整 URL 访问,注意 context path,不要只在浏览器里敲/api/hello/get,如果 Application context 配了/restapi-demo,前面必须要带。

5.3 中文数据变成问号或乱码

现象:接口返回的 JSON 里,中文字段值显示为???或乱码字符。

原因:Response.entity()传入的是字符串,@Produces(MediaType.APPLICATION_JSON)没有指定 charset。Tomcat 默认按 ISO-8859-1 编码输出,中文字符超出这个字符集范围就被替换成问号。

解决:改用带字符集的媒体类型,MediaType.APPLICATION_JSON_TYPE.withCharset("UTF-8"),这样响应头的Content-Type会变成application/json;charset=UTF-8,浏览器的解码方式随之修正。如果这是你维护的老接口,还可以在web.xml里给 ServletContainer 加初始化参数统一字符集,但最直接的方式还是改Hello类的返回写法。

5.4 改了 Java 代码重启后接口没变化

现象:修改了Student字段或Hello类的返回内容,刷新页面还是旧数据。

原因:IDEA 里 Tomcat 的默认On Update Action是Update resources,它只更新静态资源,不会重新编译 Java 类。war exploded 模式下,Java 类文件的更新不会自动同步到部署目录。

解决:打开Run -> Edit Configurations,选 Tomcat Server,把On Update Action改成Redeploy,把On frame deactivation改成Update classes and resources。这样每次切出 IDEA 窗口自动触发类重编译和热加载。如果没有触发,就手动点重启按钮,别在浏览器里空刷新。

5.5 新版 IDEA 找不到 maven-archetype-webapp

现象:Create from archetype的列表里翻不到 webapp 模板,或者列表一直转圈加载不出来。

原因:IDEA 新版本的 Archetype 列表依赖内置索引和外网同步,国内网络环境下经常拉取超时。原教程基于 2018.1.4 版本,当时这个模板就在首屏,现在不在了。

解决:在 Archetype 选择列表边上点Add按钮,手动填入三件套坐标:org.apache.maven.archetypes、maven-archetype-webapp、1.4,加进去后用自定义模板创建。也可以走 2.1 节里的mvn archetype:generate命令行,把项目建好再用 IDEA 打开。这条对已经装新版 IDEA 的人特别有参考价值,相当于把原教程的入口换了一种实现方式。

6. 部署验证与后续优化:war exploded、curl 冒烟与统一 Result

6.1 Tomcat 配置与 war exploded 的意义

代码写完,在Run -> Edit Configurations里点+,选Tomcat Server -> Local,指定本地 Tomcat 目录。然后切到Deployment标签,点+选Artifact,把restapi-demo:war exploded加进去,Application context 建议填/restapi-demo或/,根据自己的习惯来。

war exploded是 IDEA 把展开的 webapp 目录直接映射给 Tomcat,省掉了先打 war 包再部署的流程。开发时改一个 class 或 JSP,配好 5.4 节的热加载后,切窗口即可生效。点右上角运行按钮,浏览器会先显示index.jsp的 Hello World,这是部署成功的第一个信号。

6.2 用 curl 验证接口状态码与响应体

浏览器看结果直观,但验证接口状态码和响应头用 curl 更准确。启动 Tomcat 后,在终端执行:

curl -i http://localhost:8080/restapi-demo/api/hello/get

-i参数会打印响应头。重点看两处:HTTP/1.1 200表示接口正常;Content-Type: application/json确认媒体类型,如果带charset=UTF-8,说明 5.3 节的编码修复已生效。响应体就是三个学生对象的 JSON 数组。

如果想要更清晰的结构化输出,有 Python 环境的话可以这样:

curl -s http://localhost:8080/restapi-demo/api/hello/get | python3 -m json.tool

-s静默模式去掉进度条,管道后面的python3 -m json.tool会把 JSON 缩进格式化,方便核对字段名和值。这一步建议每次改完接口都跑一次,比在浏览器里肉眼判断可靠得多。

6.3 后续演进:把裸列表包成统一 Result 结构

模板直接返回 List,前端拿着数组自己处理状态,这在 demo 阶段够用,真实项目通常要统一返回结构。后续可以加一个极简的Result类:

public class Result<T> { private int code; private String message; private T data; // 略:构造器、getter、setter }

然后在Hello里改成:

Result<List<Student>> result = new Result<>(0, "ok", lists); return Response.ok(JSONObject.toJSONString(result), MediaType.APPLICATION_JSON_TYPE.withCharset("UTF-8")).build();

这样接口响应变成{"code":0,"message":"ok","data":[...]}的结构,前端拿到后先判断 code 再取 data,后续业务接口也沿用同一套包装。

这次重跑模板,最大的教训是:搭这种最小 Web 工程,真正花时间的不是写接口类,而是把包扫描、字符集、路由拼接、目录标记这些“看不见的配置”一次做对。从那以后我每次新建后端项目,都会强制走一遍“先跑通一个最小 JSON 接口,再加逻辑”的流程,这个顺序帮我省掉了大量排查时间。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询