SpringMVC核心

這是我根據自己的理解和網上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;

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