这是我根据自己的理解和网上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;