SpringMVC增强
<!--告诉SpringMVC,自己映射的请求就自己处理,不能处理的请求直接交给tomcat -->
<!--使得静态资源能够访问,但是会导致动态映射不行-->
<mvc:default-servlet-handler/>
<!--还需要再加这个,才能完美访问各种资源 -->
<mvc:annotation-driven></mvc:annotation-driven>
<mvc:annotation-driven /> 会自动注册RequestMappingHandlerMapping 、RequestMappingHandlerAdapter 与 ExceptionHandlerExceptionResolver 三个bean。
还将提供以下支持:
- 支持使用 ConversionService 实例对表单参数进行类型转换
- 支持使用 @NumberFormat annotation、@DateTimeFormat 注解完成数据类型的格式化
- 支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证
- 支持使用 @RequestBody 和 @ResponseBody 注解
3种情况
-
什么都不加
- 动态能访问的原因
- DefaultHandlerMapping中的handlerMap保存了每一个资源的映射信息
- 静态不能访问的原因
- DefaultHandlerMapping中的handlerMap没有保存静态资源映射的请求
AnnotationMethodHandlerAdapter帮我们执行目标方法(方法过时了,现在一般都使用的是RequestMappingHandlerMappingAdapter)
- 动态能访问的原因
-
只加<mvc:default-servlet-handler/>
- 动态不能访问
- DefaultHandlerMapping中被搞没了,被SimpleUrlHandlerMapping替换了,他的作用就是将所有请求直接交给tomcat
- 静态能访问的原因:SimpleUrlHandlerMapping把所有请求都映射给Tomcat(Tomact中没有配置处理动态资源的servlet);
- 动态不能访问
-
两个都加上
RequestMappingHandlerMapping中handlerMethods属性保存了每一个请求用哪个方法来处理
AnnotationMethodHandlerAdapter换成了RequestMappingHandlerMappingAdapter,
RequestMappingHandlerMappingAdapter中用一堆解析器来解析请求
必须配置<mvc:annotation-driven />的情况
① 直接配置响应的页面:无需经过控制器来执行结果 ;但会导致其他请求路径失效,需要配置mvc:annotation-driven标签
<mvc:view-controller path="/success" view-name="success"/>
② RESTful-CRUD操作,删除时,通过jQuery执行delete请求时,找不到静态资源,需要配置mvc:annotation-driven标签
<mvc:default-servlet-handler/> 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理。
③ 配置类型转换器服务时,需要指定转换器服务引用
<mvc:annotation-driven conversion-service=“conversionService”/> 会将自定义的ConversionService 注册到 Spring MVC 的上下文中
④ 后面完成JSR 303数据验证,也需要配置
JSR303 :https://blog.csdn.net/w_linux/article/details/80585468
CRUD(增删改查)(可能要补充)
C:Create创建
R:Retrieve查询
U:Update更新
D:Delete删除
数据转化、格式化、校验
1. 自定义类型转换器
1) 类型转换器概述
-
ConversionService 是 Spring 类型转换体系的核心接口。
-
可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC 容器中定义一个ConversionService. Spring 将自动识别出 IOC 容器中的 ConversionService,并在Bean 属性配置及 Spring MVC 处理方法入参绑定等场合使用它进行数据的转换
-
可通过 ConversionServiceFactoryBean 的 converters 属性注册自定义的类型转换器
-
例如:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <list> <bean class="com.application.MyConverter"/> </list> </property> </bean>
2) Spring 支持的转换器类型
Spring 定义了 3 种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到 ConversionServiceFactoryBean 中:
-
Converter:将 S 类型对象转为 T 类型对象
-
ConverterFactory:将相同系列多个 “同质” Converter 封装在一起。如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将 String 转换为 Number 及 Number 子类(Integer、Long、Double 等)对象)可使用该转换器工厂类
-
GenericConverter:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换
3) 自定义转换器示例
需求:字符串转换为对象。
步骤:
① 定义页面
<form action="empAdd" method="POST">
<!-- 解决问题:
1.数据类型转换
2.数据格式
3.数据校验
自定义类型转换器:
将字符串转换为Employee对象,完成添加功能
BirthDay :<input type="text" name="birthDay"/><br><br>
-->
<!-- 字符串格式:lastName-email-gender-department.id
例如:GG-gg@atguigu.com-0-105
-->
Employee : <input type="text" name="employee"/>
<input type="submit" value="Submit"><br><br>
</form>
② 控制器方法
@Controller
public class TypeConversionHandler {
@Autowired
private EmployeeDao employeeDao ;
// String -> Employee 需要类型转换器帮忙
@RequestMapping("/empAdd")
public String empAdd(@RequestParam(value="employee") Employee employee){
System.out.println("TypeConversionHandler - " + employee);
employeeDao.save(employee);
return "redirect:/empList";
}
}
③ 自定义类型转换器
/**
* 将字符串转换为Employee对象类型
*/
@Component
public class StringToEmployeeConverter implements Converter<String, Employee> {
@Override
public Employee convert(String source) {
if(source!=null){
String[] strs = source.split("-");
if(strs!=null && strs.length == 4){
String lastName = strs[0];
String email = strs[1];
Integer gender = Integer.parseInt(strs[2]);
Integer deptId = Integer.parseInt(strs[3]);
Department dept = new Department();
dept.setId(deptId);
Employee employee = new Employee(null,lastName,email,gender,dept);
System.out.println(source+"--converter--"+employee);
return employee ;
}
}
return null;
}
}
④ 声明类型转换器服务
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<!-- 引用类型转换器 -->
<ref bean="stringToEmployeeConverter"/>
</set>
</property>
</bean>
⑤ <mvc:annotation-driven conversion-service=“conversionService”/> 会将自定义的
ConversionService 注册到 Spring MVC 的上下文中
InitBinder注解
@InitBinder
-
由 @InitBinder 标识的方法,可以对 WebDataBinder 对象进行初始化。WebDataBinder 是 DataBinder 的子类,用于完成由表单字段到 JavaBean 属性的绑定
-
@InitBinder方法不能有返回值,它必须声明为void。
-
@InitBinder方法的参数通常是 WebDataBinder
/**
由 @InitBinder 标识的方法,可以对 WebDataBinder 对象进行初始化。
WebDataBinder 是 DataBinder 的子类,用于完成由表单字段到 JavaBean 属性的绑定
@InitBinder方法不能有返回值,它必须声明为void。
@InitBinder方法的参数通常是 WebDataBinder
*/
@InitBinder
public void initBinder(WebDataBinder dataBinder){
dataBinder.setDisallowedFields("lastName");
}
数据格式化概述
-
对属性对象的输入/输出进行格式化,从其本质上讲依然属于 “类型转换” 的范畴。
-
Spring 在格式化模块中定义了一个实现 ConversionService 接口的FormattingConversionService 实现类,该实现类扩展了 GenericConversionService,因此它既具有类型转换的功能,又具有格式化的功能
-
FormattingConversionService 拥有一个 FormattingConversionServiceFactroyBean 工厂类,后者用于在 Spring 上下文中构造前者,FormattingConversionServiceFactroyBean 内部已经注册了 :
-
NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性使用
@NumberFormat 注解
-
JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型的属性使用
@DateTimeFormat 注解
-
-
装配了 FormattingConversionServiceFactroyBean 后,就可以在 Spring MVC 入参绑定及模型数据输出时使用注解驱动了。
- <mvc:annotation-driven/> 默认创建的 ConversionService 实例即为
DefaultFormattingConversionService(继承自FormattingConversionService)
- <mvc:annotation-driven/> 默认创建的 ConversionService 实例即为
日期格式化
-
@DateTimeFormat 注解可对 java.util.Date、java.util.Calendar、java.long.Long 时间类型进行标注:
- pattern 属性:类型为字符串。指定解析/格式化字段数据的模式,如:”yyyy-MM-dd hh:mm:ss”
- iso 属性:类型为 DateTimeFormat.ISO。指定解析/格式化字段数据的ISO模式,包括四种:ISO.NONE(不使用) – 默认、ISO.DATE(yyyy-MM-dd) 、ISO.TIME(hh:mm:ss.SSSZ)、 ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ)
- style 属性:字符串类型。通过样式指定日期时间的格式,由两位字符组成,第一位表示日期的格式,第二位表示时间的格式:S:短日期/时间格式、M:中日期/时间格式、L:长日期/时间格式、F:完整日期/时间格式、-:忽略日期或时间格式
-
在写自定义类型数据转换器时,就使用FormattingConversionService 来注册,这样既具有类型转换功能还有格式化功能.
<!--在配置文件中,添加相应的bean--> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionService"> <!-- 在converters转换器中添加我们自定义的类型转换器 <property name="converters"> <set> <bean class="MyConverters"></bean> </set> </property> --> </bean>
// 在需要进行日期格式化的属性上添加注解 @DateTimeFormat(pattern="yyyy-MM-dd") private Date birth;
数值格式化概述
-
@NumberFormat 可对类似数字类型的属性进行标注,它拥有两个互斥的属性:
-
style:类型为 NumberFormat.Style。用于指定样式类型,包括三种:Style.NUMBER(正常数字类型)、 Style.CURRENCY(货币类型)、 Style.PERCENT(百分数类型)
-
pattern:类型为 String,自定义样式,如pattern="#,###";
-
类型转换失败,获取错误消息
在方法入参处,添加BindingResult 作为参数即可
@RequestMapping("/data")
public String getData(BingingResult result){
if(bindingResult.getErrorCount() > 0 ){
System.out.println("类型转换处错误了");
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
for(FieldError fieldError : fieldErrors){
System.out.println(fieldError.getField() + " - " + fieldError.getDefaultMessage());
}
}
}
数据校验
只做前端校验是不安全的,前端校验一般有js完成,浏览器可以禁用js
在重要数据上,一定要加上后端验证
JSR303 :https://blog.csdn.net/w_linux/article/details/80585468
方法:
- 写程序将每一个数据取出进行校验,如果失败直接来到添加页面,提示其重新填写,最low
- SpringMVC可以使用JSR303 来做数据校验
- JSR303规范----Hibernate Validator(第三方校验框架 )
快速进行后端校验
-
导入校验框架的jar包,也就是Hibernate Validator的jar包
-
导入的东西比较复杂,现场百度
-
只需给javaBean的属性添加上注解
// Book.class // 不为空,长度为6-18个字符 @NotEmpty @Length(min=6,max=18) private String lastName; // 格式需为邮箱格式 @Email
private String email;
* 在SpringMVC封装对象的时候,告诉SpringMVC这个javabean需要校验 ```java // 在封装时,添加@Valid 注解 @RequestMapping("/Book") public void addBook(@Valid Book book){ System.out.println("图书:"+book); }
-
如何知道校验结果
// 在校验的对象后紧跟一个BindingResult,必须是紧跟,中间不能有其他参数 @RequestMapping("/Book") public void addBook(@Valid Book book,BindingResult result){ System.out.println("图书:"+book); // 获取是否有校验错误 boolean hasErrors = result.hasErrors(); if(hasErrors){ // 有错误就返回错误页面 return "Error"; }else{ // 没有错误就返回书籍列表 bookDao.save(book); return "books" }
}
-
在页面显示错误信息
- 在输入框后面添加<form:errors path=“对应前面标签中的path”>
lastName:<form:input path="lastName"/><form:errors path="lastName">
这个表单不是原生的,但是是什么表单我也不知道
Spring MVC 数据校验
-
Spring 4.0 拥有自己独立的数据校验框架,同时支持 JSR 303 标准的校验框架。
-
Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在 Spring MVC 中,可直接通过注解驱动的方式进行数据校验
-
Spring 的 LocalValidatorFactroyBean 既实现了 Spring 的 Validator 接口,也实现了 JSR 303 的 Validator 接口。只要在 Spring 容器中定义了一个 LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean 中。
-
Spring 本身并没有提供 JSR303 的实现,所以必须将 JSR303 的实现者的 jar 包放到类路径下
-
会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @Valid 注解即可让 Spring MVC 在完成数据绑定后执行数据校验的工作
-
在已经标注了 JSR303 注解的表单/命令对象前标注一个 @Valid,Spring MVC 框架在将请求参数绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验
原生表单获取错误信息
// 后端 controller
@RequestMapping("/Book")
public void addBook(@Valid Book book,BindingResult result){
System.out.println("图书:"+book);
List<FieldError> fieldErrors = result.getFieldErrors();
Map errorsMap = new HashMap();
for(FieldError error: fieldErrors){
System.out.println("错误消息提示:"+error.getDefaultMessage());
System.out.println("错误的字段是"+error.getField());
}
// 将错误信息放入一个map 中
errorsMap.put("errorInfo",fieldErrors);
// 获取是否有校验错误
boolean hasErrors = result.hasErrors();
if(hasErrors){
// 有错误就返回错误页面
return "Error";
}else{
// 没有错误就返回书籍列表
bookDao.save(book);
return "books"
}
}
- 前端
<!--前端页面,直接从map中获取相关属性-->
lastName:<form:input path="lastName"/>${errorInfo.lastName}
国际化定制自己的错误消息显示
-
编写国际化属性文件
- errors_zh_CN.properties
-
errors_en_US.properties
-
属性文件的格式:key=value
-
key值的规定:每一个字段发生错误以后,都会有自己的错误代码;国际化文件中错误消息的key必须对应一个错误代码(在error.getDefaultMessage()中就有),key值的几种写法:
-
Pattern.user.password :校验规则.对象.属性
Pattern.password :校验规则.属性
Pattern.java.lang.String :校验规则.属性的类型
Pattern :校验规则
-
校验规则就是标志在属性上的校验注解,例如:@Email
-
-
不知道错误代码时,就将错误信息打印出来,到错误信息中找codes,从中选一个
-
-
配置属性文件
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="errors"></property> </bean>
-
到页面取值
-
高级国际化
- 动态传入消息参数,在属性文件中使用占位符{1},{0}可以取出错误消息
- {0}:当前属性名
- {1},{2}…:才是具体的规则信息,比如长度校验,{1},{2}取出的就分别是最大,最小值.
- 动态传入消息参数,在属性文件中使用占位符{1},{0}可以取出错误消息
如果没有国际化的话…
直接在注解上设置message属性,就可以设置提示消息(就不需要再写属性文件了)
@NotEmpty(message="不能为空")
对ajax的支持(重要,前后端分离,前后端之间交互只有jason)
- 返回数据时jason
- 页面:$.ajax()
快速完成ajax(处理json)
-
在原生javaweb中使用的步骤:
- 导入GSON
- 返回的数据用GSON转成json
- 写出去
-
在SpringMVC-中使用ajax的步骤:
-
导包
- jackson-annotations
- jackson-core
- jackson-databind
-
写配置
- 这个配置文件不用写
-
使用
/** * 本质:将返回的数据放在响应体中, * 也就是说,加了该注释后,方法就不能像之前那样直接返回页面了 * 如果是对象,jackson自动将对象转为json格式 */ @ResponseBody @RequestMapping("/getAll") public Collection ajaxGetAll(){ // 注意要返回json对应的对象或者集合 Collection all = employeeDao.getAll(); return all; }
-
- jackson 中还有其他很多注释,可以去了解下
- @RequestBody:请求体,获取请求的请求体,接受json数据,封装对象(放在方法参数的前面)
- 如果参数位置写HttpEntity,比@RequestBody更加强大,可以拿到请求头
- 方法返回对象可以设置为ResponseEntity,这样可以在返回时设置返回头,和一些其他的东西
SpringMVC对文件下载支持特别差
HttpMessageConverter<T>
-
HttpMessageConverter是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为 T),将对象(类型为T**)输出为响应信息**
-
HttpMessageConverter接口定义的方法:
-
Boolean canRead(Class<?> clazz,MediaType mediaType): 指定转换器可以读取的对象类型,即转换器是否可将请求信息转换为 clazz 类型的对象,同时指定支持 MIME 类型(text/html,applaiction/json等)
-
Boolean canWrite(Class<?> clazz,MediaType mediaType):指定转换器是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型在MediaType 中定义。
-
LIst getSupportMediaTypes():该转换器支持的媒体类型。
-
T read(Class<? extends T> clazz,HttpInputMessage inputMessage):将请求信息流转换为 T 类型的对象。
-
void write(T t,MediaType contnetType,HttpOutputMessgae outputMessage):将T类型的对象写到响应流中,同时指定相应的媒体类型为 contentType。
1. 使用HttpMessageConverter
-
使用 HttpMessageConverter **将请求信息转化并绑定到处理方法的入参中或将响应结果转为对应类型的响应信息,**Spring 提供了两种途径:
-
使用 @RequestBody / @ResponseBody 对处理方法进行标注
-
使用 HttpEntity / ResponseEntity 作为处理方法的入参或返回值
-
当控制器处理方法使用到 @RequestBody/@ResponseBody 或HttpEntity<T>/ResponseEntity<T> 时, Spring 首先根据请求头或响应头的 Accept 属性选择匹配的 HttpMessageConverter, 进而根据参数类型或泛型类型的过滤得到匹配的 HttpMessageConverter, 若找不到可用的 HttpMessageConverter 将报错
-
@RequestBody 和 @ResponseBody 不需要成对出现
-
@RequestBody 和 @ResponseBody 示例:
- HttpSession和ResponseEntity使用示例:
文件上传
-
文件上传表单准备:enctype=“multipart/form-data”
-
导入fileupload:
- commons-fileupload
- commons-io
-
在SpringMVC配置文件中配置文件上传解析器(id必须进行配置且必须是multipartResolver)
<bean id="multipartResolver" class="CommonsMultipartResolver"> <property name=maxUploadSize" value="#{1024*1024*20}"></property> </bean>
-
@RequestMapping("/upload") public Collection ajaxGetAll(@RequestParam("headerimg") MultipartFile file){ //MultipartFile 对象中封装了关于文件的信息 Collection all = employeeDao.getAll(); return all; }
拦截器
拦截器(HandlerInterceptor)
SpringMVC 提供了拦截器机制,允许运行目标方法之前进行一些拦截工作,或者方法运行后的一些其他处理和filter(JavaWeb)差不多
拦截器是一个接口
HandlerInterceptor下的三个方法:
-
preHandler:目标方法运行之前运行
- postHandler:目标方法运行之后运行
3. afterCompletion:来到目标页面之后运行
- postHandler:目标方法运行之后运行
实现自己拦截器的步骤
-
实现HandlerInterceptor 接口
-
在SpringMVC配置文件中注册这个拦截器工作
-
配置拦截器要拦截那些请求
<mvc:interceptors> <!--配置某个拦截器,默认拦截所有请求--> <!--<bean class="MyFirstInterceptor">--> <!--配置某个拦截器更详细的信息--> <mvc:interceptor> <!--只拦截test01请求--> <mvc:mapping path="/test01"> <bean class="MyFirstInterceptor"> </mvc:interceptor> </mvc:interceptors>
-
-
拦截器的运行流程
拦截器preHandler—>目标方法—>拦截器的postHandler—>页面—>拦截器的afterCompletion
只要preHandler不放行(返回值为false的时候), 就没有以后的流程
只要放行了,拦截器的afterCompletion就一定会运行
多个拦截器
-
方法的运行流程和filter基本一致.
- preHandler:按照拦截器的顺序执行
- postHandler:按照拦截器的逆序执行
- afterCompletion:按照拦截器的逆序执行
-
如果MySecondInterceptor 的preHandler不放行的话,她后面的方法不会再执行,但是在她之前已经放行了的拦截器的afterCompletion还是会执行
-
多拦截器
异常处理
ExceptionHandlerExceptionResolver:支持@Exception注解
ResponseStatusExceptionResolver:支持@ResponseStatus注解,给自定义异常上标注的
SpringMVC处理异常
-
给一个方法上加@ExceptionHandler 注解,来接收发送的异常
-
如果要接收异常信息就不能再参数位置写Model(加了@ExceptionHandler 注解之后,该方法就只能接收异常类信息)
-
如果想把信息返回给页面,那么把方法的返回值设置为ModelAndView就可以,然后把相返回的信息加入到ModelAndView中就行
-
如果有多个处理异常的方法,发生异常时根据异常的类型执行指定的方法,如果方法处理的异常有包含关系,那么优先执行那个更精确的异常处理方法例如
@ExceptionHandler(value = {Exception.class}) public ModelAndView errorHandler01(Exception exception){ System.out.println("异常信息:"+exception); ModelAndView view = new ModelAndView("myerror"); view.addObject("error",exception); return view; } @ExceptionHandler(value = {ArithmeticException.class}) public ModelAndView errorHandler02(Exception exception){ System.out.println("异常信息:"+exception); ModelAndView view = new ModelAndView("myerror"); view.addObject("error",exception); return view; } // 在发生ArithmeticException类型的异常时,会优先执行errorHandler02方法
设置异常处理类,处理全局异常
我们可以设置一个专门的类,来处理所有异常.
- 首先当然是将这个类加入到ioc容器中,这里我们需要一个新的注解:@ControllerAdvice.
- 之后在类中的方法上按之前所说的,加上@ExceptionHandler注解即可
当全局异常处理与局部异常处理同时存在时,谁优先执行呢?
那肯定是是局部异常处理方法优先(清扫门户当然得自己来了),只有当自己的异常处理无法处理时,才轮到全局异常处理方法执行
设计自己的异常类
使用@ResponseStatus 标注在自己的异常类上,就可以在错误页面上显示自己想要的信息
// 我的异常类 UserNotFoundError
// 注意需要继承RuntimeException类
// reason的值将会显示在错误页面的message出,value是错误码
@ResponseStatus(reason = "用户未注册",value = HttpStatus.NOT_FOUND)
public class UserNotFoundError extends RuntimeException{
}
当程序抛出UserNotFoundError 异常时,浏览器显示的错误页面将会是这样的:
注意:在进行程序测试时,如果程序中有处理UserNotFoundError 异常的类或方法,需要先行注释掉,否则异常被她们处理掉,就不会在浏览器处显示你想看到的页面了.
使用SpringMVC自带的异常处理器
当然需要首先进行配置,将其加入到ioc容器中
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!--配置那些异常区那些页面-->
<property name="exceptionMappings">
<props>
<!--key:要处理的异常的全类名,标签中间的值为要去往的页面,视图解析器会自动进行拼串-->
<prop key="java.lang.NullPointerException">myerror</prop>
</props>
</property>
<!--这个属性设置 页面取出错误信息时,使用的key,默认值是exception,可以通过value属性进行设置-->
<property name="exceptionAttribute" value="error">
</property>
</bean>
值得注意的是,自己添加到ioc容器中的异常处理器的优先级是最低的.
运行流程
1. 流程图
2. Spring工作流程描述
-
用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获;
-
DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI):
判断请求URI对应的映射
① 不存在:
a) 再判断是否配置了mvc:default-servlet-handler:
b) 如果没配置,则控制台报映射查找不到,客户端展示404错误
c) 如果有配置,则执行目标资源(一般为静态资源,如:JSP,HTML)
② 存在:
a) 执行下面流程
-
根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
-
DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。
-
如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法【正向】
-
提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
① HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
② 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
③ 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
④ 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
-
Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
-
此时将开始执行拦截器的postHandle(…)方法【逆向】
-
根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet,根据Model和View,来渲染视图
-
在返回给客户端时需要执行拦截器的AfterCompletion方法【逆向】
-
将渲染结果返回给客户端
3. 源码解析
1) 搭建环境
① 导入jar包
spring-aop-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
commons-logging-1.1.3.jar
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jar
② 配置文件web.xml
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
③ 配置文件springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 设置扫描组件的包 -->
<context:component-scan base-package="com.atguigu.springmvc"/>
<!-- 配置视图解析器 -->
<bean id="internalResourceViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
2) 完成HelloWorld
① 页面链接
<a href="helloworld">Hello World</a>
② 控制器方法
package com.atguigu.springmvc.handler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldHandler {
@RequestMapping("/helloworld")
public String testHello(){
System.out.println("Hello,SpringMVC...");
return "success";
}
}
③ 成功页面:/views/success.jsp
<h3>Success Page</h3>
helloworld 的程序:https://github.com/woodwolfzhu/webTest02