Home | 簡體中文 | 繁體中文 | 雜文 | 打賞(Donations) | ITEYE 博客 | OSChina 博客 | Facebook | Linkedin | 知乎專欄 | Search | Email

第 6 章 Spring MVC

目錄

6.1. @Controller
6.1.1. @RequestMapping
6.1.1.1. @RequestMapping("/")
6.1.1.2. 映射多個URL
6.1.1.3. headers
6.1.2. @ResponseBody
6.1.2.1. 直接返回HTML
6.1.3. RequestMapping with Path Variables - @PathVariable
6.1.3.1. URL 參數傳遞
6.1.3.2. URL 傳遞 Date 類型
6.1.3.3. 處理特殊字元
6.1.3.4. @PathVariable 注意事項
6.1.4. RequestMapping with Request Parameters - @RequestParam
6.1.4.1. HTTP GET
6.1.4.2. HTTP POST
6.1.4.3. @RequestParam 傳遞特殊字元串
6.1.4.4. 傳遞日期參數
6.1.5. @ModelAttribute
6.1.6. ModelAndView
6.1.6.1. 變數傳遞
6.1.6.2. ModelMap 傳遞多個變數
6.1.6.3. redirect
6.1.6.4. ArrayList
6.1.6.5. HashMap
6.1.6.6. 傳遞對象
6.1.6.7.
6.1.7. @Scheduled
6.1.8. @CrossOrigin
6.1.9. @SessionAttributes
6.1.10. HttpServletRequest / HttpServletResponse
6.1.10.1. 用於方法參數
6.1.10.2. 注入方式
6.2. @RestController
6.2.1. 返回實體
6.2.2. JSON
6.2.3. XML
6.2.4. 兼容傳統 json 介面
6.2.5. @PageableDefault 分頁
6.3. View
6.3.1. Using Spring’s form tag library
6.3.1.1. css
6.3.1.2. cssClass
6.3.2. Thymeleaf
6.3.2.1. Maven pom.xml
6.3.2.2. Spring 配置
6.3.2.3. controller
6.3.2.4. HTML5 Template
6.3.3. FreeMarker
6.4. Properties
6.4.1. 載入*.properties檔案
6.4.2. @Value 註解
6.4.3. @PropertySource 註解
6.5. FAQ
6.5.1. o.s.web.servlet.PageNotFound
6.5.2. HTTP Status 500 - Handler processing failed; nested exception is java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config
6.5.3. 同時使用 Thymeleaf 與 JSP
6.5.4. 排除靜態內容
6.5.5. HTTP Status 406

Spring MVC 有兩種啟動模式,一種是傳統Tomcat,需要配置很多XML檔案。另一種方式是採用 Spring Boot 需要些一個Java程序,不需要寫xml檔案,這個程序會幫助你處理啟動所需的一切,並且採用嵌入方式啟動 Tomcat 或者 Jetty.

兩種方式各有優缺點,Tomcat 方式配置繁瑣,但是可以使用虛擬機,同一個IP地址使用不同域名訪問,出現不同的內容。而Spring Boot一個應用一個容器一個連接埠,比不得不通過連接埠來區分應用。

6.1. @Controller

package cn.netkiller.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class Welcome {

	@RequestMapping("/welcome")
	public ModelAndView helloWorld() {

		String message = "Helloworld!!!";
		return new ModelAndView("welcome", "message", message);
	}
}
	

6.1.1. @RequestMapping

@RequestMapping("/welcome")
		
@RequestMapping(value = "/list", method = RequestMethod.GET)
		

6.1.1.1. @RequestMapping("/")

			
package com.cf88.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/")
public class HelloController {

	@RequestMapping(value = "/{name}", method = RequestMethod.GET)
	public String getMovie(@PathVariable String name, ModelMap model) {
		model.addAttribute("name", name);
		return "hello";
	}

}		
			
			

6.1.1.2. 映射多個URL

@RequestMapping({ "/news/zh-cn", "/news/zh-tw" })
@ResponseBody
public String getNewsByPath() {
    return "Hello";
}
			

6.1.1.3. headers

			
@RequestMapping(value = "/news/json", method = GET, headers = "Accept=application/json")
@ResponseBody
public String getFoosAsJsonFromBrowser() {
    return "{...}";
}
			
			
curl -H "Accept:application/json,text/html" http://localhost:8080/spring/news/json.html
			

6.1.2. @ResponseBody

import org.springframework.web.bind.annotation.ResponseBody;
		

6.1.2.1. 直接返回HTML

			
package cn.netkiller.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class Pathinfo {

	@RequestMapping(value = "/news/shenzhen/{numericId:[\\d]+}")
	@ResponseBody
	public String getNewsWithPathVariable(@PathVariable final long numericId) {
		return "Get a specific Bar with id=" + numericId;
	}

}

			
			

6.1.3. RequestMapping with Path Variables - @PathVariable

6.1.3.1. URL 參數傳遞

需求,我們需要通過URL傳遞參數,所傳遞的值是分類ID與文章ID,例如 /news/1.html, /news/1/1.html。

			
package cn.netkiller.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class Pathinfo {
	@RequestMapping("/pathinfo/{id}")
	public ModelAndView urlTestId(@PathVariable String id) {

		return new ModelAndView("pathinfo/param", "id", id);
	}

	@RequestMapping("/pathinfo/{cid}/{id}")
	public ModelAndView urlTestId(@PathVariable String cid, @PathVariable String id) {

		ModelMap model = new ModelMap();

		model.addAttribute("cid", cid);
		model.addAttribute("id", id);

		return new ModelAndView("pathinfo/param", model);
	}
}
			
			

jsp測試檔案

			
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
${ cid } <br>
${ id } <br>
</body>
</html>
			
			

6.1.3.2. URL 傳遞 Date 類型

http://localhost:7000/history/2016-09-28%2000:00:00/

			
	@RequestMapping("/history/{datetime}")
	public String history(@PathVariable @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") Date datetime) throws Exception {

		System.out.println(datetime)

		return null;
	}			
			
			

6.1.3.3. 處理特殊字元

http://www.netkiller.cn/release/1.0.1

@RequestMapping(value = "/release/{version:[a-zA-Z0-9\\.]+}", method = RequestMethod.GET)  
public @ResponseBody  
String release(@PathVariable String version) {  
    log.debug("version: ", version);  
    return version;  
}
			

http://www.netkiller.cn/release/1.0.1/other

@RequestMapping(value="/release/{version:.+}",method=RequestMethod.GET)
public void download(HttpSession session,@PathVariable("version")String version){
	return version;
}
			

6.1.3.4. @PathVariable 注意事項

@PathVariable 參數傳統需要注意,參數中不能攜帶 “/”,斜杠會被視為目錄。

			
	@RequestMapping("/PathVariable/{code}.html")
	@ResponseBody
	public String urlTestId(@PathVariable String code) {
		return code;
	}
			
			

6.1.4. RequestMapping with Request Parameters - @RequestParam

import org.springframework.web.bind.annotation.RequestParam;
		

6.1.4.1. HTTP GET

			
	@RequestMapping("/request/param")
	@ResponseBody
	public String getBarBySimplePathWithRequestParam(@RequestParam("id") long id) {
	    return "Get a specific Bar with id=" + id;
	}
			
			

http://localhost:8080/Spring/request/param.html?id=100
			

6.1.4.2. HTTP POST

			
package cn.netkiller.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class Http {

	@RequestMapping("/http/form")
	public ModelAndView createCustomer(){
		ModelMap model = new ModelMap();

		model.addAttribute("email", "netkiller@msn.com");
		model.addAttribute("phone", "13113668890");

	    return new ModelAndView("http/form", model);
	}

	@RequestMapping(value= "/http/post", method = RequestMethod.POST)
	public ModelAndView saveCustomer(HttpServletRequest request,
	        @RequestParam(value="Email", required=false) String email,
	        @RequestParam(value="Password", required=false) String password,
	        @RequestParam(value="Phone", required=false) String phone){

		ModelMap model = new ModelMap();

		model.addAttribute("email", email);
		model.addAttribute("password", password);
		model.addAttribute("phone", phone);

	    return new ModelAndView("http/post", model);
	}

}
			
			

http/form.jsp

			
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

	<form method="POST"
		action="http://localhost:8080/Spring/http/post.html" id="Register"
		name="Register">
		Email: <input class="register" type="text" id="Email" name="Email" value="${email}" /> <br />
		Password: <input class="register" type="password" id="Password" name="Password" value="" /><br />
		Phone: <input class="register" type="text" id="Phone" name="Phone" value="${phone}" /> <br />
		<input type="submit" id="btnRegister" name="btnRegister" value="Register" style="cursor: pointer" />
	</form>

</body>
</html>
			
			

http/post.jsp

			
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	${email}<br>
	${password}	<br>
	${phone} <br>
</body>
</html>
			
			

6.1.4.3. @RequestParam 傳遞特殊字元串

URL 中 “+” 有特殊意義,表示空格。

如果 @RequestParam 傳遞參數含有空格可以這樣處理。

			
	@RequestMapping("/RequestParam")
	@ResponseBody
	public String query(@RequestParam("code") String code) {

		return code.replace(" ", "+");

	}
			
			

6.1.4.4. 傳遞日期參數

			
	@RequestMapping("/range")
	public ModelAndView range(@RequestParam("beginDate") @DateTimeFormat(pattern = "yyyy-MM-dd") Date beginDate, @RequestParam("endDate") @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate) {
		log.info("===== Begin ===== {}", beginDate);

		// 你的邏輯

		log.info("===== End ===== {}", endDate);
		return new ModelAndView("activity/index", "message", "操作成功");
	}
			
			

6.1.5. @ModelAttribute

@ModelAttribute 處理 HTML FORM POST 提交

		
package cn.netkiller.pojo;

import java.util.List;

public class Deploy {

	private String group;
	private String envionment;
	private String project;
	private List<String> arguments;
	public Deploy() {
		// TODO Auto-generated constructor stub
	}
	// Getters & Setters
}
		
		
		
	@RequestMapping(value="/deploy/post",  method = RequestMethod.POST)
	public ModelAndView post(@ModelAttribute("deploy")Deploy deploy, BindingResult result) {
		if (result.hasErrors()) {
			System.out.println(result.toString());
        }
		System.out.println(deploy.toString());
		return new ModelAndView("output").addObject("output", deploy.toString());
	}		
		
		

6.1.6. ModelAndView

6.1.6.1. 變數傳遞

	@RequestMapping("/testString")
	public ModelAndView helloWorld() {
		String message = "Helloworld!!!";
		return new ModelAndView("welcome", "message", message);
	}
			
	public ModelAndView handleRequestInternal() {

		ModelAndView mav = new ModelAndView("test");// 實例化一個VIew的ModelAndView實例
		mav.addObject("variable", "Hello World!");// 添加一個帶名的model對象
		return mav;
	}
			

6.1.6.2. ModelMap 傳遞多個變數

傳遞多個字元串

	@RequestMapping("/testModelMap")
	public ModelAndView testModelMap() {
		ModelMap model = new ModelMap();

		model.addAttribute("username", "Neo");
		model.addAttribute("password", "Netkiller");

		return new ModelAndView("test/modelmap", model);
	}
			

推薦使用ModelMap

			
	@RequestMapping("/testMapString")
	public ModelAndView testMapString() {

		Map<String,String> data = new HashMap<String,String>();
		data.put("username","Neo");
		data.put("password","Netkiller");
	    return new ModelAndView("test/modelmap",data);

	}
			
			
			
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
${username}<br>
${password}<br>
</body>
</html>
			
			

6.1.6.3. redirect

			
	@RequestMapping("/testRedirect")
	public ModelAndView testRedirect(){
		   RedirectView view = new RedirectView("testMapString.html");
		   return new ModelAndView(view);
	}
			
			

6.1.6.4. ArrayList

			
	@RequestMapping(value = "testList")
	public ModelAndView testList() {
		ModelAndView mav = new ModelAndView();
		mav.setViewName("/test/list");

		// List
		List<String> list = new ArrayList<String>();
		list.add("java");
		list.add("c++");
		list.add("oracle");
		mav.addObject("bookList", list);

		return mav;
	}
			
			
			
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
	${bookList}
	<br>

	<c:forEach items="${bookList}" var="node">
		<c:out value="${node}"></c:out><br>
	</c:forEach>

</body>
</html>
			
			

6.1.6.5. HashMap

			
	@RequestMapping("/testMap")
	public ModelAndView testMap() {
		ModelAndView mav = new ModelAndView();
		mav.setViewName("test/map"); // 返回的檔案名

		// Map
		Map<String, String> map = new HashMap<String, String>();
		map.put("Java", "http://www.netkiller.cn/java");
		map.put("PHP", "http://www.netkiller.cn/php");
		map.put("Home", "http://www.netkiller.cn");
		mav.addObject("channel", map);

		return mav;
	}
			
			
			
<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
	<c:forEach items="${channel}" var="node">
		<a href="<c:out value="${node.value}"></c:out>"><c:out value="${node.key}"></c:out></a>
        <br/>
	</c:forEach>
</body>
</html>
			
			

6.1.6.6. 傳遞對象

			
	@RequestMapping("/testObject")
	public ModelAndView testObject() {
		ModelMap model = new ModelMap();

		User user = new User("neo", "passw0rd");
		model.addAttribute("user", user);
		return new ModelAndView("test/object", model);

	}
			
			
			
package cn.netkiller;

public class User {
	public String username;
	public String password;
	public User(String username, String password){
		this.username = username;
		this.password = password;
	}
	public String getUsername(){
		return this.username;
	}
	public String getPassword(){
		return this.password;
	}
}
			
			
			
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
Username: ${user.username}<br>
Password: ${user.password}<br>
</body>
</html>
			
			

6.1.6.7. 

			
			
			

6.1.7. @Scheduled

計劃任務

		
@Component
@EnableScheduling
public class MyTask {

    @Scheduled(cron="*/3 * * * * *")
    public void myTaskMethod(){
        //do something
    }
}		
		
		

6.1.8. @CrossOrigin

	@CrossOrigin(origins = "http://localhost:9000")
    @GetMapping("/greeting")
    public Greeting greeting(@RequestParam(required=false, defaultValue="World") String name) {
        System.out.println("==== in greeting ====");
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }
		

6.1.9. @SessionAttributes

		
@Controller
@SessionAttributes("myRequestObject")
public class MyController {
  ...
}
		
		

6.1.10. HttpServletRequest / HttpServletResponse

6.1.10.1. 用於方法參數

HttpServletResponse 實例

			
package cn.netkiller.api.rest;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import api.util.MatrixToImageWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Hashtable;

@Controller
@RequestMapping("/public/QRCode")
public class QRCodeController {
    private static final Logger log = LoggerFactory.getLogger(QRCodeController.class);
	
    @RequestMapping("/create/{code}" )
    @ResponseBody
    public void create(@PathVariable String code, HttpServletResponse httpServletResponse) throws WriterException, IOException {
        log.info(code);
        if (code != null && !"".equals(code)){
            ServletOutputStream stream = null;
            try {
                String text = code; 	// 二維碼內容
                int width = 300; 		// 二維碼圖片寬度
                int height = 300; 		// 二維碼圖片高度
                String format = "gif";	// 二維碼的圖片格式

                Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
                hints.put(EncodeHintType.CHARACTER_SET, "utf-8");   // 內容所使用字符集編碼

                BitMatrix bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height, hints);
                // 生成二維碼
                stream = httpServletResponse.getOutputStream();
                MatrixToImageWriter.writeToStream(bitMatrix, format, stream);

            }catch (WriterException e) {
                e.printStackTrace();
            } finally {
                if (stream != null) {
                    stream.flush();
                    stream.close();
                }
            }
        }
    }

    @RequestMapping("show")
    @ResponseBody
    public ModelAndView show(){

        return new ModelAndView("/qrcode/qrcode");
    }
}		
			
			

6.1.10.2. 注入方式