REST (Representational State Transfer) 以資源為核心,透過 HTTP 方法操作資源表現:
| HTTP 方法 | 語義 | 範例 |
|---|---|---|
| GET | 獲取資源 | GET /articles/1 |
| POST | 建立資源 | POST /articles |
| PUT | 完整更新 | PUT /articles/1 |
| PATCH | 部分更新 | PATCH /articles/1 |
| DELETE | 刪除資源 | DELETE /articles/1 |
JSR 370 (JAX-RS 2.1) 定義 Java 實作標準,Jersey 是 Oracle 維護的參考實作,功能最完整、生態最成熟。
<dependencies>
<!-- Jersey 核心 -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>3.1.5</version>
</dependency>
<!-- JSON 支援 (Jackson) -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>3.1.5</version>
</dependency>
</dependencies>
import jakarta.xml.bind.annotation.XmlRootElement;
@XmlRootElement // 支援 XML 序列化
public class Article {
private Long id;
private String title;
private String content;
private String author;
private int votes;
// Getter/Setter/建構子 略...
}
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@Path("/articles")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public class ArticleResource {
private final ConcurrentHashMap articles = new ConcurrentHashMap<>();
private final AtomicLong idGen = new AtomicLong();
@GET
public Response listArticles() {
return Response.ok(articles.values()).build();
}
@GET @Path("/{id}")
public Response getArticle(@PathParam("id") Long id) {
Article a = articles.get(id);
return a != null ? Response.ok(a).build() : Response.status(Response.Status.NOT_FOUND).build();
}
@POST
public Response createArticle(Article article) {
long id = idGen.incrementAndGet();
article.setId(id);
articles.put(id, article);
return Response.status(Response.Status.CREATED).entity(article).build();
}
@PUT @Path("/{id}")
public Response updateArticle(@PathParam("id") Long id, Article article) {
if (!articles.containsKey(id)) return Response.status(Response.Status.NOT_FOUND).build();
article.setId(id);
articles.put(id, article);
return Response.ok(article).build();
}
@DELETE @Path("/{id}")
public Response deleteArticle(@PathParam("id") Long id) {
articles.remove(id);
return Response.noContent().build();
}
}
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
@ApplicationPath("/api")
public class RestApplication extends Application {
// 掃描 @Path 註解類別,無需 web.xml
}
https://host:port/context/api/articles 即可看到 JSON 列表| 維度 | JSF (JavaServer Faces) | REST (Jersey/JAX-RS) |
|---|---|---|
| 狀態管理 | 伺服器端 HttpSession,ViewState 隱藏欄位 | 客戶端無狀態,資源 URI 自描述 |
| 輸出格式 | 僅 HTML (組件樹渲染) | JSON、XML、PDF、CSV 任意格式 |
| 前端耦合 | 緊耦合 JSF 組件庫 | 完全解耦,支援 React/Vue/Angular/行動端 |
| URL 設計 | 自動生成,難以書籤/SEO | RESTful URI,語意清晰,利於 SEO |
| 開發效率 | 組件豐富,適合傳統企業後台 | 需自行構建前端,初期投入大 |
| 擴展性 | 叢集需 Session 複製/黏性 Session | 天然無狀態,水平擴展容易 |
| 測試性 | 需容器/模擬器,整合測試複雜 | 純 HTTP 呼叫,單元/契約測試簡單 |
同一 WAR 可同時部署 JSF 與 Jersey:
web.xml 配置 FacesServlet (映射 *.xhtml) 與 Jersey ServletContainer (映射 /api/*)Jersey 以標準化、高效能、多格式支援成為 Java REST 開發首選。對於新專案,建議採用「REST 後端 + SPA 前端」架構;存量 JSF 專案可採用共存策略漸進遷移,降低風險。