這是我根據自己的理解和網上Simon丶Ma大神的總結,陸陸續續將我自己的理解寫在這裏,希望大家互相討論~~~
一、SpringMVC的幾個關鍵步驟:
1、用戶向spring發起一個請求
2、請求被DsipatcherServlet攔截,並且交給處理映射器處理
3、處理映射器(HandlerMapping)尋找對應的控制器
4、控制器(Controller)處理後,返回一個ModelAndView(模型和頁面)給前端控制器
5、前端控制器去尋找對應的ViewResolver對象解決從控制器返回的視圖
6、前端找到對應的頁面後,返回給用戶,否則拋出異常
二、SpringMVC的角色劃分:
1、控制器(Controller)
2、驗證器(Validator)
3、命令對象(Object)
4、表單對象(BeanObject)
5、模型對象(Model)
6、分發器(Adapter)
7、處理映射器(HandlerAdapter)
8、視圖解析器(ViewResolver)
三、@RequestMapping(),包含4個參數:
value:請求的路徑
method:請求的方式
params:請求的參數
headers:請求頭
四、@RequestParam(),包含3個參數:
value:參數名
required:是否必需,默認爲true
defaultValue:默認參數名,設置該參數時,自動將required設爲false。極少情況需要使用該參數,也不推薦使用該參數。
五、在JSP中使用SpringMVC標籤:需要在網頁頭部添加標籤支持:
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
1.<form>標籤:
<form:form action="${ctx}/user/save.${ext}" method="post" commandName="user">
<table>
<tr>
<td>Id:</td>
<td><form:input path ="id" /></td>
</tr>
<tr>
<td>name:</td>
<td><form:input path ="name" /></td>
</tr>
<tr>
<td>password:</td>
<td><form:input path ="password" /></td>
</tr>
<tr>
<td colspan="2"><input type ="submit" value="Save" /></td>
</tr>
</table>
</form:form>
必須要與方法中的參數名一樣:
@RequestMapping(value="/user/save",method=RequestMethod.GET)
public String forSave(@ModelAttribute User user){
return "/index";
}
2.<input>標籤:
<form:input path ="id" />解析後會變成<input id="name" name="name" type="text" value=""/>
3.checkbox標籤:
boolean : <td><form:checkbox path="receiveNewsletter"/></td>
String[]:
<td>
Quidditch: <form:checkbox path="interests" value="Quidditch"/>
Herbology: <form:checkbox path="interests" value="Herbology"/>
Defence Against the Dark Arts: <form:checkbox path="interests"
value="Defence Against the Dark Arts"/>
</td>
String :
<td>
Magic: <form:checkbox path="favouriteWord" value="Magic"/>
</td>
4.radiobutton標籤:單選按鈕
<tr>
<td>Sex:</td>
<td>Male: <form:radiobutton path="sex" value="M"/> <br/>
Female: <form:radiobutton path="sex" value="F"/> </td>
</tr>
5.password標籤:密碼框
<tr>
<td>Password:</td>
<td>
<form:password path="password" />
</td>
</tr>
6.select標籤:下拉選擇框
<tr>
<td>Skills:</td>
<td><form:select path="skills" items="${skills}" /></td>
</tr>
7.option標籤:
<form:select path="house">
<form:option value="Gryffindor"/>
<form:option value="Hufflepuff"/>
<form:option value="Ravenclaw"/>
<form:option value="Slytherin"/>
</form:select>
8.options標籤:
<form:select path="country">
<form:option value="-" label="--Please Select"/>
<form:options items="${countryList}" itemValue="code" itemLabel="name"/>
</form:select>
9.textarea標籤:
<td><form:textarea path="notes" rows="3" cols="20" /></td>
10.hidden標籤:
<form:hidden path="house" />
11.errors標籤:
<form:form>
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName" /></td>
<%-- Show errors for firstName field --%>
<td><form:errors path="firstName" /></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName" /></td>
<%-- Show errors for lastName field --%>
<td><form:errors path="lastName" /></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes" />
</td>
</tr>
</table>
</form:form>
六、SpringMVC攔截器:
自定義攔截器:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<bean class="com.my.spring.interceptor.myInterceptor">
<property name="xxx" value="xxx"/>
<property name="xxx" value="xxx" />
<property name="xxx" value="xxx" />
</bean>
</mvc:interceptor>
</mvc:interceptors>
七、SpringMVC類型轉換:
在實際操作中經常會碰到表單中的日期字符串和Javabean中的日期類型的屬性自動轉換,而springMVC默認不支持這個格式的轉換,所以必須要手動配置,自定義數據類型的綁定才能實現這個功能。
1. 直接將自定義的propertyEditor放到需要處理的java bean相同的目錄下
名稱和java Bean相同但後面帶Editor後綴。
例如需要轉換的java bean 名爲User,則在相同的包中存在UserEditor類可實現customer propertyEditor的自動註冊。
2.利用@InitBinder來註冊customer propertyEditor:
public class BaseController {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Date.class, new CustomDateEditor(true));
}
}
3.繼承 WebBindingInitializer 接口來實現全局註冊:
使用@InitBinder只能對特定的controller類生效,爲註冊一個全局的customerEditor,可以實現接口WebBindingInitializer:
public class CustomerBinding implements WebBindingInitializer {
public void initBinder(WebDataBinder binder, WebRequest request) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
}
並修改spring-servlet.xml:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean class="net.zhepu.web.customerBinding.CustomerBinding" />
</property>
</bean>
但這樣一來就無法使用mvc:annotation-driven 了。
4.使用conversion-service來註冊自定義的converter
DataBinder實現了PropertyEditorRegistry,TypeConverter這兩個interface,而在springmvc實際處理時,返回值都是returnbinder.convertIfNecessary(見HandlerMethodInvoker中的具體處理邏輯)。因此可以使用customer
conversionService來實現自定義的類型轉換:
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="net.zhepu.web.customerBinding.CustomerConverter" />
</list>
</property>
</bean>
需要修改spring-servletxml配置文件中的annotation-driven,增加屬性conversion-service指向新增的conversionService bean:
<mvc:annotation-driven validator="validator" conversion-service="conversionService" />
5.對於requestBody或httpEntity中數據的類型轉換:
SpringMVC中對於requestBody中發送的數據轉換不是通過databind來實現,而是使用HttpMessageConverter來實現具體的類型轉換。
例如,之前提到的json格式的輸入,在將json格式的輸入轉換爲具體的model的過程中,spring mvc首先找出request header中的contenttype,再遍歷當前所註冊的所有的HttpMessageConverter子類,根據子類中的canRead()方法來決定調用哪個具體的子類來實現對requestBody中的數據的解析。如果當前所註冊的httpMessageConverter中都無法解析對應contexttype類型,則拋出 HttpMediaTypeNotSupportedException (http 415錯誤)。
那麼需要如何註冊自定義的messageConverter呢,很不幸,在spring3.0.5中如果使用annotation-driven的配置方式的話,無法實現自定義的messageConverter的配置,必須老老實實的自己定義AnnotationMethodHandlerAdapter的bean定義,再設置其messageConverters以註冊自定義的 messageConverter。
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
八、JSON格式數據的輸入和輸出:
首先是依賴包:
dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-lgpl</artifactId>
<version>1.8.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.8.0</version>
</dependency>
輸入:
1.使用@RequestBody來設置輸入:
@RequestMapping("/json1")
@ResponseBody
public JsonResult testJson1(@RequestBody User u){
log.info("get json input from request body annotation");
log.info(u.getUserName());
return new JsonResult(true,"return ok");
}
2.使用HttpEntity來實現輸入綁定:
@RequestMapping("/json2")
public ResponseEntity<JsonResult> testJson2(HttpEntity<User> u){
log.info("get json input from HttpEntity annotation");
log.info(u.getBody().getUserName());
ResponseEntity<JsonResult> responseResult = new ResponseEntity<JsonResult>(new JsonResult(true,"return ok"),HttpStatus.OK);
return responseResult;
}
輸出:
1.使用@responseBody來設置輸出內容爲context body:
@RequestMapping(value="/kfc/brands/{name}", method = RequestMethod.GET)
public @ResponseBody List<Shop> getShopInJSON(@PathVariable String name) {
List<Shop> shops = new ArrayList<Shop>();
Shop shop = new Shop();
shop.setName(name);
shop.setStaffName(new String[]{"mkyong1", "mkyong2"});
shops.add(shop);
Shop shop2 = new Shop();
shop2.setName(name);
shop2.setStaffName(new String[]{"mktong1", "mktong2"});
shops.add(shop2);
return shops;
}
2.返回值設置爲ResponseEntity<?>類型,以返回context body:
@RequestMapping("/json2")
public ResponseEntity<JsonResult> testJson2(HttpEntity<User> u){
log.info("get json input from HttpEntity annotation");
log.info(u.getBody().getUserName());
ResponseEntity<JsonResult> responseResult = new ResponseEntity<JsonResult>( new JsonResult(true,"return ok"),HttpStatus.OK);
return responseResult;
}
九、SpringMVC文件上傳:Spring mvc使用jakarta的commons fileupload來支持文件上傳。
首先也是依賴包:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
在spring-servlet.xml中加入以下這段代碼:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="100000" />
</bean>
在jsp頁面中,也就是客戶端中:
<form method="post" action="${ctx}/user/upload.${ext}" enctype="multipart/form-data">
<input type="text" name="name"/>
<input type="file" name="file"/>
<input type="submit"/>
</form>
在服務器端中:
@RequestMapping(value = "/user/upload", method = RequestMethod.POST)
public String handleFormUpload(@RequestParam("name") String name,
@RequestParam("file") MultipartFile file, HttpServletRequest request)
throws IOException {
String filePath = request.getRealPath("/");
if (!file.isEmpty()) {
String fileName = file.getOriginalFilename();
System.out.println(filePath + "/" + fileName);
byte[] bytes = file.getBytes();
FileOutputStream output = new FileOutputStream(new File(filePath
+ fileName));
output.write(bytes);
output.close();
return "redirect:/success.jsp";
} else {
return "redirect:/failure.jsp";
}
}
十、SpringMVC國際化和本地化:實現三種語言可以相互切換的國際化和本地化:
1.在resources下創建3個property文件,分別爲:message_de.properties、message_en.properties、message_zh.properties
(文件的命名規則:message_語言.properties),文件的內容如下:
messages_de.properties
label.firstname=Vorname
label.lastname=Familiename
label.email=Email
label.telephone=Telefon
label.addcontact=Addieren Kontakt
label.title=spring mvc Internationalization (i18n) / Localization
messages_en.properties
label.firstname=First Name
label.lastname=Last Name
label.email=Email
label.telephone=Telephone
label.addcontact=Add Contact
label.title=spring mvc Internationalization (i18n) / Localization
messages_zh.properties(經過轉換後的中文)
label.firstname=\u59D3
label.lastname=\u540D\u5B57
label.email=\u7535\u5B50\u90AE\u4EF6
label.telephone=\u7535\u8BDD
label.addcontact=\u8054\u7CFB\u65B9\u5F0F
label.title=spring mvc \u56FD\u9645\u5316\u548C\u672C\u5730\u5316\u652F\u6301
2.spring-servlet.xml配置:爲了使用國際化信息源,SpringMVC必須實現MessageSource接口。當然框架內部有許多內置的實現類。我們需要做的是註冊一個MessageSource類型的Bean。Bean的名稱必須爲messageSource,從而方便DispatcherServlet自動檢測它。每個DispatcherServlet只能註冊一個信息源:
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
<!—session 解析區域 -->
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<!-- property name="defaultLocale" value="en"/> -->
</bean>
<!-- 修改用戶的區域需要調用區域修改攔截器LocaleChangeInterceptor。如下所設定設定paramName屬性來設定攔截請求中的特定參數(這裏是language)確定區域。既然是攔截器那就需要註冊到攔截器Bean中,這裏是註冊到了DefaultAnnotationHandlerMapping Bean中 -->
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang" />
</bean>
<!--
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="defaultLocale" value="en" />
</bean>
-->
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<ref bean="localeChangeInterceptor" />
</property>
</bean>
3.創建contact.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="ctx" value="${pageContext.request.contextPath}" />
<c:set var="ext" value="html" />
<html>
<head>
<title>Spring 3 MVC Series - Contact Manager</title>
</head>
<body>
<h3><spring:message code="label.title"/></h3>
<span style="float: right">
<a href="${ctx}/language.${ext}?local=en">英文</a>
|
<a href="${ctx}/language.${ext}?local=de">德文</a>
|
<a href="${ctx}/language.${ext}?local=zh">中文</a>
</span>
<form:form method="post" action="addContact.html" commandName="contact">
<table>
<tr>
<td><form:label path="firstname"><spring:message code="label.firstname"/></form:label></td>
<td><form:input path="firstname" /></td>
</tr>
<tr>
<td><form:label path="lastname"><spring:message code="label.lastname"/></form:label></td>
<td><form:input path="lastname" /></td>
</tr>
<tr>
<td><form:label path="lastname"><spring:message code="label.email"/></form:label></td>
<td><form:input path="email" /></td>
</tr>
<tr>
<td><form:label path="lastname"><spring:message code="label.telephone"/></form:label></td>
<td><form:input path="telephone" /></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="<spring:message code="label.addcontact"/>"/>
</td>
</tr>
</table>
</form:form>
</body>
</html>
其中<spring:message>標籤結合 ResourceBundleMessageSource 的功能,在網頁上顯示 messages.properties 中的文字訊息。
4.創建LanguageController
@Controller
public class LanguageController {
@Autowired
private SessionLocaleResolver localeResolver;
@RequestMapping("/forLanguage")
public String forLanguage(@ModelAttribute Contact contact){
return "/contact";
}
@RequestMapping(value="/language",method=RequestMethod.GET)
public ModelAndView changeLocal(@ModelAttribute Contact contact,HttpServletRequest request,@RequestParam String local,HttpServletResponse response){
if("zh".equals(local)){
localeResolver.setLocale(request, response, Locale.CHINA);
}else if("en".equals(local)) {
localeResolver.setLocale(request, response, Locale.ENGLISH);
}else if("de".equals(local)){
localeResolver.setLocale(request, response, Locale.GERMAN);
}
return new ModelAndView("/contact");
}
}
十一、使用jsr303進行驗證:
private Long id;
@Size(min = 1, message = "自定義錯誤信息:姓氏長度必須大於1")
private String firstName;
@NotNull
@Size(min = 1, message = "自定義錯誤信息:名字長度必須大於1")
private String lastName;
@Past(message = "自定義錯誤信息:出生日期必須是在現在的時間之前")
private Date birth;
private Boolean married;
@Min(value = 0, message = "孩子數量最少爲0")
@Max(value = 20, message = "孩子數量最大爲20")
private int children;
jsr303的常用註解:
表 1. Bean Validation 中內置的 constraint
注 解 功能說明
@Null 被註釋的元素必須爲 null
@NotNull 被註釋的元素必須不爲 null
@AssertTrue 被註釋的元素必須爲 true
@AssertFalse 被註釋的元素必須爲 false
@Min(value) 被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@Max(value) 被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@DecimalMin(value) 被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@DecimalMax(value) 被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@Size(max, min) 被註釋的元素的大小必須在指定的範圍內
@Digits (integer, fraction) 被註釋的元素必須是一個數字,其值必須在可接受的範圍內
@Past 被註釋的元素必須是一個過去的日期
@Future 被註釋的元素必須是一個將來的日期
@Pattern(value) 被註釋的元素必須符合指定的正則表達式
表 2. Hibernate Validator 附加的 constraint
@Email 被註釋的元素必須是電子郵箱地址
@Length 被註釋的字符串的大小必須在指定的範圍內
@NotEmpty 被註釋的字符串的必須非空
@Range 被註釋的元素必須在合適的範圍內
自定義註解:
@Age部分
public @interface Age {
String message() default "年齡填寫不正確";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@Status部分
@Constraint(validatedBy = { StatusValidator.class })
@Documented
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Status {
String message() default "狀態選擇不正確";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class StatusValidator implements ConstraintValidator<Status, Integer> {
private final int[] ALL_STATUS = { 1, 2, 3 };
public void initialize(Status arg0) {
// TODO Auto-generated method stub
}
public boolean isValid(Integer arg0, ConstraintValidatorContext arg1) {
if (Arrays.asList(ALL_STATUS).contains(arg0)) {
return true;
} else {
return false;
}
}
}
使用,在屬性上增加age、status
@Age
private int age;
@Status
private int status;