1.Spring入门
1.1 Springmvc是什么
Spring web mvc和Struts2都属于表现层的框架,它是Spring框架的一部分,我们可以从Spring的整体结构中看得出来,如下图:
1.2 Springmvc处理流程
1.3 入门程序(Hello world)
- 步骤:
- 加入 jar – 包
- 在 web.xml 中配置 DispatcherServlet –
- 加入 Spring MVC 的配置文件 –
- 编写处理请求的处理器,并标识为处理器 –
- 编写视图
1.3.1 创建web工程
1.3.2 导入jar包
1.3.3 加入配置文件
1.3.3.1 创建springmvc.xml
创建SpringMVC的核心配置文件
SpringMVC本身就是Spring的子项目,对Spring兼容性很好,不需要做很多配置。
这里只配置一个Controller扫描就可以了,让Spring对页面控制层Controller进行管理。
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 配置controller扫描包=================== -->
<context:component-scan base-package="com.itheima" />
<!-- 配置处理器映射器,默认的已经废弃 -->
<!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" /> -->
<!-- 配置处理器适配器,默认的已经废弃 -->
<!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" /> -->
<!-- 注解驱动,替代上面两个====================== -->
<mvc:annotation-driven/>
<!-- 配置视图解析器 ==========================-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置逻辑视图的前缀 -->
<property name="prefix" value="/WEB-INF/views/"/>
<!-- 配置逻辑视图的后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
1.3.3.2. 配置前端控制器
配置 DispatcherServlet :DispatcherServlet 默认加载 /WEB-INF/<servletName-servlet>.xml 的 Spring 配置文件, 启动 WEB 层
的 Spring 容器。可以通过 contextConfigLocation 初始化参数自定义配置文件的位置和名称
在web.xml中
<?xml version="1.0" encoding="UTF-8"?>
<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/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>SpringMvc_day01</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 配置SpringMVC前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定SpringMVC配置文件 -->
<!-- SpringMVC的配置文件的默认路径是/WEB-INF/${servlet-name}-servlet.xml -->
<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>springmvc</servlet-name>
<!--
1. /*拦截所有,全部拦截
2. *.action *.do 拦截以do,action结尾的请求 ERP
3. /拦截所有(不包括jsp)(包含 .js .png .css) 强烈建议使用 面向前端消费者 对静态资源放行
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
1.3.4 Controller
Controller是一个普通的java类,不需要实现任何接口。需要在类上添加@Controller注解,把Controller交由Spring管理,在方法上面添加@RequestMapping注解,里面指定请求的url。
1.3.5 页面
1.3.6 success.jsp页面
2 Springmvc架构
2.1 框架结构
框架结构如下图:
2.2 架构流程
- 用户发送请求至前端控制器DispatcherServlet
- DispatcherServlet收到请求调用HandlerMapping处理器映射器。
- 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
- DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
- 执行处理器(Controller,也叫后端控制器)。
- Controller执行完成返回ModelAndView
- HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器
- ViewReslover解析后返回具体View
- DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
- DispatcherServlet响应用户
2.3 组件说明
以下组件通常使用框架提供实现:
- DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。
- HandlerMapping:处理器映射器
HandlerMapping负责根据用户请求url找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
- Handler:处理器
Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
- HandlAdapter:处理器适配器
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
下图是许多不同的适配器,最终都可以使用usb接口连接
- ViewResolver:视图解析器
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
- View:视图
springmvc框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
说明:在springmvc的各个组件中,处理器映射器、处理器适配器、视图解析器称为springmvc的三大组件。
需要用户开发的组件有handler、view
2.4 默认加载的组件
我们没有做任何配置,就可以使用这些组件
因为框架已经默认加载这些组件了,配置文件位置如下图:
2.5 组件扫描器
使用组件扫描器省去在spring容器配置每个Controller类的繁琐。
使用<context:component-scan>自动扫描标记@Controller的控制器类,
在springmvc.xml配置文件中配置如下:
2.6. 注解映射器和适配器
2.6.1. 配置处理器映射器
注解式处理器映射器,对类中标记了@ResquestMapping的方法进行映射。根据@ResquestMapping定义的url匹配@ResquestMapping标记的方法,匹配成功返回HandlerMethod对象给前端控制器。
HandlerMethod对象中封装url对应的方法Method。
从spring3.1版本开始,废除了DefaultAnnotationHandlerMapping的使用,推荐使用RequestMappingHandlerMapping完成注解式处理器映射。
在springmvc.xml配置文件中配置如下:
注解描述:
@RequestMapping:定义请求url到处理器功能方法的映射
2.6.2. 配置处理器适配器
注解式处理器适配器,对标记@ResquestMapping的方法进行适配。
从spring3.1版本开始,废除了AnnotationMethodHandlerAdapter的使用,推荐使用RequestMappingHandlerAdapter完成注解式处理器适配。
在springmvc.xml配置文件中配置如下:
2.6.3. 注解驱动
直接配置处理器映射器和处理器适配器比较麻烦,可以使用注解驱动来加载。
SpringMVC使用<mvc:annotation-driven>自动加载RequestMappingHandlerMapping和RequestMappingHandlerAdapter
可以在springmvc.xml配置文件中使用<mvc:annotation-driven>替代注解处理器和适配器的配置。
2.7. 视图解析器
视图解析器使用SpringMVC框架默认的InternalResourceViewResolver,这个视图解析器支持JSP视图解析
在springmvc.xml配置文件中配置如下:
逻辑视图名需要在controller中返回ModelAndView指定,比如逻辑视图名为ItemList,则最终返回的jsp视图地址:
“WEB-INF/jsp/itemList.jsp”
最终jsp物理地址:前缀+逻辑视图名+后缀
4. 使用 @RequestMapping 映射请求
- Spring MVC 使用 @RequestMapping 注解为控制器指定可 以处理哪些 URL 请求
- 在控制器的类定义及方法定义处都可标注@RequestMapping
- 类定义处:提供初步的请求映射信息。相对于 WEB 应用的根目录
- 方法处:提供进一步的细分映射信息。相对于类定义处的 URL。若类定义处未标注 @RequestMapping,则方法处标记的 URL 相对于WEB 应用的根目录
- DispatcherServlet 截获请求后,就通过控制器上 @RequestMapping 提供的映射信息确定请求所对应的处理方法。
- value的值是数组,可以将多个url映射到同一个方法
4.1 映射请求参数、请求方法或请求头
- @RequestMapping 除了可以使用请求 URL 映射请求外, 还可以使用请求方法、请求参数及请求头映射请求
- @RequestMapping 的 value、method、params 及 heads 分别表示请求 URL、请求方法、请求参数及请求头的映射条件,他们之间是与的关系,联合使用多个条件可让请求映射更加精确化。
- params 和 headers支持简单的表达式:
- param1: 表示请求必须包含名为 param1 的请求参数
- !param1: 表示请求不能包含名为 param1 的请求参数
- param1 != value1: 表示请求包含名为 param1 的请求参数,但其值不能为 value1
- {“param1=value1”, “param2”}: 请求必须包含名为 param1 和param2 的两个请求参数,且 param1 参数的值必须为 value1
4.2 Ant 风格
- Ant 风格资源地址支持 3 种匹配符:
- ?:匹配文件名中的一个字符 –
- *:匹配文件名中的任意字符 –
- **:** 匹配多层路径 –
- @RequestMapping 还支持 Ant 风格的 URL• :
- /user/*/createUser: 匹配 /user/aaa/createUser、/user/bbb/createUser 等 URL
- /user/**/createUser: 匹配 /user/createUser、/user/aaa/bbb/createUser 等 URL
- /user/createUser??: 匹配 /user/createUseraa、/user/createUserbb 等 URL
- 实例:
4.3 @PathVariable 映射 URL 绑定的占位符
- 带占位符的 URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义
- 通过 @PathVariable 可以将 URL 中占位符参数绑定到控 制器处理方法的入参中:URL 中的 {xxx} 占位符可以通过@PathVariable("xxx") 绑定到操作方法的入参中。
4.4 RestFu风格
- REST:即 Representational State Transfer。(资源)表现层状态转化。是目前 最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
- 资源(Resources):网络上的一个实体,或者说是网络上的一个具体信息。它 可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要获取这个资源,访问它的URI就可以,因此 URI 即为每一个资源的独一无二的识别符。
- 表现层(Representation):把资源具体呈现出来的形式,叫做它的表现层 •(Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
- 状态转化(State Transfer):每 发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是 “表现层状态转化”。具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
- Rest 风格的 URL. 以 CRUD 为例:
- 新增: /order POST
- 修改: /order/1 PUT update?id=1
- 获取:/order/1 GET get?id=1
- 删除: /order/1 DELETE delete?id=1
如何发送 PUT 请求和 DELETE 请求呢 ?
HiddenHttpMethodFilter:浏览器 form 表单只支持 GET •与 POST 请求,而DELETE、PUT 等 method 并不支持Spring3.0 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使得支持 GET、POST、PUT 与DELETE 请求。
1. 需要配置 HiddenHttpMethodFilter
2. 需要发送 POST 请求
3. 需要在发送 POST 请求时携带一个 name="_method" 的隐藏域, 值为 DELETE 或 PUT
在 SpringMVC 的目标方法中如何得到 id 呢? 使用 @PathVariable 注解
表单: 必须包含隐藏的属性
controller:
【特别注意】:
* 我们要能支持直接发送PUT之类的请求还要封装请求体中的数据(针对ajax)
* 1、配置上HttpPutFormContentFilter;
* 2、他的作用;将请求体中的数据解析包装成一个map。
* 3、request被重新包装,request.getParameter()被重写,就会从自己封装的map中取数据
5 请求处理方法签名
- Spring MVC 通过分析处理方法的签名,将 HTTP 请求信 息绑定到处理方法的相应人参中。
- Spring MVC 对控制器处理方法签名的限制是很宽松的,几乎可以按喜欢的任何方式对方法进行签名。
- 必要时可以对方法及方法入参标注相应的注解(@PathVariable、@RequestParam、@RequestHeader 等)、Spring MVC 框架会将 HTTP 请求的信息绑定到相应的方法入参中,并根据方法的返回值类型做出相应的后续处理。
5.1 使用 @RequestParam 绑定请求参数值
value:参数名字,即入参的请求参数名字,如value=“itemId”表示请求的参数 区中的名字为itemId的参数的值将传入
required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报错
TTP Status 400 - Required Integer parameter 'XXXX' is not present
defaultValue:默认值,表示如果请求中没有同名参数时的默认值
定义如下:
5.2 @RequestHeader
请求头包含了若干个属性,服务器可据此获知客户端的信 息,通过 @RequestHeader 即可将请求头中的属性值绑
定到处理方法的入参中。
5.3 @CookieValue
@CookieValue 可• 让处理方法入参绑定某个 Cookie 值
5.4. 绑定简单类型
当请求的参数名称和处理器形参名称一致时会将请求参数与形参进行绑定。这样,从Request取参数的方法就可以进一步简化。
参数类型推荐使用包装数据类型,因为基础数据类型不可以为null
整形:Integer、int
字符串:String
单精度:Float、float
双精度:Double、double
布尔型:Boolean、boolean
说明:对于布尔类型的参数,请求的参数值为true或false。或者1或0
请求url:
http://localhost:8080/xxx.action?id=2&status=false
处理器方法:
public String editItem(Model model,Integer id,Boolean status)
5.5 绑定pojo类型
Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值。
支持级联属性,如:dept.deptId、dept.address.tel 等
要求:pojo对象中的属性名和表单中input的name属性一致。
5.5.1 示例(包含级联)
1. POJO类:
2. 表单页面定义如下图:
3. 请求的参数名称和pojo的属性名称一致,会自动将请求参数赋值给pojo的属性。
注意:提交的表单中不要有日期类型的数据,否则会报400错误。如果想提交日期类型的数据需要用到后面的自定义参数绑定的内容。
5.5.2 解决post乱码问题
提交发现,保存成功,但是保存的是乱码
在web.xml中加入:
以上可以解决post请求乱码问题。
对于get请求中文参数出现乱码解决方法有两个:
修改tomcat配置文件添加编码与工程编码一致,如下:
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
另外一种方法对参数进行重新编码:
String userName new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")
ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码
5.6 复杂类型数据绑定
5.6.1 绑定数组类型
表单页面:checkbox是复选框提交到后台将是一个数组类型
pojo对象:
处理表单页面请求的handler:使用数组类型的参数或者直接使用pojo类就可以绑定这个数组
5.6.2 绑定list类型
首先是页面请求handler,hander保存数据到域对象,保存的数据是一个list集合
items.jsp页面接受到handler发送来的数据,把数据遍历显示出来:
Pojo类和Item类:
items.jsp表单提交的页面,使用Poojo类接收表单提交的list集合,实现参数绑定:
5.7 Controller中方法默认支持的参数类型(ServletAPI)
处理器形参中添加如下类型的参数处理适配器会默认识别并进行赋值。
- HttpServletRequest:通过request对象获取请求信息
- HttpServletResponse:通过response处理响应信息
- HttpSession:通过session对象得到session中存放的对象
- java.security.Principal
- Locale
- InputStream
- OutputStream
- Reader
- Writer
6 处理模型数据
Spring MVC 提供了以下几种途径输出模型数据:
- ModelAndView: 处理方法返回值类型为 ModelAndView 时, 方法体即可通过该对象添加模型数据
- Map 及 Model: 入参为
java.util.Map
/org.springframework.io.Model
/org.springframework.ui.ModelMap
时,处理方法返回时,Map 中的数据会自动添加到模型中。- @SessionAttributes: 将模型中的某个属性暂存到 HttpSession 中,以便多个请求之间可以共享这个属性
- @ModelAttribute: 方法入参标注该注解后, 入参的对象就会放到数据模型中
模型数据就是放到request域中,可以在页面获取到
6.1 ModelAndView
控制器处理方法的返回值如果为 ModelAndView, 则其既 包含视图信息,也包含模型数据信息。
- 添加模型数据 :
- MoelAndView addObject(String attributeName, Object attributeValue)
- ModelAndView addAllObject(Map<String, ?> modelMap– )
- 设置视图 :
- void setView(View view )
- void setViewName(String viewName)
示例:
controller:
页面:
跳转的页面:从request域中获取
6.2 Map & Model & ModelMap
- Spring MVC 在内部使用了一个org.springframework.ui.Model 接口存储模型数据
- 具体• 步骤
- Spring MVC 在调用方法前会创建一个–隐含的模型对象作为模型数据的存储容器。
- 如果方法的入参为 Map 或 Model类型,Spring MVC 会将隐含模型的引用传给这些入参。在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据
- Model是一个接口,在参数里直接声明model即可。
- 如果使用Model则可以不使用ModelAndView对象,Model对象可以向页面传递数据,View对象则可以使用String返回值替代。不管是Model还是ModelAndView,其本质都是使用Request对象向jsp传递数据。
- ModelMap是Model接口的实现类,也可以通过ModelMap向页面传递数据,使用Model和ModelMap的效果一样,如果直接使用Model,springmvc会实例化ModelMap。
页面请求:
Controller:
显示的页面:
6.3 @SessionAttributes
- 若希望在多个请求之间共用某个模型属性数据,则可以在 控制器类上标注一个@SessionAttributes, Spring MVC 将在模型中对应的属性暂存到 HttpSession 中。
- @SessionAttributes 除了可以通过属性名指定需要放到会 话中的属性外,还可以通过模型属性的对象类型指定哪些。
- 模型属性需要放到会话中:
- @SessionAttributes(types=User.class) 会将隐含模型中所有类型为 User.class 的属性添加到会话中
- @SessionAttributes(value={“user1”, “user2”})
- @SessionAttributes(types={User.class, Dept.class})
- @SessionAttributes(value={“user1”, “user2”}, types={Dept.class})
页面:
Contriller:
6.4 @ModelAttribute
- 1. 有 @ModelAttribute 标记的方法, 会在每个目标方法执行之前被 SpringMVC 调用!
- 2. @ModelAttribute 注解也可以来修饰目标方法 POJO 类型的入参, 其 value 属性值有如下的作用:
- 1). SpringMVC 会使用 value 属性值在 implicitModel 中查找对应的对象, 若存在则会直接传入到目标方法的入参中.
- 2). SpringMVC 会以 value 为 key, POJO 类型的对象为 value, 存入到 request 中.将方法入参对象添加到模型中
- 在方法定义上使用 @ModelAttribute 注解有两种方式:
- 在方法参数上使用@ModelAttribute注解:
案例:修改user的数据,但是不修改密码
- 表单:
- Controller:
- success.jsp:
- success.jsp页面显示结果:
- 运行流程:
1. 执行 @ModelAttribute 注解修饰的方法: 从数据库中取出对象, 把对象放入到了 Map 中. 键为: user
2. SpringMVC 从 Map 中取出 User 对象, 并把表单的请求参数赋给该 User 对象的对应属性.
3. SpringMVC 把上述对象传入目标方法的参数.
注意: 在 @ModelAttribute 修饰的方法中, 放入到 Map 时的键需要和目标方法入参类型的第一个字母小写的字符串一致!
- SpringMVC 确定目标方法 POJO 类型入参的过程
1. 确定一个 key:
1). 若目标方法的 POJO 类型的参数木有使用 @ModelAttribute 作为修饰, 则 key 为 POJO 类名第一个字母的小写
2). 若使用了 @ModelAttribute 来修饰, 则 key 为 @ModelAttribute 注解的 value 属性值.
2. 在 implicitModel 中查找 key 对应的对象, 若存在, 则作为入参传入
1). 若在 @ModelAttribute 标记的方法中在 Map 中保存过, 且 key 和 1 确定的 key 一致, 则会获取到.
3. 若 implicitModel 中不存在 key 对应的对象, 则检查当前的 Handler 是否使用 @SessionAttributes 注解修饰, 若使用了该注解, 且 @SessionAttributes 注解的 value 属性值中包含了 key, 则会从 HttpSession 中来获取 key 所对应的 value 值, 若存在则直接传入到目标方法的入参中. 若不存在则将抛出异常.。
4. 若 Handler 没有标识 @SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key, 则会通过反射来创建 POJO 类型的参数, 传入为目标方法的参数。
5. SpringMVC 会把 key 和 POJO 类型的对象保存到 implicitModel 中, 进而会保存到 request 中。
- 概括:使用@ModelAttribute修饰方法的入参,会先从implicitModel中取,若取不到则从session中取,若再取不到,则自己反射创建一个,然后放到implicitModel中。
- 源代码分析的流程
1. 调用 @ModelAttribute 注解修饰的方法. 实际上把 @ModelAttribute 方法中 Map 中的数据放在了 implicitModel 中.
2. 解析请求处理器的目标参数, 实际上该目标参数来自于 WebDataBinder 对象的 target 属性
1). 创建 WebDataBinder 对象:
①. 确定 objectName 属性: 若传入的 attrName 属性值为 "", 则 objectName 为类名第一个字母小写.
注意: attrName. 若目标方法的 POJO 属性使用了 @ModelAttribute 来修饰, 则 attrName 值即为 @ModelAttribute 的 value 属性值
②. 确定 target 属性:
> 在 implicitModel 中查找 attrName 对应的属性值. 若存在, ok
> 若不存在: 则验证当前 Handler 是否使用了 @SessionAttributes 进行修饰, 若使用了, 则尝试从 Session 中获取 attrName 所对应的属性值. 若 session 中没有对应的属性值, 则抛出了异常.
> 若 Handler 没有使用 @SessionAttributes 进行修饰, 或 @SessionAttributes 中没有使用 value 值指定的 key和 attrName 相匹配, 则通过反射创建了 POJO 对象
2). SpringMVC 把表单的请求参数赋给了 WebDataBinder 的 target 对应的属性.
3). SpringMVC 会把 WebDataBinder 的 attrName 和 target 给到 implicitModel. 近而传到 request 域对象中.
4). 把 WebDataBinder 的 target 作为参数传递给目标方法的入参.
HttpSessionRequiredException异常:
- 在3处使用@SessionAttributes(value = "user")将model中的数据放置到session中,使用1将user对象放置到model中。当请求过来时,先访问1方法,1将user对象放置到model,表单的数据会被封装到这个model中,然后@SessionAttributes会将model中的数据放置到session中,2直接从model中取数据,放到自己的入参中。
- 如果没有1,则2从model和session中取不到数据,会报异常。
如果在处理类定义处标注了@SessionAttributes(“xxx”),则 尝试从会话中获取该属性,并将其赋给该入参,然后再用请求消息填充该入参对象。如果在会话中找不到对应的属性,则抛出 HttpSessionRequiredException 异常。
如何解决异常:使用@ModelAttribute标注的方法往session中添加数据
7 视图&视图解析器
7.1 Spring MVC如何解析视图
- 请求处理方法执行完成后,最终返回一个 ModelAndView对象。对于那些返回 String,View 或 ModeMap 等类型的处理方法,Spring MVC 也会在内部将它们装配成一个ModelAndView 对象,它包含了逻辑名和模型对象的视图
- Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是 JSP ,也可能是Excel、JFreeChart 等各种表现形式的视图
- 对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现 MVC 的充分解耦
7.2 视图
视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客 户。
为了实现视图模型和具体实现技术的解耦,Spring 在org.springframework.web.servlet 包中定义了一个高度抽象的 View接口:
视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们不会有线程安全的问题
常用的视图实现类:
7.3 视图解析器
- 以在 Spring WEB 上下文中配置一种或多种解析策略,并指定他们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类。
- 视图解析器的作用比较单一:将逻辑视图解析为一个具体 的视图对象。
- 所有的视图解析器都必须实现 ViewResolver 接口:
常用的视图解析器实现类:
- 程序员可以选择一种视图解析器或混用多种视图解析器 每个视图解析器都实现了 Ordered 接口并开放出一个 order 属性,可 以通过 order 属性指定解析器的优先顺序,order 越小优先级越高。
- SpringMVC 会按视图解析器顺序的优先顺序对逻辑视图名进行解 析,直到解析成功并返回视图对象,否则将抛出ServletException 异常
7.4 InternalResourceViewResolver
- JSP 是最常见的视图技术,可以使用InternalResourceViewResolver 作为视图解析器:
- 若项目中使用了 JSTL,• 则 SpringMVC 会自动把视图由InternalResourceView 转为 JstlView
- 若使用 JSTL 的 fmt 标签则需要在 SpringMVC 的配置文件中配置国际化资源文件
- success.jsp页面输出:
- i18n配置文件:
- 若希望直接响应通过 SpringMVC 渲染的页面,可以使用 mvc:view-controller 标签实现直接跳转
注意:配置这个标签后需要配置下面这个标签,不然(requestmapping会不起作用)
----自定义示视图解析器----
1.编写一个类实现view接口
2.在speingMVC配置文件中配置视图解析器
3.使用方法
7.5 关于重定向
- 一般情况下,控制器方法返回字符串类型的值会被当成逻辑视图名处理.
- 如果返回的字符串中带 forward: 或 redirect: 前缀时,SpringMVC 会对他们进行特殊处理:将 forward: 和redirect: 当成指示符,其后的字符串作为 URL 来处理.
- redirect:success.jsp:会完成一个到 success.jsp 的重定向的操作 –
- forward:success.jsp:会完成一个到 success.jsp 的转发操作
- 页面:
-
Controller
8 使用 Spring 的表单标签
通过 SpringMVC 的表单标签可以实现将模型数据 中的属性和 HTML 表单元素相绑定,以实现表单数据更便捷编辑和表单值的回显。
- 使用前要引入表单标签库:
全部标签:
form 标签:
- 一般情况下, 通过 GET 请求获取表单页面,而通过POST 请求提交表单页面,因此获取表单页面和提交表单页面的 URL 是相同的。只要满足该最佳条件的契约,<form:form> 标签就无需通过 action 属性指定表单提交的 URL
- 可以通过 modelAttribute 属性指定绑定的模型属性,若 没有指定该属性,则默认从 request 域对象中读取command 的表单 bean,如果该属性值也不存在,则会发生错误。一定要设置modelAttribute 属性的值。
表单标签:
- SpringMVC提供了多个表单组件标签,如<form:input/>、<form:select/> 等,用以绑定表单字段的属性值,它们的共有属性下:
- path:表单字段,对应 html 元素的 name 属性,支持级联属性
- htmlEscape:是否对表单值的 HTML 特殊字符进行转换,默认值为 true
- cssClass:表单组件对应的 CSS 样式类名
- cssErrorClass:表单组件的数据存在错误时,采取的 CSS样式
- form:input、form:password、form:hidden、form:textarea :对应 HTML 表单的 text、password、hidden、textarea标签
- form:radiobutton:单选框组件标签,当表单 bean 对应的属性值和 value 值相等时,单选框被选中
- form:radiobuttons:单选框组标签,用于构造多个单选框
- items:可以是一个 List、String[] 或 Map
- itemValue:指定 radio 的 value 值。可以是集合中 bean 的一个属性值
- itemLabel:指定 radio 的 label值
- delimiter:多个单选框可以通过 delimiter 指定分隔符
- form:checkbox:复选框组件。用于构造单个复选框
- form:checkboxs:用于构造多个复选框。使用方式同 form:radiobuttons 标签
- form:select:用于构造下拉框组件。使用方式同 form:radiobuttons 标签
- form:option:下拉框选项组件标签。使用方式同 form:radiobuttons 标签
- form:errors:显示表单组件或数据校验所对应的错误
- <form:errors path= “ *” /> :显示表单所有的错误
- <form:errors path= “ user*” /> :显示所有以 user 为前缀的属性对应的错误
- <form:errors path= “ username” /> :显示特定表单对象属性的错误
9 处理静态资源
- 优雅的 REST 风格的资源URL 不希望带 .html 或 .do 等后缀
- 若将 DispatcherServlet 请求映射配置为 /,则 Spring MVC 将捕获WEB 容器的所有请求,包括静态资源的请求, SpringMVC 会将他们当成一个普通请求处理,因找不到对应处理器将导致错误。
- 可以在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler/> 的方式解决静态资源的问题:
- <mvc:default-servlet-handler/> 将在 SpringMVC 上下文中定义一个DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由DispatcherServlet 继续处理
- 一般 WEB 应用服务器默认的 Servlet 的名称都是 default。若所使用的WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定
10 数据转换 & 数据格式化 & 数据校验
10.1 数据绑定流程
- Spring MVC 主框架将 ServletRequest对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象
- DataBinder 调用装配在 Spring MVC 上下文中的ConversionService 组件进行数据类型转换、数据格式化工作。将 Servlet 中的请求信息填充到入参对象中
- 调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果BindingData 对象
- Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参
- Spring MVC 通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中。数据绑定的核心部件是DataBinder,运行机制如下:
10.2 数据转换
Spring MVC 上下文中内建了很多转换器,可完成大多数 Java 类型的转换工作。
自定义类型转换器:
- ConversionService 是 Spring 类型转换体系的核心接口。
- 可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC 容器中定义一个 ConversionService. Spring 将自动识别出IOC 容器中的 ConversionService,并在 Bean 属性配置及Spring MVC 处理方法入参绑定等场合使用它进行数据的转换
- 可通过 ConversionServiceFactoryBean 的 converters 属性注册自定义的类型转换器
Spring 支持的转换器:
- Spring 定义了 3 种类型的转换器接口,实现任意一个转换 器接口都可以作为自定义转换器注册到ConversionServiceFactroyBean 中:
- Converter<S,T>:将 S 类型对象转为 T 类型对象 .
- ConverterFactory:将相同系列多个 “同质” Converter 封装在一起。如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将 String 转换为 Number 及 Number 子类(Integer、Long、Double 等)对象)可使用该转换器工厂类.
- GenericConverter:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换.
可以在springmvc处理器适配器上自定义转换器:
- Converter:哪里都可以用,可以将任意数据类型转成任意数据类型
- Formatter:web层推荐使用,只能将String类型转成任意数据类型
一般使用<mvc:annotation-driven/>注解驱动加载处理器适配器,可以在此标签上进行配置。
注意:配置FormattingConversionServiceFactoryBean既可以进行类型转换,又可以进行格式化。
10.2.1 设置自定义数据转换器示例1
需求:在商品修改页面可以修改商品的生产日期,并且根据业务需求自定义日期格式。
需求分析:由于日期数据有很多种格式,springmvc没办法把字符串转换成日期类型。所以需要自定义参数绑定。前端控制器接收到请求后,找到注解形式的处理器适配器,对RequestMapping标记的方法进行适配,并对方法中的形参进行参数绑定。
修改jsp页面
- 如下图修改itemEdit.jsp页面,显示时间
- 自定义Converter:
- 配置Converter:我们同时可以配置多个的转换器。在springmvc.xml里面
10.2.2 设置自定义数据转换器示例2(将字串-->对象)
- 页面
- 转换器
- 配置转换器
- controller使用
10.2.3 自定义Formatter
转换器:
配置Formatter:
- 需要给formatter添加一个component-scan
使用Register注册Formatter
- 有了Register就不需要在SpringMVC的配置文件中注册Formatter了,只需要注册Register即可。
10.2.4 关于 mvc:annotation-driven
- <mvc:annotation-driven /> 会自动注 册RequestMappingHandlerMapping、RequestMappingHandlerAdapter 与ExceptionHandlerExceptionResolver 三个bean。
- 还将提供以下支持:
- 支持使用 ConversionService 实例对表单参数进行类型转换
- 支持使用 @NumberFormat annotation、@DateTimeFormat 注解完成数据类型的格式化
- 支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证
- 支持使用 @RequestBody 和 @ResponseBody 注解
10.2.5 @InitBinder
- 由 @InitBinder 标识的方法,可以对 WebDataBinder 对象进行初始化。WebDataBinder 是 DataBinder 的子类,用于完成由表单字段到 JavaBean 属性的绑定
- @InitBinder方法不能有返回值,它必须声明为void
- @InitBinder方法的参数通常是是 WebDataBinder
10.3 数据格式化
- 对属性对象的输入/输出进行格式化,从其本质上讲依然 属于 “类型转换” 的范畴。
- Spring 在格式化模块中定义了一个实现 ConversionService 接口的FormattingConversionService 实现类,该实现类扩展了 GenericConversionService,因此它既具有类型转换的功能,又具有格式化的功能
- FormattingConversionService 拥有一个 FormattingConversionServiceFactroyBean 工厂类,后者用于在 Spring 上下文中构造前者
- FormattingConversionServiceFactroyBean • 内部已经注册了 :
- NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性 使用 @NumberFormat 注解
- JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型的属性使用 @DateTimeFormat 注解
- 装配了 FormattingConversionServiceFactroyBean 后,就可以在 Spring MVC 入参绑定及模型数据输出时使用注解驱动了。<mvc:annotation-driven/> 默认创建的ConversionService 实例即FormattingConversionServiceFactroyBean。使用这个bean,既可以进行数据转换,又可以进行格式化。
10.3.1 日期格式化
- @ 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:完整日期/时间格式、-:忽略日期或时间格式
10.3.2 数值格式化
- @NumberFormat 可对类似数字类型的属性进行标注,它拥有两个互斥的属性:
- style:类型为 NumberFormat.Style。用于指定样式类型,包括三种:Style.NUMBER(正常数字类型)、Style.CURRENCY(货币类型)、 Style.PERCENT(百分数类型)
- pattern:类型为 String,自定义样式,如patter="#,###";
10.3.3 格式化示例
bean的注解标识:
- controller层处理页面数据(使用BindingResult)
- 配置文件的设置
10.4 JSR 303
- JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架, 它已经包含在 JavaEE 6.0 中 .
- JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证
10.4.1 Hibernate Validator 扩展注解
Hibernate Validator 是 JSR 303 的一个参考实现,除支持 所有标准的校验注解外,它还支持以下的扩展注解
- Spring 4.0 拥有自己独立的数据校验框架,同时支持 JSR 303 标准的校验框架。
- Spring 在进行数据绑定时,可同时调用校验框架完成数据校 验工作。在 Spring MVC 中,可直接通过注解驱动的方式进行数据校验
- Spring 的 LocalValidatorFactroyBean 既实现了 Spring 的Validator 接口,也实现了 JSR 303 的 Validator 接口。只要在 Spring 容器中定义了一个LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean 中。
- Spring 本身并没有提供 JSR303 的实现,所以 必须将JSR303 的实现者的 jar 包放到类路径下。
- <mvc:annotation-driven/> 会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @valid 注解即可让 Spring MVC 在完成数据绑定后执行数据校验的工作
- 在已经标注了 JSR303 注解的表单/命令对象前标注一个 @Valid,Spring MVC 框架在将请求参数绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验
- Spring MVC 是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存到随后的入参中,这个保存校验结果的入参必须是 BindingResult 或Errors 类型,这两个类都位于org.springframework.validation 包中
- 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参
- Errors 接口提供了获取错误信息的方法,如 getErrorCount() 或getFieldErrors(String field)
- BindingResult 扩展了 Errors 接口
10.4.2 在目标方法中获取校验结果
- 在表单/命令对象类的属性中标注校验注解,在处理方法对应的入参前添加 @Valid,Spring MVC 就会实施校验并将校验结果保存在被校验入参对象之后的 BindingResult 或Errors 入参中。
- 常用方法:
- FieldError getFieldError(String field)
- List<FieldError> getFieldErrors()
- Object getFieldValue(String field)
- Int getErrorCount()
10.4.3 在页面上显示错误
- Spring MVC 除了会将表单/命令对象的校验结果保存到对 应的 BindingResult 或 Errors 对象中外,还会将所有校验结果保存到 “隐含模型”
- 即使处理方法的签名中没有对应于表单/命令对象的 结果入参,校验结果也会保存在 “隐含对象” 中。
- 隐含模型中的所有数据最终将通过 HttpServletRequest 的属性列表暴露给 JSP 视图对象,因此在 JSP 中可以获取
- 错误信息在 JSP 页面上可通过 <form:errors path=“userName”>显示错误消息
示例:
10.4.4 提示消息的国际化
- 每个属性在数据绑定和数据校验发生错误时,都会生成一 个对应的 FieldError 对象。
- 当一个属性校验失败后,校验框架会为该属性生成 4 个消 息代码,这些代码以校验注解类名为前缀,结合modleAttribute、属性名及属性类型名生成多个对应的消息代码:例如 User 类中的 password 属性标准了一个 @Pattern 注解当该属性值不满足 @Pattern 所定义的规则时, 就会产生以下 4个错误代码:
- Pattern.user.password
- Pattern.password
- Pattern.java.lang.String
- Pattern
示例:
- 当使用 Spring MVC 标签显示错误消息时, Spring MVC 会查看WEB 上下文是否装配了对应的国际化消息,如果没有,则显示默认的错误消息,否则使用国际化消息。
- 若数据类型转换或数据格式转换时发生错误,或该有的参 数不存在,或调用处理方法时发生错误,都会在隐含模型中创建错误消息。其错误代码前缀说明如下:
- required:必要的参数不存在。如 @RequiredParam(“param1”) 标注了一个入参,但是该参数不存在
- typeMismatch:在数据绑定时,发生数据类型不匹配的问题
- methodInvocation:Spring MVC 在调用处理方法时发生了错误
- 注册国际化资源文件
- 国际化资源书写规则文件示例:
11 处理 JSON:使用 HttpMessageConverter
11.1 处理 JSON
1. 加入 jar 包:
2. 编写目标方法,使其返回 JSON 对应的对象或集合
3. 在方法上添加 @ResponseBody 注解
4. 页面Ajax请求和和回调函数的处理:
11.2 HttpMessageConverter<T>
- HttpMessageConverter<T> 是 Spring3.0 新添加的一个接 口,负责将请求信息转换为一个对象(类型为 T),将对象(类型为 T)输出为响应信息
- HttpMessageConverter<T>接口定义的方法:
- Boolean canRead(Class<?> clazz,MediaType – mediaType): 指定转换器可以读取的对象类型,即转换器是否可将请求信息转换为 clazz 类型的对象,同时指定支持 MIME 类型(text/html,applaiction/json等)
- Boolean canWrite(Class<?> clazz,MediaType mediaType):指定转换器是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型在MediaType 中定义。
- LIst<MediaType> getSupportMediaTypes():该转换器支持的媒体类型。
- T read(Class<? extends T> clazz,HttpInputMessage inputMessage– ):将请求信息流转换为 T 类型的对象。
- void write(T t,MediaType contnetType,HttpOutputMessgae outputMessage):将T类型的对象写到响应流中,同时指定相应的媒体类型为 contentType。
- 使用 HttpMessageConverter<T> 将请求信息转化并绑定到处理方法的入 参中或将响应结果转为对应类型的响应信息,Spring 提供了两种途径:
- 使用 @RequestBody / @ResponseBody – 对处理方法进行标注
- 使用 HttpEntity<T> / ResponseEntity<T> 作为处理方法的入参或返回值
- 当控制器处理方法使用到 @RequestBody/@ResponseBody •或HttpEntity<T>/ResponseEntity<T> 时, Spring 首先根据请求头或响应头的Accept 属性选择匹配的 HttpMessageConverter, 进而根据参数类型或泛型类型的过滤得到匹配的 HttpMessageConverter, 若找不到可用的HttpMessageConverter 将报错
- @RequestBody 和 @ResponseBody 不需要成对出现
@RequestBody、@ResponseBody示例
文件下载示例:ResponseEntity<T>
页面请求:
12 国际化
关于国际化:
1. 在页面上能够根据浏览器语言设置的情况对文本(不是内容), 时间, 数值进行本地化处理
2. 可以在 bean 中获取国际化资源文件 Locale 对应的消息
3. 可以通过超链接切换 Locale, 而不再依赖于浏览器的语言设置情况
解决:
1. 使用 JSTL 的 fmt 标签
2. 在 bean 中注入 ResourceBundleMessageSource 的示例, 使用其对应的 getMessage 方法即可
3. 配置 LocalResolver 和 LocaleChangeInterceptor
实现方式一:通过自己设置浏览器的语言实现国际化切换
实现方式二:通过超链接获取locale对象实现国际化切换
1. 页面设置超链接传递locale,超链接的hreaf=“自身的路径”
2. 在springmvc.xml配置SessionLocaleResolver 和LocaleChangeInterceptor
3.在handler中还可以请求获取信息
点击i18n1.jsp页面的中英文切换,控制台输出:
内部原理:
- 默认情况下,SpringMVC 根据 Accept-Language 参数 判断客户端的本地化类型。
- 当接受到请求时,SpringMVC 会在上下文中查找一个本 地化解析器(LocalResolver),找到后使用它获取请求所对应的本地化类型信息。
- SpringMVC还允许装配一个动态更改本地化类型的拦截器,这样通过指定一个请求参数就可以控制单个请求的本地化类型。
SessionLocaleResolver & LocaleChangeInterceptor 工作原理
本地化解析器和本地化拦截器:
- AcceptHeaderLocaleResolver:根据 HTTP 请求头的Accept-Language 参数确定本地化类型,如果没有显式定义本地化解析器, SpringMVC 使用该解析器。
- CookieLocaleResolver:根据指定的 Cookie 值确定本地化 类型
- SessionLocaleResolver:根据 Session 中特定的属性确定本 地化类型
- LocaleChangeInterceptor:从请求参数中获取本次请求对应的本地化类型。
13 文件上传
- Spring MVC 为文件上传提供了直接的支持,这种支持是通 过即插即用的 MultipartResolver 实现的。Spring 用Jakarta Commons FileUpload 技术实现了一个MultipartResolver 实现类:CommonsMultipartResovler
- Spring MVC 上下文中默认没有装配 MultipartResovler,因 此默认情况下不能处理文件的上传工作,如果想使用 Spring 的文件上传功能,需现在上下文中配置 MultipartResolver
配置 MultipartResolver:
- defaultEncoding: 必须和用户 JSP 的 pageEncoding 属性 一致,以便正确解析表单的内容
- 为了让 CommonsMultipartResovler 正确工作,必须先将 Jakarta Commons FileUpload 及 Jakarta Commons io的类包添加到类路径下。
文件上传示例:
1. springmvc.xml配置:
2.前端页面:
3.handle处理:
14 拦截器
14.1 自定义拦截器
- Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口
- preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
- postHandle():这个方法在业务处理器处理完请求后,但 是DispatcherServlet 向客户端返回响应前被调用,在该方法中对用户请求request进行处理。
- afterCompletion():这个方法在 DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
拦截器实例:
public class FirstInterceptor implements HandlerInterceptor {
/**
* 该方法在目标方法之前被调用.
* 若返回值为 true, 则继续调用后续的拦截器和目标方法.
* 若返回值为 false, 则不会再调用后续的拦截器和目标方法.
*
* 可以考虑做权限. 日志, 事务等.
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("[FirstInterceptor] preHandle");
return true;
}
/**
* 调用目标方法之后, 但渲染视图之前.
* 可以对请求域中的属性或视图做出修改.
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("[FirstInterceptor] postHandle");
}
/**
* 渲染视图之后被调用. 释放资源
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("[FirstInterceptor] afterCompletion");
}
}
拦截器配置:
单个拦截器的执行顺序:
两个拦截器的执行顺序:
第二个拦截器的preHandle方法返回false时的执行顺序:
总结:
preHandle按拦截器定义顺序调用
postHandler按拦截器定义逆序调用
afterCompletion按拦截器定义逆序调用
postHandler在拦截器链内所有拦截器返成功调用
afterCompletion只有preHandle返回true才调用
15 异常处理
- 系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
- 系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:
- Spring MVC 通过 HandlerExceptionResolver 处理程序 的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。
- SpringMVC 提供的 HandlerExceptionResolver 的实现类
DispatcherServlet 默认装配的 HandlerExceptionResolver :
- 没有使用 <mvc:annotation-driven/> 配置:
- 使用了 <mvc:annotation-driven/> 配置:
15.1 ExceptionHandlerExceptionResolver的使用
- 主要处理 Handler 中用 @ExceptionHandler 注解定义的方法。
- @ExceptionHandler 注解定义的方法优先级问题:例如发生的是NullPointerException,但是声明的异常有RuntimeException 和 Exception,此候会根据异常的最近继承关系找到继承深度最浅的那个 @ExceptionHandler注解方法,即标记了 RuntimeException 的方法
- ExceptionHandlerMethodResolver 内部若找不到@ExceptionHandler 注解标记的方法的话,会找@ControllerAdvice 中的@ExceptionHandler 注解方法
1. 在 @ExceptionHandler 方法的入参中可以加入 Exception 类型的参数, 该参数即对应发生的异常对象
2. @ExceptionHandler 方法的入参中不能传入 Map. 若希望把异常信息传导页面上, 需要使用 ModelAndView 作为返回值
3. @ExceptionHandler 方法标记的异常有优先级的问题.
4. @ControllerAdvice: 如果在当前 Handler 中找不到 @ExceptionHandler 方法来出来当前方法出现的异常,
则将去 @ControllerAdvice 标记的类中查找 @ExceptionHandler 标记的方法来处理异常.
实例:数学异常
1 前端页面
2. handler处理
- 接收页面请求的方法
-
处理异常的方法
3 也可以另外创建一个类,实现全局异常的捕捉,类上需要使用 @ControllerAdvice注解
15.2 ResponseStatusExceptionResolver的使用
- 在异常及异常父类中找到 @ResponseStatus 注解,然 后使用这个注解的属性进行处理。
- 定义一个 @ResponseStatus 注解修饰的异常类
- 若在处理器方法中抛出了上述异常:若ExceptionHandlerExceptionResolver 不解析述异常。由于触发的异常 UnauthorizedException 带有@ResponseStatus注解。因此会被ResponseStatusExceptionResolver 解析到。最后响应HttpStatus.UNAUTHORIZED 代码给客户端。HttpStatus.UNAUTHORIZED 代表响应码401,无权限。关于其他的响应码请参考 HttpStatus 枚举类型源码。
使用实例
1. 页面:
2. 接收页面请求的handler方法
3 .定义一个 @ResponseStatus 注解修饰的异常类,处理异常
页面请求后的结果:
4.把@ResponseStatus 注解放在handler方法上
i 不等于10时页面的请求结果:
15.3 DefaultHandlerExceptionResolver
使用无需其他配置,默认包含
测试方法不支持异常:
1.页面发送get请求
2.目标方法限定为post
异常信息:
15.4 SimpleMappingExceptionResolver
如果希望对所有异常进行统一处理,可以使用 SimpleMappingExceptionResolver,它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常。【将指定的异常和显示页面进行绑定】
实例:
1.请求页面:
2.接收请求的handler
3.异常配置页面
3.配置的errors.jsp显示异常信息
显示结果:
16 Spring和SpringMVC
需要进行 Spring 整合 SpringMVC 吗 ? 还是否需要再加入 Spring 的 IOC 容器 ?
是否需要再 web.xml 文件中配置启动 Spring IOC 容器的 ContextLoaderListener ?
1. 需要: 通常情况下, 类似于数据源, 事务, 整合其他框架都是放在 Spring 的配置文件中(而不是放在 SpringMVC 的配置文件中).
实际上放入 Spring 配置文件对应的 IOC 容器中的还有 Service 和 Dao.
2. 不需要: 都放在 SpringMVC 的配置文件中. 也可以分多个 Spring 的配置文件, 然后使用 import 节点导入其他的配置文件
问题: 若 Spring 的 IOC 容器和 SpringMVC 的 IOC 容器扫描的包有重合的部分, 就会导致有的 bean 会被创建 2 次.
解决:
1. 使 Spring 的 IOC 容器扫描的包和 SpringMVC 的 IOC 容器扫描的包没有重合的部分.
2. 使用 exclude-filter 和 include-filter 子节点来规定只能扫描的注解
Spring 的 IOC 容器不应该扫描 SpringMVC中的 bean, 对应的SpringMVC 的 IOC 容器不应该扫描 Spring 中的 bean
- SpringMVC的IOC容器springmvc.xml配置:只扫描指定的注解
- Spring的IOC容器beans.xml配置:不扫描指定的注解
在 Spring MVC 配置文件中引用业务层的 Bean:
- 多个 Spring IOC 容器之间可以设置为父子关系, 以实现良好的解耦。
- Spring MVC WEB 层容器可作为 “业务层” Spring容器的子容器:
- SpringMVC 的 IOC 容器中的 bean 可以来引用 Spring IOC 容器中的 bean.
- 返回来呢 ? 反之则不行. Spring IOC 容器中的 bean 却不能来引用 SpringMVC IOC 容器中的 bean!
17 SpringMVC 对比 Struts2
①. Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter
②. Spring MVC 会稍微比 Struts2 快些. Spring MVC 是基于方法设计, 而 Sturts2 是基于类, 每次发一次请求都会实
例一个 Action.
③. Spring MVC 使用更加简洁, 开发效率Spring MVC确实 比 struts2 高: 支持 JSR303, 处理 ajax 的请求更方便
④. Struts2 的OGNL 表达式使页面的开发效率相比Spring MVC 更高些.