SpringBoot2.x系列教程38--整合JAX-RS之利用Jersey框架實現RESTful

一. JAX-RS與Jersey簡介

1. JAX-RS簡介

在Java EE 6 中引入了對 JSR-311 的支持。JSR-311(也就是JAX-RS:Java API for RESTful Web Services)旨在定義一個統一的規範,它的核心概念是resource,也就是面向資源。JAX-RS使得 Java 程序員可以使用一套固定的接口來開發 REST 應用,避免了依賴於第三方框架。同時,JAX-RS 使用 POJO 編程模型和基於註解的配置,並集成了 JAXB,從而可以有效縮短 REST 應用的開發週期。

JAX-RS 定義的 API 位於 javax.ws.rs 包中,其中一些主要的接口、註解和抽象類如下圖所示。

javax.ws.rs 包概況:

可以把 JAX-RS 理解爲是一套開發協議,該協議具體的實現由第三方來完成,例如 Sun 的實現 Jersey框架、Apache 的 CXF框架 以及 JBoss 的 RESTEasy框架。

其中Jersey,RESTEasy這兩個框架創建的應用,可以很方便地部署到Servlet 容器中,比如Tomcat,JBoss等。

2. Jersey框架簡介

Jersey是對JAX-RS(JSR311)協議的實現,用於構建RESTful Web Service,可以進一步地簡化 RESTful service 和 client 的開發。

也就是說Jersey是一個RESTful框架,與SpringMVC框架類似,但是使用上面和SpringMVC又有不同。此外Jersey還提供一些額外的API和擴展機制,所以我們可以按照自己的需要對Jersey進行擴展。

Jersey的一大特點就是,基於Jersey的REST應用,可以運行在Servlet環境下面,也可以脫離該環境。

3. Jersey常用註解

@Path

@Path註解的值是一個相對的URI路徑。@Path的有沒有/開頭是一樣的,同理,結尾有沒有包含/也是一樣的。

請求類註解

@GET, @PUT, @POST, @DELETE, … (HTTP Methods)
@GET, @PUT, @POST, @DELETE, @HEAD這些註解稱爲resource method designator,與HTTP規範中定義的方法一致。這些方法決定資源的行爲。

@Produce

@Produce註解指定返回給客戶端的MIME媒體類型。可以用於註解類或者註解方法。如果類中的方法沒有指定,則默認使用類級別的@Produce值。@Produce註解可以指定多個值,同時可以指定quality factor:

@Produces({"application/xml; qs=0.9", "application/json"})

@Consumes

該註解用於指定可以接受的客戶端請求的MIME媒體類型:

@POST
@Consumes("text/plain")
public void postClichedMessage(String message) {
    // Store the message

參數註解(@*Param)

參數註解用於從請求中提取參數,例如上面的@PathParam用於提取路徑中的參數。

@QueryParam

@QueryParam註解用於提取查詢參數。

@MatrixParam

從url片段中提取參數,即url中冒號後面的參數。

@HeaderParam

從請求的頭部提取Header。

@CookieParam

提取cookie。

@FormParam

用於提取請求中媒體類型爲”application/x-www-form-urlencoded” 的參數,根據相應的表單類型提取其中的參數。

@BeanParam

該註解用於從請求的各部分中提取參數,並注入到對應的Bean中。

@Context的使用

Context註解一般用於獲取request或者response相關的上下文,例如UriInfo。

三. Jersey框架實現RESTful

1. 創建web項目(略)

我們首先創建一個web項目,並將該項目改造成一個Spring boot項目,具體依賴包請參考之前的章節,此處略過!

2. 引入jersey的依賴包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jersey</artifactId>
</dependency>

3. 創建service接口

3.1 創建IHelloService接口類:

package com.yyg.boot.service;

/**
 * @Description Description
 * @Author 一一哥Sun
 * @Date Created in 2020/3/28
 */
public interface IHelloService {

    void sayHi(String msg);

}

3.2 創建HelloServiceImpl實現類:

package com.yyg.boot.service.impl;

import com.yyg.boot.service.IHelloService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * @Description Description
 * @Author 一一哥Sun
 * @Date Created in 2020/3/28
 */
@Slf4j
@Service
public class HelloService implements IHelloService {

    @Override
    public void sayHi(String msg) {
        log.warn("展示信息:" + msg);
    }

}

4. 創建Resource資源類

package com.yyg.boot.web;

import com.yyg.boot.service.IHelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.awt.*;

/**
 * @Description Description
 * @Author 一一哥Sun
 * @Date Created in 2020/3/28
 */
@Component
@Path("hello")
public class HelloResource {

    @Autowired
    private IHelloService helloService;

    /**
     * @Produces(value =MediaType.APPLICATION_JSON):設置輸出內容爲json格式,且可以解決中文亂碼問題;
     * @Path("sayHi"):設置資源的請求路徑;
     * @GET:設置請求方式爲get請求.
     */
    @Produces(value =MediaType.APPLICATION_JSON)
    @Path("sayHi")
    @GET
    public String sayHi(@QueryParam("msg") String msg) {
        this.helloService.sayHi(msg);
        return "success--->"+msg;
    }

}

5. 對Jersey進行配置

Springboot中對Jersey的配置有三種方式:

  • 第一種方式創建一個自定義的ResourceConfig;
  • 第二種方式,返回一個ResourceConfig類型的@Bean;
  • 第三種方式,配置一組ResourceConfigCustomizer對象。

我們這裏以第一種配置方式來講解如何實現配置。

package com.yyg.boot.config;

import com.yyg.boot.web.HelloResource;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.context.annotation.Configuration;

import javax.ws.rs.ApplicationPath;

/**
 * @Description Description
 * @Author 一一哥Sun
 * @Date Created in 2020/3/28
 * @ApplicationPath("shop")資源根路徑。
 */
@Configuration
@ApplicationPath("shop")
public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        register(HelloResource.class);
    }

}

Springboot默認把Jersey的根路徑映射在/*上;如果要更改默認的根路徑設置,對於自定義的ResourceConfig方式來說,可以在類上面添加一個@ApplicationPath註解即可。

我們也可以在application.properties中添加配置來改變項目的根路徑:

spring.jersey.application-path=shop

另外,Spring Boot建議在使用ResourceConfig添加資源類的時候,不要使用ResourceConfig類的packages方法去自動掃描,建議還是手動添加。

官方的解釋爲:

Jersey’s support for scanning executable archives is rather limited. For example, 
it cannot scan for endpoints in a package found in WEB-INF/classes when running an executable war file. 
To avoid this limitation, the packages method should not be used and endpoints should be registered individually using the register method

也就是使用Jersey的packages是比較有侷限的,比如在應用運行在war包中的時候,就不能掃描到其中的包。所以建議單獨的爲每一個資源類獨立使用register方法註冊。

6. 創建入口類

package com.yyg.boot;

import com.yyg.boot.service.impl.HelloService;
import com.yyg.boot.web.HelloResource;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

/**
 * @Description Description
 * @Author 一一哥Sun
 * @Date Created in 2020/3/28
 */
@SpringBootApplication
public class JerseyApplication {

    /**
     * 第二種方式,使用@Bean創建一個ResourceConfig類實例
     */
//    @Bean
//    public ResourceConfig resourceConfig() {
//        ResourceConfig config = new ResourceConfig();
//        config.register(HelloResource.class);
//        return config;
//    }

    public static void main(String[] args){
        SpringApplication.run(JerseyApplication.class,args);
    }

}

7. 項目結構

8. 啓動項目,測試接口

我們在瀏覽器中輸入地址:
http://localhost:8080/shop/hello/sayHi?msg=%E4%B8%80%E4%B8%80%E5%93%A5
如果出現如下內容,說明我們的jersey與Spring Boot成功的實現了整合。

9. 其他細節

9.1 Jersey註冊方式

Jersey和Springboot的集成有兩種方式,一種是使用Filter的方式註冊,一種是使用Servlet的方式註冊,默認使用的是Servlet的方式,也可以通過spring.jersey.type=filter或者通過spring.jersey.type=servlet來控制。

spring.jersey.type=servlet

9.2 更改延遲啓動

如果使用Servlet的方式啓動,默認是使用的延遲啓動。

jerseyServletRegistration方法的代碼就可以看出來:

registration.setName(getServletRegistrationName());
    registration.setLoadOnStartup(this.jersey.getServlet().getLoadOnStartup());
return registration;

第二句代碼setLoadOnStartup方法,調用的是this.jersey.getServlet().getLoadOnStartup(),而這個地方的jersey就是JerseyProperties對象:

@ConfigurationProperties(prefix = "spring.jersey")
public class JerseyProperties {...}

其中servlet的類代碼爲:

public static class Servlet {

    /**
     * Load on startup priority of the Jersey servlet.
     */
    private int loadOnStartup = -1;

    public int getLoadOnStartup() {
        return this.loadOnStartup;
    }

    public void setLoadOnStartup(int loadOnStartup) {
        this.loadOnStartup = loadOnStartup;
    }

}

可以看到,默認值爲-1。

那麼我們只需要在application.properties中配置spring.jersey.servlet.loadOnStartup=1即可立即讓Jersey的Servlet實例化。

application.properties中配置spring.jersey.servlet.loadOnStartup=1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章