手动配置
web.xml下
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<servlet>
<servlet-name>springDispatcherServletservlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
servlet-class>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springDispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
开启包扫描
装配org.springframework.web.servlet.view.InternalResourceViewResolver
<context:component-scan base-package="com.nond.springmvc">context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="suffix" value=".jsp">property>
<property name="prefix" value="/WEB-INF/pages/">property>
bean>
@Controller
public class HelloWorld {
//说明应答http://localhost:8080/projectName/HelloWorld这个请求
@RequestMapping("/HelloWorld")
public String helloWorld() {
System.out.println("HelloWorld Call");
return "success";
}
}
在SpringMVC中RequestMapping注解的作用是用来声明,当前控制器是用来处理哪些请求的
该注解能够标注在类、方法上,标注在类上提供初步的映射信息,标注在方法上,提供进一步的映射信息
method属性
映射对应的请求方法
params属性
限定请求参数
如!param1
,请求不包含param1
param1 != value1
,请求的param1参数值不为value1
headers属性
同params属性相类似,用于限定请求的请求头
@RequestMapping的value属性支持的三种匹配符
通过PathVariable注解能够将URL中占位符参数绑定到控制器方法的入参当中
如
@RequestMapping(value = "/xxx/{name}")
public String func01(@PathVariable("name")String name){
System.out.println(name);
return "success";
}
表单
<form action="xxx" method="post">
<input type="hidden" name="_method" value="PUT"/>
<input type="submit" value="Put"/>
form>
web.xml当中配置filter
<filter>
<filter-name>HiddenHttpMethodFilterfilter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilterfilter-class>
filter>
<filter-mapping>
<fitler-name>HiddenHttpMethodFilterfitler-name>
<url-pattern>/*url-pattern>
filter-mapping>
获取请求参数化为入参
@RequestMapping("/test")
public String func(@RequestParam(value="参数名称",required="布朗值,参数是否为必须" defalutValue="该参数为提供的情况下默认值为null,未提供参数时的默认值"))
获取请求头为入参
@RequestMapping("/test")
public String func(@RequestHeader(value="参数名称",required="布朗值,参数是否为必须" defalutValue="该参数为提供的情况下默认值为null,未提供参数时的默认值"))
获取Cookie值为入参
value属性为Cookie的名称
MVC能够通过请求参数以及控制器的入参自动的创建对象
如参数username,password,address.province,address.city
入参user
还能够一Servlet API作为入参,Spring mvc将会自动的将对应对象传入
支持的ServletAPI
使用ModelAndView需要指定视图对象,即已经包含了视图信息在内
@RequestMapping("/testModelAndview")
public ModelAndView testModelAndView(){
//传入视图名称创建ModelAndView对象
ModelAndview modelAndView = new ModelAndView("success");
//插入对象键值对
modelAndView.addObject("name","Hari");
}
//在视图当中获取对象,success.jsp
${
requestScope.name}
@RequestMapping("/testMap")
public String testMap(Map<String, Object> models) {
models.put("name", "Hari");
return "success";
}
使用@SessionAttributes标注类
@SessionAttributes有两个属性
value:指定插入到session当中的键
type:指定插入到session当中的数据类型
@SessionAttributes(value={
"user"})
public class TestSessionAttribute
@RequestMapping("/testSessionAttribute")
public String testSessionAttibute(Map<String,Object> map) {
User u = new User();
u.setUsername("hari");
u.setPassword("hhh123");
map.put("user", u);
return "success";
}
接受部分Bean属性,保留敏感信息,确敏感信息的安全。
如,修改用户名和email,保留原本的登录密码
注意:@ModelAttribute注解标记的方法,在控制器的每一个方法执行前都会调用一次
表单
<form action="/spring-mvc/testModelAttribute" method="post">
oldName:
<input type="text" name="oldName"/>
<br/>
newName:
<input type="text" name="username"/>
br>
<input type="submit"/>
form>
控制器
@ModelAttribute
public String getUser(@RequestParam(value="oldName",required=false,defaultValue="")String
name,Map<String,Object> m){
if(!"".equalss(oldName)){
m.put("user",UserDao.queryUser(oldName));
System.out.println("Query by old username...");
}
}
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@RequestParam(value="oldName",User user){
UserDao.UpdateByUserName(oldName,user);
System.out.println("Update..."+user);
}
页面发出请求,@ModelAttribute标注的方法先拦截到ServletRequest对象
@ModelAttribute注解标注的getUser方法根据请求参数oldName查询到User对象并且通过Map插入到请求
对象当中,键名为"user"
此时请求对象当中有
“oldName”: “oldname”
“username”:“newname”
“user”:[“username”:“oldname”,“password”:“password”]
请求对象的目标方法为@RequestMapping(“testModelAttibute”)标注的方法
springmvc将请求对象中的属性,按照属性名赋值给请求对象当中的"user"对象
此时请求对象
“oldName”: “oldname”
“username”:“newname”
“user”:[“username”:“newname”,“password”:“password”]
最后,springmvc再把键名为"user"的对象传给入参User user
注意:默认情况下,控制器目标方法的入参类型为User,入参对象接受对象的键名就为"user",即
入参数据类型的首字母小写。
如果,需要指定接口对象的键名,给入参对象使用@ModelAttribute标注,value值指定接收对象
的键名
public String testModelAttribute(@RequestParam(value="oldName",@ModelAtrribute
("user") User user){
...
}
首先需要知道无论控制器目标方法返回的是String,ModelAndView,ModelMap,springmvc都会把他们转换成ModelAndView在ModelAndView对象当中包含了Model和View
常见View的实现如下
然后ModelAndView移交给视图解析器进行解析
常用的视图解析器实现如下
<mvc:view-controller path="/targetPath" view-name="success"/>
<mvc:annotation-driven>mvc:annotation-driven>
自定义一个视图,输出当前请求的所有请求参数
创建CustomView
public class CustomView implements View {
//视图的数据类型
public String getContentType() {
return "text/html";
}
//解析视图
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
throws Exception {
Writer writer = response.getWriter();
if(model.isEmpty())
writer.append("Model map empty.");
else
for(Entry<String, ?> e:model.entrySet())
writer.append("Model key:"+e.getKey()+"|Model value:"+e.getValue()+"
");
}
}
配置一个BeanNameViewResolver到上下文
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="100">property>
bean>
BeanNameViewResolver视图解析器是根据bean的名称来
访问到自己的视图
@RequestMapping("testCustomView")
public Object testCustomView(HttpServletRequest request,Map<String,Object> m) {
Enumeration<String> parameterNames = request.getParameterNames();
while(parameterNames.hasMoreElements()) {
String name = parameterNames.nextElement();
m.put(name, request.getParameter(name));
}
return new CustomView();
}
页面发出请求->ServletRequest对象
ServletRequest对象、控制器目标方法的入参对象以及入参对象的名称(默认没有@ModelAttribute指定的情况下为对象类名的首字母小写)交给WebDataBinderFactory
WebDataBinderFactory创建DataBinder对象
DataBinder根据上下文当中的
然后根据处理结果生成BindingData对象
最后在BindingResult中抽取出入参对象和错误对象赋值给入参对象
Spring支持三种数据转换器接口,如下
@Component("CustomConverter")
public class CustomConverter implements Converter<String, Person> {
//接受一行字符串,解析字符串为String属性name和age
public Person convert(String source) {
String[] strs = source.split(":");
Person person = new Person();
person.setName(strs[0]);
person.setAge(Integer.parseInt(strs[1]));
return person;
}
}
要使用到自定义的类型转换器,除了实现相应接口以外,还需要
配置一个ConversionServiceFactoryBean到容器中
<bean class="org.springframework.context.support.ConversionServiceFactoryBean" name="ConversionService">
<!--属性接收一个 Set,Set的元素为类型转换器Bean
name="converters">
<set>
<ref bean="CustomConverter">ref>
set>
property>
bean>
在BeanConfig中配置annotation-driven标签,指定ConversionService
<mvc:annotation-driven conversion-service="ConversionService">mvc:annotation-driven>
将DispatcherServlet映射路径配置为/,spring mvc将会捕捉web容器当中所有的请求(包括静态资源请求),在这种情况下静态资源的请求如果没有对应的Controller进行处理的话,项目将会出现异常。Spring mvc针对这种情况给出了下面这种解决方案
在BeanConfig中配置
标签
配置该标签后,会在Spring mvc上下文中注册一个DefaultServletHttpRequestHandler组件,该组件会检查DispatcherServlet拦截的请求,如果是静态资源请求则交于web应用服务器进行处理,否则继续给DispatcherServlet处理
首先有两个应用情景:
配置了
标签或
标签
配置了前者之后,Spring mvc无法判断页面的请求是不经过Handler直接转发还是通过Handler处理,导致了@RequestMapping映射的路径无法正确访问。
配置了后者之后,Spring mvc无法判断页面的请求是静态资源请求还是mvc的注解,又导致了@RequestMapping映射路径无法正确访问
但是加上了mvc:annotation-driven标签之后,前面的问题就解决了,为什么?
使用了mvc:annotation-driven后,Spring mvc默认会帮我们注册默认处理请求,参数和返回值的类,其中最主要的两个类,它们分别为HandlerMapping的实现类和HandlerAdapter的实现类
DefaultAnnotationHandlerMapping
实现类RequestMappingHandlerMapping,它会处理@RequestMapping 注解,并将其注册到请求映射表中。
AnnotationMethodHandlerAdapter
实现类RequestMappingHandlerAdapter,则是处理请求的适配器,确定调用哪个类的哪个方法,并且构造方法参数,返回值
当配置了mvc:annotation-driven后,Spring就知道了我们启用注解驱动。然后Spring通过context:component-scan标签的配置,会自动为我们将扫描到的@Component,@Controller,@Service,@Repository等注解标记的组件注册到工厂中,来处理我们的请求
除此之外,配置了mvc:annotation-driven后还提供一下支持
InitBinder注解标注的方法可以对WebDataBinder对象进行初始化,其中WebDataBinder对象用于完成表单请求参数到JavaBean的属性绑定
通过WebDataBinder对象可以对数据绑定 的过程进行控制
InitBinder标注方法返回值为void
入参一般为WebDataBinder
@InitBinder
public void InitBinder(WebDataBinder binder){
...
}
在一些情况下,传入的数据需要进行格式化,比如说金额、日期等。传递的日期格式为yyyy-MM-dd或者yyyy-MM-dd hh:ss:mm,这些是需要格式化的,对于金额也是如此,比如1万元人民币,在正式场合往往要写作¥10 000.00,这些都要求把字符串按照一定的格式转换为日期或者金额
为了满足这些场景的需求,Spring提供了Fomatter接口、DateTimeFormat注解和NumberFormat注解
后面两个接口可能无法满足需求,可以自定义Formatter
自定义DateTimeFormatter
数据格式化
JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。
JSR-303标准
Hibernate Vaildator附加
Hibernate Vaildator的依赖
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-validatorartifactId>
<version>5.4.3.Finalversion>
dependency>
除了需要导入JSR-303的实现依赖以外,还需要在Spring上下文当中装配一个LocalValidatorFactoryBean,不过,只要在BeanConfig中配置了 mvc:annotation-driven 标签,Spring就会自动的为我们装配这个FactoryBean
实际使用时,只要给需要校验的类属性标注上需要的约束注解,并且给相应的控制器方法入参标注@Valid注解即可
首先来说数据校验过程中出现的错误对象,从数据绑定流程我们可以知道数据校验产生的错误也会传入到BindingResult对象当中,需要用到错误信息,添加BindingResult到控制器方法入参即可
需要注意的时,如果需要这个入参,那么这个入参必须要紧跟在@Validator注解的入参后面,即public String xxx(@Valid String name,BindingResult bindingResult){...}
,否则会出现错误
对于简单数据类型(如String,Integer,Float)需要回显到表单,则需要手动的把数据传入到request域中
对于POJO类型入参Spring会自动的将POJO对象传入到Request域中,我们只要使用input标签的value属性即可进行回显
要使用Spring mvc提供的文件上传功能需要把MultipartResolver bean配置到BeanConfig中
在MultipartResolver当中还能配置默认编码,最大上传文件大小等配置信息
<bean name="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8">property>
<property name="maxUploadSize" value="1024000"/>
bean>
还需要使用到commos-fileupload
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.3.1version>
dependency>
上传表单
<form action="xxx" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit"/>
form>
在Controller方法接收上传文件对象
@RequestMapping("testFileUpload")
public String testFileUpload(@RequestParam("file")MultipartFile file){
System.out.println(file.getOriginalName());
System.out.println(file.getSize());
return "success";
}
自定义的拦截器类
public class MyHandlerInterceptor implements HandlerInterceptor {
/**
* 目标方法执行前调用
* 返回值决定了是否执行后续的拦截器方法和目标方法
* handler对象为拦截器所拦截的方法
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
/**
* 目标方法执行后调用,但是在视图渲染之前
* modelAndView对象为渲染前的视图对象,程序处理后返回的ModelAndView对象,亦可为空
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
}
/**
* 目标方法执行后,且在视图渲染之后执行
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
}
}
配置到BeanConfig中
<mvc:interceptors>
<bean class="com.xxx.xxx.MyHandlerInterceptor"/>
mvc:interceptors>
在mvc:interceptors
标签内配置子标签mvc:interceptor
,在子标签内通过mvc:mapping
或mvc:exclude-mapping
子标签配置当前mvc:interceptor
标签下所有的拦截器bean拦截与不拦截的路径
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/xxx"/>
<bean class="xxxHandlerInterceptor">bean>
mvc:interceptor>
mvc:interceptors>
先后配置有拦截器
执行顺序为
在Controller中设置ExceptionHandler注解标注的控制器方法处理异常,接受参数为异常类对象列表,即目标异常类
@ExceptionHandler({
ArithmeticException.class})
public ModelAndView exceptionHandler(Exception ex) {
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
注意,在拦截异常方法中不能使用Map参数,如果需要传参到请求域可以返回ModelAndView对象
拦截的异常类有优先级,有限到具体的异常
该注解标注在抛出异常的方法或自定义异常类上
接受两个参数code与reason,分别为HttpStatus状态码和异常信息
Spring mvc还有默认的异常处理类会对一些特殊的异常进行自动处理
如果希望对所有的异常进行统一的处理可以使用这个解析器,它会将异常映射到视图
配置SimpleMappingExceptionResolver
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionAttribute" value="ex"/>
<property name="exceptionMappings">
<pops>
<prop key="com.nond.springmvc.exceptions.MyException">errorprop>
pops>
property>
bean>
请求发出到服务器,Spring的DispatcherServlet查找Spring mvc中是否存在相应的映射
若不存在,如果配置了mvc:default-servlet-handler则寻找目标静态资源,如果未配置则控制台输出(No mapping found for HTTP request with URI [xx/xx] in DispatcherServlet)且转到404页面
若存在则请求映射到HandlerMapping,获取HandlerExecutionChain(在HandlerExecutionChain当中有处理器对象以及拦截器对象),然后获取HandlerAdapter对象(在其中有数据处理,视图解析等工作),接下来就是拦截器的运行流程