ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

IntelliJ IDEA构建RESTful API模板:Maven+Jersey+Servlet完整流程

IntelliJ IDEA构建RESTful API模板:Maven+Jersey+Servlet完整流程 简介PDF电子教程以IntelliJ IDEA 2018.1.4为操作环境全程演示从零创建一个基于Maven与Jersey的Java Web后端RESTful API模板。教程先从Maven archetype选择maven-archetype-webapp开始讲解GroupId、ArtifactId的含义随后在pom.xml中加入Jersey容器与FastJson依赖并配置web.xml中的JAX-RS Servlet使接口统一映射到/api/*路径。在此基础上教程说明如何新建java与resources目录、在IntelliJ IDEA中标记为Sources和Resources并创建com.detectivehlh.test包及Hello类通过Path/GET注解配合FastJson直接返回JSON数据还附带Student类的简单示例便于理解接口数据模型。通过阅读可以避开servlet映射或包扫描路径常见的坑项目初始化后可直接运行并返回JSON内容。压缩包内含1个PDF文件大小仅59KB已有1319人学习使用内容短小精悍适合刚接触Java Web或打算快速搭建RESTful服务骨架的开发者参考。1. 用IntelliJ IDEA新建RESTful API模板从骨架到接口的一条完整链路很多人在 IDEA 里第一次建 Java Web 后端照着网上的教程敲完最后发现两个结果要么 Tomcat 启动了但访问接口是 404要么页面还停在经典的 Hello World根本不是你写的接口。这套“IntelliJ IDEA 新建 Java Web 后端 RESTful API 模板”解决的就是这个问题——用 Maven 的 webapp 骨架建出工程引入 Jersey 容器接管/api/*的请求再用传统 Servlet 的方式把 RESTful 接口暴露出去全程不依赖 Spring Boot。适合两类人一是课程设计或毕业项目需要交付一个能演示 RESTful 接口的后端二是维护老项目、需要给既有 Servlet 加 JSON 接口的从业者。它的核心价值不在框架多新而在于把 JAX-RS 的注册链路完整走通让你知道一个接口从 URL 到 Java 方法的路径到底是怎么串起来的。2. Maven项目初始化先锁定webapp骨架再谈RESTful2.1 选型理由为什么用maven-archetype-webapp而不是Spring Boot提到新建 Java Web 后端接口第一反应往往是 Spring Boot。但很多课程设计和老项目场景里环境是 JDK 7 或 8或者老师只要求“用 Servlet 技术实现 RESTful 接口”这时候 Spring Boot 的自动配置反而成了黑匣子出了问题不好解释。maven-archetype-webapp 生成的是最标准的 Java Web 工程结构pom.xml、src/main/webapp、WEB-INF/web.xml 全都就位。它的优点有两个。第一工程结构极简没有任何多余的依赖接口通了就是你加的那几段配置起了作用逻辑链路一目了然。第二它最终打包成 war可以直接扔进 Tomcat符合 Java Web 课程设计对“部署形态”的要求。Jersey 是 JAX-RS 的参考实现把它接在 webapp 工程里只需要一个 Servlet 容器类加一段 web.xml 配置代价比引入整个 Spring 体系小得多。需要说明的是这套模板不是给大型生产项目准备的生产上你自然会更倾向于 Spring Boot 或微服务。它的定位是“教学演示、课程设计、老工程改造前的最小验证模板”。如果你只是想知道 RESTful API 的后端是怎么跑起来的用这套骨架去理解效率比直接抄 Spring Boot 高。2.2 从Create New Project到Enable Auto-Import的完整操作打开 IntelliJ IDEA版本无论 2018 还是 2024入口基本一致只是新版本界面稍微紧凑一些。第一步点击 Create New Project在左侧列表选择 Maven然后勾选 Create from archetype在右侧列表中找到org.apache.maven.archetypes:maven-archetype-webapp选中它点 next。接下来填写 GroupId 和 ArtifactId。这一步很多人随手写但后面 web.xml 的包扫描路径完全依赖它得认真想。我用一个对照来说明GroupId 相当于组织标识比如阿里的 fastjson 框架groupId 是com.alibabaartifactId 是fastjson。拿 GitHub 类比GroupId 就是你的用户名ArtifactId 就是仓库名。范例里写的是参数示例值说明GroupIdcom.detectivehlh.test组织反向域名也是资源类扫描的基础包ArtifactIdtestDemo项目名会作为本地目录和 war 包名后续两页Maven 配置、项目位置都不用动直接 next 直到完成。建完的瞬间 IDEA 右下角会弹出 “Maven projects need to be imported”这里务必选择 Enable Auto-Import。这个动作意味着之后每次修改 pom.xmlIDEA 都会自动重新拉取依赖。没勾选的话后面添加 fastjson、Jersey 依赖后还需要手动刷 Maven容易忘了导致代码大面积标红。2.3 初始化完成后先检查三个位置项目建好后先别急着写代码按下面顺序确认一遍状态能省掉后面不少排查时间。第一是看左侧 Project 面板确认出现了 src/main/webapp 和 pom.xml。第二是打开 pom.xml观察packaging标签webapp 骨架生成的是war这决定了最终部署方式。第三是看 IDEA 底部 Maven 工具窗口依赖列表里默认只有 junit 和几个插件以及 webapp 骨架自带的 servlet-api版本很老后面别跟 Jersey 依赖混淆。骨架生成的 web.xml 内容也是关键。看它的根节点web-app xmlnshttp://java.sun.com/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd version2.5这段配置声明了 Servlet 规范版本是 2.5。后面注册 Jersey 的 ServletContainer 时一定要保留这个头不要换成别的版本否则 Tomcat 启动时可能因为 schema 不匹配解析失败。初学阶段最稳妥的做法是骨架生成什么版本就保持什么版本。3. Jersey依赖与web.xml把RESTful请求交给Servlet的配置细节3.1 pom.xml引入jersey-container-servlet依赖要让 JAX-RS 的注解生效必须在 pom.xml 中引入 Jersey 的 Servlet 容器实现。打开根目录 pom.xml在dependencies标签内添加dependency groupIdorg.glassfish.jersey.containers/groupId artifactIdjersey-container-servlet/artifactId version2.22.2/version /dependency这个依赖内部会传递引入 jersey-server、jersey-common、hk2 等核心组件不需要自己一个个加。版本 2.22.2 是教程里的选择对应 JDK 7/8 比较稳。如果你本地是 JDK 8 以上也可以放到 2.28 或 2.29接口用法没差别。加了依赖后观察 IDEA 是否自动在 Maven 面板刷新如果 pom 上还有波浪线点右键 Maven - Reload Project。从依赖层级上看jersey-container-servlet 提供了org.glassfish.jersey.servlet.ServletContainer这个类它本身继承了 HttpServlet。这意味着 Jersey 是借 Servlet 标准来接收 HTTP 请求再通过内部的 JAX-RS 路由分发到你的Path类。理解这一点后面 web.xml 的配置就不会觉得玄学。3.2 web.xml里注册JAX-RS Servlet打开/src/main/webapp/WEB-INF/web.xml在web-app标签内部添加以下配置servlet servlet-nameJAX-RS Servlet/servlet-name servlet-classorg.glassfish.jersey.servlet.ServletContainer/servlet-class init-param param-namejersey.config.server.provider.packages/param-name param-valuecom.detectivehlh.test/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-nameJAX-RS Servlet/servlet-name url-pattern/api/*/url-pattern /servlet-mapping这里有几个参数非常关键后续 404 基本都出在它们身上。jersey.config.server.provider.packages的值是资源类扫描根包。Jersey Servlet 启动时会去扫描这个包下所有带有Path注解的类注册成可路由的资源。这个值必须和代码包的包名保持一致比如你的资源类在com.detectivehlh.test下那这里就填com.detectivehlh.test。你要连子包也一起扫习惯写成com.detectivehlh.test就行Jersey 默认递归扫描子包。url-pattern是/api/*代表所有以/api/开头的请求都会进入这个 Servlet。一个 Tomcat 容器里可以注册多个 Servlet分别响应不同前缀/api/*只是把接口统一收口到这一条路径上。load-on-startup设为 1表示 Tomcat 启动时就实例化并初始化这个 Servlet目的是让包扫描尽早执行。如果你在浏览器里直接访问项目根路径还是能看到 Hello World那是 webapp 骨架自带的 index.jsp 在响应和/api/*路径不冲突不用奇怪。3.3 URL路由拼接逻辑三层路径是怎么变成最终接口的很多人在这一步开始迷糊到底访问什么地址才能看到接口这其实是一个三层拼接的规则看下表就清晰了配置层级内容说明Servlet 映射/api/*第一层前缀由 web.xml 控制类级别 Path/hello第二层作用于整个资源类方法级别 Pathget第三层作用于具体方法三者拼接后得到完整接口路径/api/hello/get。访问时还要加上上下文路径也就是部署时项目的访问名。如果用 IDEA 的 Tomcat 集成配置默认上下文根是 war 包名比如 testDemo那完整地址就是http://localhost:8080/testDemo/api/hello/get。这个拼接规则是 JAX-RS 规范的核心也是排查 404 的第一手依据。无论怎么改路径最终决定权都在这三层上缺一层都不行。4. 目录标注、fastjson与第一个接口类把序列化链路打通4.1 Project Structure里标记源码目录这一步不能省webapp 骨架默认只有 src/main/webapp没有 src/main/java 和 src/main/resources。这两个目录要手动新建否则 Java 类没地方放。操作是在 src/main 目录上右键New - Directory分别创建 java 和 resources然后进入 File - Project StructuremacOS 快捷键 command ;Windows 下是 Ctrl Alt Shift S在 Modules 面板里把 java 目录标记为 Sources把 resources 目录标记为 Resources。这一步在初学时容易跳过去。不标记 Sources 的结果是IDEA 不认为这个目录是可编译源码目录你写的类的图标右下角会显示一个灰色的小标记编译时直接忽略。到时候 Tomcat 能启动但访问接口必然 404因为 Hello.class 压根不存在。标记完成后Apply 生效。resources 目录里目前可以不放东西但它以后会承担日志配置、数据库配置文件等资源提前把工程规范建好后续扩展不用再折腾目录结构。项目正式跑起来后习惯就是 Java 代码严格放 Sources 目录配置文件放 Resources 目录不会乱。4.2 编写Student类与Hello接口类在src/main/java下按 web.xml 里 param-value 的包路径逐层新建包。比如扫描根包是com.detectivehlh.test就建出一模一样的包结构。实务上我一般直接这样建右键 java 目录New - Package输入com.detectivehlh.test回车IDEA 会帮你一次性创建三层目录。然后先写 Student 类充当接口返回的数据对象package com.detectivehlh.test; 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; } }Student 类本身是个标准 POJO字段是 id、name、age。关键点在于 getter/setter 必须齐全后面 fastjson 序列化时是依赖 getter 来读取字段值的。没有 getter 的字段序列化时会静默缺失不加报错也不知道。所以写这类数据对象时我的习惯是字段定义了就把 getter/setter 一次性补全不做半截工程。然后写核心的 Hello 资源类package com.detectivehlh.test; import com.alibaba.fastjson.JSONObject; import javax.ws.rs.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) public class Hello { Path(get) GET Produces(MediaType.APPLICATION_JSON) public Response getStudent() { ListStudent lists new ArrayList(); lists.add(new Student(1, mayun, 23)); lists.add(new Student(2, mahuateng, 24)); lists.add(new Student(3, zhouhongyi, 25)); JSONObject json new JSONObject(); return Response.status(Response.Status.OK) .entity(json.toJSONString(lists)) .build(); } }这段代码的逻辑拆开来讲。Path(/hello)是类级别的路由URL 第二层Path(get)是方法级别的路由URL 第三层。GET限定只有 HTTP GET 请求才能命中这个方法如果你用 POST 去访问Jersey 会直接返回 405 Method Not Allowed这一步是 JAX-RS 的硬规则。Produces(MediaType.APPLICATION_JSON)指定响应的 Content-Type 是 application/json这样浏览器和客户端拿到响应后会按 JSON 去解析。方法内部构造了一个包含三个 Student 对象的 List然后JSONObject.toJSONString(lists)把 List 序列化成 JSON 数组字符串比如[{id:1,name:mayun,age:23}, ...]。最后Response.status(Response.Status.OK).entity(...).build()是 JAX-RS 的标准响应封装它同时设置 HTTP 状态码 200 和响应体。直接用 Response 而不是返回裸 List 的好处是能精确控制状态码和响应类型后续要加错误状态、自定义响应头只需在同一条链路上扩展。4.3 引入fastjson并理解为什么要显式序列化写到这里IDEA 会在 Hello 类的 JSONObject 上报红因为还没引入 fastjson 依赖。回到 pom.xml 添加dependency groupIdcom.alibaba/groupId artifactIdfastjson/artifactId version1.2.21/version /dependency添加后 Maven 刷新标红消失。fastjson 在这里的作用就是把 Java 对象转成 JSON 字符串。教程里特意用json.toJSONString(lists)这种写法而不是依赖 JAX-RS 自动序列化这在实际项目里是常见的手动控制风格——框架不负责序列化时你显式调用工具方法反而对输出的内容有完全的掌控力。不过要提一句fastjson 1.2.21 是很老的版本历史上部分版本出过安全公告。如果这只是课程设计无所谓如果以后进了生产环境要么升级到 1.2.83 以上要么整体换成 Jackson 或 Gson。模板的作用是把链路跑通具体序列化工具可以按团队规范替换不影响 Jersey 的整体路由设计。5. 避坑手册404、不生效与社区版Tomcat的排查顺序5.1 接口404先看包扫描路径再看URL拼写现象Tomcat 正常启动浏览器访问http://localhost:8080/testDemo/api/hello/get返回 404页面甚至还能看到 Hello World。原因404 在这个模板里有三个常见来源。一是 web.xml 里jersey.config.server.provider.packages的值与 Hello 所在的包不一致Jersey 扫不到资源。二是访问 URL 里的上下文路径不对比如部署名不是 testDemo。三是 Servlet 映射的/api/*与 Path 拼接后不匹配。解决按顺序排查。先打开 web.xml把 param-value 的值和 Hello.java 的 package 声明逐字符比对一个字母都不能差。再 Run - Edit Configurations 看 Deployment 里的 Application context它决定了部署名。最后在浏览器直接访问根路径确认 Tomcat 已启动再逐步加api/hello/get这段路径走到哪一层 404 就停在哪一层问题立刻暴露。5.2 加了依赖类还是标红Maven刷新不到位现象pom.xml 里已经写了 fastjson 和 Jersey 依赖代码里 import 依然全部标红或者只有部分类能识别。原因创建项目时右下角弹出的 Enable Auto-Import 没有点或者点了之后因为网络问题依赖没有下载完整。解决打开 IDEA 右侧 Maven 工具窗口点击刷新按钮Reload All Maven Projects强制重读 pom.xml 并重新拉取依赖。如果本地仓库缺包File - Settings - Build Tools - Maven里看本地仓库路径必要时删除~/.m2/repository下对应目录再刷新。这一步是治本的手段依赖问题八成出在本地仓库不完整而不是 IDEA 坏了。5.3 IDEA社区版没有Tomcat Server用Maven插件代替现象打开 Run - Edit Configurations左侧加号里找不到 Tomcat Server 选项。原因Tomcat Server 集成是 IntelliJ IDEA Ultimate 版的功能社区版里没有这个 Run 类型。解决社区版用户不用换 IDE在 pom.xml 里加 tomcat7-maven-plugin 就能启动plugin groupIdorg.apache.tomcat.maven/groupId artifactIdtomcat7-maven-plugin/artifactId version2.2/version configuration port8080/port path/testDemo/path /configuration /plugin然后在 IDEA 右侧 Maven 面板展开 Plugins - tomcat7双击 tomcat7:run项目就起来了。访问路径由path控制比如这里配的是/testDemo访问http://localhost:8080/testDemo/api/hello/get。注意 tomcat7 插件内部的容器是 Tomcat 7对应 Servlet 3.0 规范跑这套 Jersey 2.x 模板没有压力。5.4 返回的JSON中文乱码现象接口通了但返回的 name 字段是乱码类似ä½ å¥½这种。原因fastjson 序列化时输出的是 Unicode 字符串响应时 Content-Type 里没有 charset 声明Tomcat 默认按 ISO-8859-1 编码发送中文前端拿到的字节流就乱了。解决给响应显式指定编码。把 Hello 类里的返回语句改成return Response.ok(json.toJSONString(lists), MediaType.APPLICATION_JSON_TYPE).build();.entity()换成Response.ok(entity, mediaType)的写法MediaType.APPLICATION_JSON_TYPE 会带上 UTF-8 charset。如果还不行在 Spring 风格的容器里再配置 CharacterEncodingFilter但在 Jersey 这个模板里上面这一行基本就治好了。5.5 改了代码后运行结果没变部署的war没更新现象改了 Hello 类的返回内容重启 Tomcat浏览器里还是旧数据。原因Deployment 里选的是testDemo:warIDEA 打包成 war 再部署改了代码需要重新 build 才会生成新的 war没 build 自然跑的还是旧包。解决我把这个坑的经验写死成一条固定动作在 Run - Edit Configurations 的 Deployment 标签里把部署项改成testDemo:war exploded。这个类型是直接加载项目目录下的编译产物改完代码点一下构建Tomcat 下次重启就是最新内容。开发阶段用 war exploded发布时才用 war这条规则不用怀疑。6. Tomcat部署与接口验证从war exploded到curl的一条龙技巧6.1 配置Tomcat Server与DeploymentIDEA Ultimate 版用户走到最后一步点击顶部 Run - Edit Configurations左侧加号选择 Tomcat Server - local。这里首先要把 Application server 指向本机 Tomcat 的安装目录Tomcat 8.x 或 9.x 都能跑这套模板Jersey 2.22.2 对版本没有挑剔要求。配置好 Server 后切到旁边的 Deployment 标签点加号选择 Artifact弹窗里会列出两种格式testDemo:war和testDemo:war exploded。选war exploded然后 Apply。这时候在 Server 标签页会看到 Applications context 自动填了/testDemo/这是上下文根可以按需改但要和访问 URL 对上。这里说明一下两种类型的具体差别war exploded 部署时把编译好的 classes 目录、web.xml、jsp 等资源直接暴露给 Tomcat启动速度快也不是打压缩包的过程war 则是先打出整包Tomcat 再解压运行多一道打包过程。开发调试阶段用 exploded 能少等几十秒钟。6.2 浏览器、curl与接口验证的完整路径点右上角绿色运行按钮Tomcat 启动IDEA 会自动打开默认浏览器显示欢迎页。现在手动把地址改到/testDemo/api/hello/get就能看到返回的 JSON 数组。但是这个验证方式有个不严谨的地方——浏览器地址栏回车走的是 GET 请求而接口实际可能限制方法类型所以更精准的验证方式是用 curlcurl http://localhost:8080/testDemo/api/hello/get输出应该是一段数组字符串格式类似[{age:23,id:1,name:mayun},{age:24,id:2,name:mahuateng},{age:25,id:3,name:zhouhongyi}]。看到这个说明从路由到序列化的整条链路都通了。要注意 key 的排序不按字段声明顺序fastjson 默认输出顺序是按 getter 扫描来的这是正常现象不影响客户端解析。如果需要更直观的调试体验装一个 Postman 或直接用 IDEA 自带的 HTTP Client 新建.http文件写上 GET 请求地址点运行看响应。这个习惯比每次在浏览器里手动敲 URL 高效还能保存请求记录供后续回归验证。6.3 模板就绪后的三个扩展方向模板跑通之后这套工程本身可以继续生长。第一个方向是加 POST 接口在 Hello 类里再写一个方法把GET换成POST方法参数加RequestBody或用Consumes(MediaType.APPLICATION_JSON)就能接收前端传来的 JSON 数据。第二个方向是加子包拆分比如com.detectivehlh.test.controller放资源类、com.detectivehlh.test.model放 POJOweb.xml 的 param-value 改成com.detectivehlh.test即可扫描所有子包。第三个方向是把 fastjson 换成 Jackson移除 fastjson 依赖后在 Jersey 里注册 JacksonFeatures返回类型直接写 POJO让 Jersey 自动序列化。我搭这套模板的经验是多花五分钟把验证动作固定下来后面半个月都不会踩翻车的坑。第一次搭完我在浏览器里反复访问/api/hello/get每次 404 都怀疑是 Jersey 没注册折腾了一个下午才发现是 web.xml 的包名少打了个字母。从那以后我每次建类似的 RESTful 模板都强制走一遍固定检查先看 Maven 刷新完没再比对 param-value 和 package 声明最后启动前看一眼 Deployment 里是不是 war exploded。这套流程跑熟了从建项目到看到 JSON 输出十五分钟之内能完成中间不会再被玄学问题拦住。希望帮到你。本文还有配套的精品资源点击获取
返回列表