2017年2月4日

Spring MVC with Maven

最近工作上遇到需要快速建立 web service project 來讓同仁測試的需求,想起了好久之前看過但是一直沒試的 Spring MVC。詢問了朋友的意見與上網查了相關資料之後,發覺用這個來建還真的蠻快的,而且處理 JSON 也很方便,通通幫你整合好了,是個很好的快速解決方案,因此嘗試了一下,然後在搭配 Maven 處理 jar 檔問題,摸索加建立基礎架構大概只花兩天而已吧,整個就是迅速。以下是記錄建立整個 web service project 的過程。

Maven

Apache Maven Project 官網 抓 maven,設 path,然後 maven repository 設定位置如下,可用預設不用改:

/Users/mayer/Develop/apache-maven-3.3.9/conf/settings.xml
Default: ${user.home}/.m2/repository

Spring 開發工具:

  1. 先抓 eclipse 4.6.2 j2ee
  2. 在抓 spring 開發工具 Spring Tool Suite。到 https://spring.io/tools/sts/all 抓 Update Site Archives,版本要是 4.6.2
  3. 開啟 eclipse,到 Help -> Install New Software,然後點 Add,在選 Archive,指到你剛剛下載的 spring 開發工具。然後選 Core Spring IDE、Extensions Spring IDE、Integrations Spring IDE、Resources Spring IDE,裝好之後會重啟,這樣 IDE 就完成了。

建立 Maven Project

  1. 建立 Maven Project

    1. 右鍵 New -> Maven Project,選 maven-archetype-webapp 1.0,Group Id 填 tw.com.maxkit,Artifact Id 填 ivrsample,package 會長這樣 tw.com.maxkit.ivrsample,
    2. 預設 index.jsp 會出錯,把 Server Runtime 加進 project 去就好。
    3. 修改 pom 檔案,加上會用到的 spring mvc dependency

      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>4.3.5.RELEASE</version>
      </dependency>
    4. 修改 pom 在 build 內加上 plugins,確保下次執行 Maven Update Project 時 eclipse 設定不會跑掉:

        <build>
          <finalName>ivrsample</finalName>
          <plugins>
              <plugin>
                  <groupId>org.apache.maven.plugins</groupId>
                  <artifactId>maven-compiler-plugin</artifactId>
                  <version>3.1</version>
                  <configuration>
                      <source>1.8</source>
                      <target>1.8</target>
                  </configuration>
              </plugin>
          </plugins>
        </build>
  2. Spring MVC 相關設定

    1. 在 web.xml 加上:

      <servlet>
          <servlet-name>dispatcher</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      </servlet>
      <servlet-mapping>
          <servlet-name>dispatcher</servlet-name>
          <url-pattern>/</url-pattern>
      </servlet-mapping>
    2. 增加 dispatcher-servlet.xml,把它放到路徑 ivrsample/WebContent/WEB-INF 底下。專案點右鍵 -> New -> Spring Bean Configuration File,名稱為 dispatcher-servlet.xml,xsd 選 mvc、p、context。然後加上這兩行:

      <context:component-scan base-package="tw.com.maxkit.ivrsample.controller"/>
      <mvc:annotation-driven />
    3. 這樣就可以開始寫 web service,到 tw.com.maxkit.ivrsample.controller 底下建立 FooController.java,範例如下:

      @Controller
      @RequestMapping("/foo")
      public class FooController {
      
          @ResponseBody
          @PostMapping("/test")
          public String hello(){ 
              System.out.println("into test");
              return "hello";
          }
      
          @ResponseBody
          @PostMapping(path = "/testjson", consumes = "application/json")
          public TestBean json(@RequestBody TestBean testBean) {
              System.out.println("testBean = " + testBean);
              System.out.println(testBean.getId());
              System.out.println(testBean.getName());
              System.out.println(testBean.getDate());
      
              TestBean testBean2 = new TestBean();
              testBean2.setId(2);
              testBean2.setName("james");
              testBean2.setDate(new Date());
              return testBean2;
          }
      }

      這個 class 的存取路徑會是:

      http://localhost:8080/ivrsample/foo

      有開放兩個 api:

      http://localhost:8080/ivrsample/foo/test
      http://localhost:8080/ivrsample/foo/testjson

      json 處理參考接下來的說明。

Spring MVC with Json

需要 jackson lib,在 pom 加上:

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>1.9.13</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.5.3</version>
</dependency>

然後你的 controller method 直接回傳物件就好,沒有其他設定了,如下:

@ResponseBody
@PostMapping(path = "/testjson", consumes = "application/json")
public TestBean json(@RequestBody TestBean testBean) {
    System.out.println("testBean = " + testBean);
    System.out.println(testBean.getId());
    System.out.println(testBean.getName());
    System.out.println(testBean.getDate());

    TestBean testBean2 = new TestBean();
    testBean2.setId(2);
    testBean2.setName("james");
    testBean2.setDate(new Date());
    return testBean2;
}

遇到日期時,他會回傳 timestamp,若要日期轉字串則要加上:

public class TestBean {
    private int id;
    private String name;
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date date;
    // ... get and set method
}

日期相關處理參考:http://wiki.fasterxml.com/JacksonFAQDateHandling

不讓 null 回傳,在 bean 上面加上:

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;

@JsonInclude(Include.NON_NULL)
public class ApiOut { ... }

or

import com.fasterxml.jackson.databind.annotation.JsonSerialize;

@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class ApiOut { ... }

null 回傳處理參考:http://stackoverflow.com/questions/11757487/how-to-tell-jackson-to-ignore-a-field-during-serialization-if-its-value-is-null

取 ServletContext

在你的 Spring 控管 object 內寫上這個,就會自動注入了

protected ServletContext context;

@Autowired
public void setContext(ServletContext context) {
    this.context = context;
}

另一種方式,連 get、set 都省了,上網查是用 JAVA Reflection 做到的,不過細節沒去看就是了:

@Autowired
protected ServletContext context;