2016年1月28日 星期四

HelloWorld (Spring3.x MVC 一)

此專案使用 xml 加 annotation 架設 SpringMVC
這次使用 gradle,創建好 gradle 後,預設不是 Web 的,使用以下方式變成 Web


※變 Web 專案

專案按右鍵 Properties


產生 Web.xml



增加 jar 檔

※build.gradle

apply plugin: 'java-library'
    
repositories {
    jcenter()
}
    
def springVer = '3.2.13.RELEASE'
    
dependencies {
    api 'org.apache.commons:commons-math3:3.6.1'
    implementation 'com.google.guava:guava:21.0'
    
    testImplementation 'junit:junit:4.12'
    compile 'org.springframework:spring-context:"${springVer}"'
    compile group: 'org.springframework', name: 'spring-webmvc', version: "${springVer}"
    // compile group: 'javax.servlet.jsp.jstl', name: 'jstl', version: '1.2'
    compile group: 'javax.servlet', name: 'jstl', version: '1.2'
}

※之後只要有改,就要在專案按右鍵做如下的設定,才會更新 build.gradle

※web.xml

<servlet>
    <servlet-name>testServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/testServlet-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
    
<servlet-mapping>
    <servlet-name>testServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

※根據官方文件的第二張圖 (Context hierarchy in Spring Web MVC) 下面,第一段有說明 [servlet-name]-servlet.xml in the WEB-INF,表示預設會在 WEB-INF 下找 [servlet-name]-servlet.xml,以此例來說就是 testServlet-servlet.xml,如果是叫這個名字且放在這個目錄下,那就可以不寫 init-param

※如果想用 annotation,本篇最下面有


※testServlet-servlet.xml

<context:component-scan base-package="controller" />




※XxxAction.java

@Controller
public class XxxAction {
    @RequestMapping("/ooo/xxx/*.mvc")
    public String hello() {
        System.out.println("hello mvc");
        return "/WEB-INF/hello.jsp";
    }
}

※只要是/ooo/xxx/開頭且是 mvc 結尾就會進來這個方法,進來後導向到另一支 jsp


※index.jsp

<a href="ooo/xxx/hello.mvc">Hello World</a>

※也可用 form,但網址是 GET,所以也可以用這個方式連後端


※@RequestMapping 

可寫在類和方法上,兩個都寫可以合併,如上例可改成下面二種方式

@Controller
@RequestMapping("/ooo")
public class XxxAction {
    @RequestMapping("/xxx/*.mvc")
    public String hello() {
        System.out.println("hello mvc");
        return "/WEB-INF/hello.jsp";
    }
}

@Controller
@RequestMapping("/ooo/xxx/")
public class XxxAction {
    @RequestMapping("*.mvc")
    public String hello() {
        System.out.println("hello mvc");
        return "/WEB-INF/hello.jsp";
    }
}


※處理 view

<bean id="jspViewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- <property name="viewClass" -->
    <!-- value="org.springframework.web.servlet.view.JstlView" /> -->
    <property name="prefix" value="/WEB-INF/" />
    <property name="suffix" value=".jsp" />
</bean>

※可在 testServlet-servlet.xml 增加這段,可參考官網

※JstlView 需要 JSTL 的 jar 包,但此例可以不加這個屬性,只要一加上,也不用寫上面那個 viewClass屬性,DispatcherServlet.resolveViewName,裡面的迴圈有個 View,會回傳 JstlView,原本是 InternalResourceView
因為 InternalResourceViewResolver 的預設建構子有寫


※上面的 gradle.build 有寫兩個,要小心註解那一個是有問題的,會出找不到 jstl 的 class

※設定好後,controller 只要回傳 hello 即可


※編譯及執行問題

編譯時找不到 Servlet,可在專案按右鍵 Build Path >> Configure Build Path >> Libraries 活頁標籤裡的 Add Library,然後如下操作



運行時出現找不到 DispatcherServlet 的 class,可如下操作

※tomcat 還要打包到指定的路徑上,這裡不選 ,jar 包就不會包到指定的地方,此時就找不到jar 檔而報錯,所以只要將 gradle 下載的 jar 選起來即可


※@RequestMapping

※value 支援三種通配符:?、*、**

※@RequestMapping(value = "*.mvc", method = RequestMethod.POST, params = { "xxx", "ooo=9" }, headers="Host=localhost:9080")

※params 表示一定要有屬性 xxx 和 ooo,且屬性 ooo一定要是 9 的,且 request header 的 Host 一定要是 localhost:9080 才可以請求,否則 404

※param 裡的都是字串,只能用「=」、「!=」,沒有正則表達式

※注意如果 params 過了,但 headers 沒通過,錯誤訊息還是顯示 params 沒通過

※瀏覽器可看到 header 的訊息,以 Chrome 為例



※@RequestParam、@RequestHeader、@CookieValue

@RequestMapping(value = "abc/*.mvc")
public String hello(
    @RequestParam(value = "id", defaultValue = "0") int i,
    @RequestParam(value = "name", required = false) String n,
    @RequestHeader(value = "Accept-Language") String alang, 
    @CookieValue("JSESSIONID") String cookie) {
    
    System.out.println(i + ":" + n + ":" + alang + ":" + cookie);
    return "hello";
}

※這三個 annotation 裡的屬性一樣,用法也差不多,也都只能寫在參數裡

※假設 hello(@RequestParam(value="id") int id),因為都叫 id,所以 annotation 可以不寫,主要是用在 defaultValue 和 required 這兩個屬性要設定時

※注意如果是基本型態,沒有 null,required 設定 false,還是會 400,可以改成 Wrapper 型態或者使用 defaultValue

※@RequestHeader 的 value 一樣是看上一張圖

※@CookieValue 的 value 看上一張圖的 Cookie

※測試
<a href="ooo/xxx/abc/bruce.mvc?id=99&name=bruce">param</a>




※@PathVariable

※只能寫在參數裡


@RequestMapping("abc/{name}")
public String pathVar(@PathVariable("name") String n) {
    System.out.println(n);
    return "hello";
}

※{name} 必需和 @PathVariable 裡的字對應



<a href="ooo/xxx/abc/bruce">param</a>
<a href="ooo/xxx/abc/bruce.mvc">param</a>

※如果 web.xml 設定附檔名對應,就寫第二種;如果全部都給 spring 處理,就寫第一種,抓到的都是 bruce



※其他方式

※mvc:view-controller

假設 controller 沒做什麼事,只是重導而已,可以用這個,就不用寫 controller 了
官網連結

※testServlet-servlet.xml

<mvc:view-controller path="ooo/xxx/view.mvc" view-name="hello" />
<mvc:annotation-driven />

※path 為請求的網址,和 @RequestMapping 不同,如果 web.xml 設定 *.mvc,在這裡一定要打才行,但 @RequestMapping 可打可不打

※view-name 會配合 InternalResourceViewResolver 設定的路徑

※annotation-driven 不寫時,ooo/xxx/view.mvc 這個請求沒有問題,但其他的,如上面有很多的其他請求會 404,加上這個才可以正常訪問

※如果這個設定和 controller 都寫了,會以 controller 為主


※重導

@RequestMapping("view")
public String takeView() {
    System.out.println("yeah");
    return "forward:/WEB-INF/hello.jsp";
    //return "redirect:/success.html";
}
------------------------------
<a href="ooo/xxx/view.mvc">view</a>

※使用重導的方式,InternalResourceViewResolver 是沒有作用的

.直接請求重導 forward
A想拿東西給B,一進門看到C,C會轉交給B,有沒有轉交成功,A都會知道
伺服器端很忙,因為何服器會去找,所以可以找到WEB-INF之下的資源,所以網址不變
使用 RequestDispatcher


.間接請求重導 redirect
A想拿東西給B,一進門看到C,C叫他自己去找B
用戶端很忙,因為是用戶端,所以找不到WEB-INF之下的資源,所以網址會變
使用 HttpServletRequest,很明顯是用戶端


※不寫 web.xml 


@WebServlet(
    urlPatterns = "*.mvc", 
    initParams = @WebInitParam(
        name = "contextConfigLocation", 
        value = "/WEB-INF/applicationContext.xml"
    )
)
public class TestDispatcherServlet extends DispatcherServlet {}

※隨便寫個類實作 DispatcherServlet,然後如上代碼,注意 @WebInitParam 寫在 @WebServlet 裡面才有用

※如果不寫  @WebInitParam,那預設的 spring 設定檔名是「套件名.類名-servlet.xml」,此例為 init.TestDispatcherServlet-servlet.xml

※又如果不想用 applicationContext,直接使用 annotation,可如下設定
@WebServlet(
    urlPatterns = "/", 
    initParams = {
        @WebInitParam(name = "contextConfigLocation", value = "controller.XxxAction"),
        @WebInitParam(name = "contextClass", value = "org.springframework.web.context.support.AnnotationConfigWebApplicationContext") })
public class TestDispatcherServlet extends DispatcherServlet {}

沒有留言:

張貼留言