SpringMVC
1. 概述:
MVC是一个表现层框架,通过注解让POJO【简单的Java对象】成为请求处理的控制器,且无需实现任何接口。支持REST风格的URL请求
优略分析:
SpringMVC与Struts2的优略分析
1.1 B/S基本流程
[外链图片转存
-
客propagation户端请求提交到DispatcherServlet
-
由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
-
DispatcherServlet将请求提交到Controller(也称为Handler)
-
Controller调用业务逻辑处理后,返回ModelAndView
-
DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
-
视图负责将结果显示到客户端
SpringMVC中的主要组件:
- DispatcherServlet :前端控制器
- 根据自定义的拦截规则,将拦截下的请求,根据相应规则分发到目标Coltroller处理
(简单来说,就时将拦截下的请求路径交给不同的servlet处理)
- 根据自定义的拦截规则,将拦截下的请求,根据相应规则分发到目标Coltroller处理
- Controller :页面控制器
- 做MVC中C的事情,(可能相当于上面前端控制器交予的Servlet)用于对请求进行处理
- HandlerMapping :请求映射到处理器
- 请求映射到处理器,找谁来处理,如果映射成功返回一个HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器对象)
- HandlerAdaptor :处理转换器
- View Resolver :视图解析器
- 把逻辑视图解析为具体的VIew,
- 如InternalResourceViewResolver将逻辑视图名映射为JSP视图
- 把逻辑视图解析为具体的VIew,
- MultipartResolver :文件上传解析器
- HandlerExceptionResolver :异常处理器
1.2SpringMVC简单入门
-
创建Web环境,导入jar包(Spring核心包和web包)
- spring-beans-5.3.1.jar
- spring-context-5.3.1.jar
- spring-core-5.3.1.jar
- spring-expression-5.3.1.jar
- spring-jcl-5.3.1.jar
- spring-aop-5.3.1.jar
- spring-web-5.3.1.jar
- spring-webmvc-5.3.1.jar
-
在web.xml文件中配置前端控制器DispatcheServlet
- 需要在标签中通过子标签设置SpringMVC的配置路径
- 如果不配置,就会默认寻找【/WEB-INF/标签里配置的值-servlet.xml】
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--配置前端处理器--> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--配置 DispatcherServlet 的初始化参数:设置springMVC文件的路径和文件抿成--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <!--设置为服务器启动时创建--> <load-on-startup>1</load-on-startup> </servlet> <!--设置访问所有非jsp路径的--> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <!--此处设置 【/】 是为了替换tomcat中的默认行为 --> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
-
在src目录下配置Springmvc.xml
-
配置自动扫描的包
-
视图解析器【InternalResourceViewResolver】
到时候转发的路径网页为:前缀 + 转发过来的数据+.jsp-
配置前缀:prefix
-
配置后缀:suffix
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 配置自动扫描的包--> <context:component-scan base-package="com.atguigu.springmvc"></context:component-scan> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 配置前缀--> <property name="prefix" value="/WEB-INF/views/"></property> <!-- 配置后缀--> <property name="suffix" value=".jsp"></property> </bean> </beans>
-
-
-
在首页添加一个超链接
<a href="${pageContext.request.contextPath}/helloworld">Hello SpringMVC</a>
-
创建控制器/处理器(Controller)
-
在类上添加注解@Contorller
-
在方法上添加注解@RequsetMapping
-
value属性为超链接中传过来的请求路径
package com.atguigu.springmvc; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HelloWorld { @RequestMapping(value = "/helloworld") public String helloWorld(){ System.out.println("Hello SpringMVC!"); return "success"; } }
-
-
-
在前缀定义的相应位置创建success.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>成功页面</title> </head> <body> <h1>请求成功!</h1> </body>
-
部署tomcat测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XrW3VWiG-1617632806119)(C:\Users\罗先生\AppData\Roaming\Typora\typora-user-images\1615206892128.png)]
2. @RequestMapping注解
2.1 映射普通请求地址
-
可以添加的位置
- 类上:添加到类上的信息,相当于标记类中的根目录
- 方法上:提供进一步细分的映射信息
-
可以配置的属性
-
value/path:用来设置要映射URL的【String数组】
- 花括号可以省略 {“/fingOne”,“/findTwo”}
- 如果只有value属性,则属性名可以省略
-
method:用来设置映射URL可以处理的请求方式
- 如果该属性没有指定,那么将只考虑请求地址,不考虑请求方式
- 可以这只GET、POST等
-
params:过滤指定参数的请求
-
设置URL必须包含的请求参数
-
值为String类型数组,
- 如果某一元素中只有名称,则必须包含
- 如果某一元素为一个简单表达式(age=18)那么请求参数必须有18的一个age才可以
-
可以使用【= 、!=、!xxx=xx】
-
使用案例:
@RequestMapping(value = "/testParams",params ={"name=zs","age"} ) public String testParams() { System.out.println("参数必须有age,且name属性为zs"); return "success"; }
-
-
header:过滤指定请求的header参数
-
设置处理包含请求头信息的请求
-
或者请求头中某个参数的值为XXX时,才会处理请求,
- 这里的处理请求,指得是这个方法是否会执行,但是想要获取到header中的参数值,仅仅这样是不够的,还需要再方法的声明参数中,用@RequestHeader注解来获取
-
使用案例:请求头中的Accept-Language属性值必须为zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7时,才会进行处理,
@RequestMapping(value = "/testHeaders",headers = "Accept-Language=zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7") public String testHeaders(){ System.out.println("测试@RequestMapping的headers属性"); return SUCCESS; }
-
-
2.2 通配符与占位符
2.2.1 通配符如何使用
-
?:代表一个任意字符
-
* :代表多个任意字符
-
**:代表多层路径
在接受请求路径时,?可以兼容任意一个字符,一个与两个【*】同理比如下:
<a href="/testTPF/1" >/testTPF
<a href="/testTPF/d" >/testTPF -
使用案例:
@RequestMapping(value = "/testTPF/?/{id}") public String testTPF(@PathVariable("id") Integer id) { System.out.println("参数为:" + id); return "success"; }
2.2 映射带占位符的URL
@PathVariable:获取Rest风格中的占位符参数。
注意:RequestMapping中的占位符必须与方法声明中的参数名称相同,如果名称不相同,那么方法声明中的参数值为空,也可以通过required属性来指定该参数为非必须参数
-
该注解用到参数中
- value/name:用来指定占位符的名称
- required:用来设置value属性指定的占位符是否必须,默认true
-
使用案例:{id}即为占位符
@RequestMapping(value = "/hello/{id}")//此处的【id】应该与方法参数中的【id】相匹配,不然获取不到该参数的值 public String testRuest(@PathVariable(value = "id",required = false) Integer id) { return "success"; }
2.3 REST风格
简单来说就是请求中没有参数名称,直接跟相应的参数值,通过不同的提交方法,来指明为不同的操作,举例: /getBookById/1 GET请求 == /getBookById?id=1 GET请求
-
将四种请求方式对应四种操作方式
- GET:获取资源
- POST:新建资源
- PUT:更新资源
- DELETE:删除资源
-
如何发送PUT与DELETE方式的请求
-
在web.xml中通过HiddenHttpMethodFilter过滤器,解析响应的方法
-
配置过滤器
<filter> <filter-name>hiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>hiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
发送格式
<form action="${pageContext.request.contextPath}/testMethod" method="post"> <input type="hidden" name="_method" value="put"> <input type="submit" value="Test REST_Put"> </form> <form action="${pageContext.request.contextPath}/testMethod/8" method="post"> <input type="hidden" name="_method" value="delete"> <input type="submit" value="Test REST_Delete"> </form>
-
2.4 如何获取传统请求中参数
-
@RequestParam
- 该注解使用在参数中,用来获取传统请求中携带的参数
- 属性有几下几点
- value/name:用来接受请求参数的参数名
- required:设置该参数是否为必须。默认为ture
- defaultvalue属性:给当前请求参数设置默认值,如果没有请求参数的传入,则使用默认值
-
@RequestHeader:获取请求中携带的请求头信息【与上方所拥有的属性一致】
-
@CookieValue:获取请求中携带的Cookie信息【与上方所拥有的属性一致】
-
直接封装为POJO【简单对象】
- 使用要求:必须保证请求参数名与POJO的属性名保持一致
- form中的name必须与bean中的属性名一致
- bean中的属性最好使用自动生成的getter与setter
- 处理该请求的方法申明参数的类型必须与类名称一致
请求页面 <form method="post" action="${pageContext.request.contextPath}/testPOJO"> 图书数量: <input type="text" name="totalAccount"><br> 图书名称: <input type="text" name="book.title"><br> 图书作者: <input type="text" name="book.author"><br> <input type="submit" value="testPOJO"> </form> 处理页面 @RequestMapping(value = "/testPOJO", method = RequestMethod.POST) public String testPOJO(CartItem cartItem) { System.out.println(cartItem); return "success"; } bean public class CartItem { private Book book; private Integer totalAccount; ...... } public class Book { private String title; private String author; ...... }
- 使用要求:必须保证请求参数名与POJO的属性名保持一致
-
如果不使用@RequestParam注解和POJO,也可以使用原生的Servlet API作为声明参数进行操作
-
Springmvc处理器方法中接受原生的Servlet API有以下9个
- HttpServletRequest
- HttpServletResponse
- HttpSession
- java.security.Principal
- Locale
- InputStream
- OutputStream
- Reader
- Writer
3. 解决springmvc中request的post请求中文乱码问题
- 在Java web工程中的配置文件web.xml中配置一个filter
- 具体配置的类文件为:org.springframework.web.filter.CharacterEncodingFilter
- 需要初始化其中两个参数
- encoding:初始化该属性为UTF-8
- forceRequestEncoding:初始化该属性为true
<!-- 配置过滤器,用来配置POST格式的请求编码为UTF-8 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 设置encoding属性为 UTF-8 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!-- 设置 forceRequestEncoding 属性为true-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
可以查看源码如下:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String encoding = this.getEncoding();
if (encoding != null) {//当encoding不为空,就是只有初始化之后,才可以进入此判断
if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) {
//isForceRequestEncoding属性含义为:是否强制请求编码
//如果,在初始化时,将上述属性设置为true或者检测到客户端已经设置过编码,
//那么此时过滤器将不会再帮助我们重新设置编码
//getCharacterEncoding==null 即为客户端没有设置过编码
request.setCharacterEncoding(encoding);
}
if (this.isForceResponseEncoding()) {
response.setCharacterEncoding(encoding);
}
}
filterChain.doFilter(request, response);
}
4. 处理响应参数
SpringMVC提供两种途径输出模型数据:两种方法最后都会将数据添加到Request域中
- ModelAndView:
处理方法返回值类型为 ModelAndView 时, 方法体即可通过该对象添加模型数据,最终会添加到request域中。 - Map或Model或ModelMap:
入参为java.uti.Map、 org.springframework.ui.Model或org.springframework.ui.ModelMap 时,处理方法返回时,Map 中的数据会自动添加到模型中,并最终添加到request域中。
4.1 ModelAndView
当返回值为ModelAndView 时,其中包含视图信息,也包括模型数据信息
- 核心属性
- private Object view; 视图信息
- private ModelMap model; 模型数据
- 添加模型数据的方法
- ModelAndView addObject(String attributeName,Object attributeValue)
- ModelAndView addAllObject(Map<String, ?> modelMap)
- 获取模型数据的方法
- protected Map<String, Object> getModelInternal()
- public ModelMap getModelMap()
- public Map<String, Object> getModel()
- 设置视图的方法
- void setView(View view) 设置视图对象
- void setViewName(String viewName) 通过视图名字设置跳转视图
4.2 Map或Model或ModelMap:
Spring MVC 在内部使用了一个 org.springframework.ui.Model 接口存储模型数据,具体使用步骤
-
Spring MVC 在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器。
-
如果方法的入参为 Map 或 Model 类型,Spring MVC 会将隐含模型的引用传递给这些入参。
-
在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据
-
Map、Model、ModelMap 三者的关系
- ModelMap继承了LinkedHashMap
- ExtendedModelMap继承了ModelMap实现了Model接口
当使用隐含对象模型作为数据的存储容器时,在处理器处理完成之后,会将该该隐含对象模型【Map、Model、ModelMap】放入到Ruquest域中,
5. 视图解析
- 不论处理器方法返回一个String,ModelAndView还是View,Spring MVC 都会在内部将它们转换成一个 ModelAndView 对象,由视图解析器解析视图,然后,进行页面的跳转。
- Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是 JSP ,也可能是 Excel、JFreeChart等各种表现形式的视图。
5.1 视图解析器:
视图解析器的功能:
- 视图解析器的作用比较单一:将逻辑视图解析为一个具体的视图对象。
- 所有的视图解析器都必须实现 ViewResolver 接口
- 可以在 SpringMVC 上下文中配置一种或多种解析器,每个视图解析器都实现了 Ordered 接口并开放出一个 order 属性,可以通过 order 属性指定解析器的优先顺序,order 越小优先级越高。
- SpringMVC 会按视图解析器顺序的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则将抛出 ServletException 异常。
- JSP 是最常见的视图技术,可以使用 InternalResourceViewResolve作为视图解析器。
5.2 指定请求页面直接跳转
5.2.1 spring配置__mvc:view-controller标签之后
通过配置该标签,让指定的访问请求不经过调度员直接跳转到指定页面
设置标签属性:
- path属性:请求的路径
- view-name属性:跳转资源的视图名称
注意:
- 不经过处理器方法,直接转发到指定页面,
- 但会使所有的@RequestMapping标签失效,所以我们要配置如下标签
- <mvc:annotation-driven/>
5.2.2 配置静态资源处理器
在配置了DispatcherServlet后,网页中的js路径请求将会找不到处理该请求对应的处理器,想要使网页中的js路径请求与css路径请求可以使用,需要配置静态资源处理器,也就是在springxml中配置
<!-- 配置静态资源处理器-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
- 但是配置该标签之后,同样会使自定义的内部资源视图解析器【InternalResourceViewResolver】失去作用,所以,我们要想上面一样,同样才配置一个注解驱动支持【<mvc:annotation-driven/>】
5.3 转发与重定向:
一般情况下,处理器方法返回字符串类型的值会被当成逻辑视图名处理。如果返回的字符串中带 【forward: 】或【 redirect: 】前缀时,SpringMVC 会对他们进行特殊处理:将 forward: 和 redirect: 当成指示符,其后的字符串作为 URL 来处理:
处理方法返回时添加如下前缀:
-
forward:转发
-
redirect:重定向
-
注意:在添加前缀之后,系统不会自动添加后缀,所以需要我们手动配置,
-
该处的解析又服务器进行完成
-
案例如下:
return "forward:/XXX.jsp";
-
6. SpringMVC的form表单
使 编辑更加快捷、方便表单回显
导入标签库即可使用。<%@ taglib prefix=“form” uri=“http://www.springframework.org/tags/form” %>
- form:form标签
- 使用RESTFul时,get请求为获取表单页面,post请求为提交表单页面
- 如果获取表单和提交表单的URL相同,<form:form> 标签就无需通过 action 属性指定表单提交的 URL,默认提交方法为 method= post
- 默认会回显,以command为Key从request域中的查找数据
- 可以在form中使用modelAttribut属性修改command
- 表单项form:input、form:password、form:radiobutton、form:select对应 HTML input表单项的 text、password、radio和select 标签
- form:input标签中的属性
- path:表单字段,对应 html 元素的 name 属性,支持级联属性。
- htmlEscape:是否对表单值的 HTML 特殊字符进行转换,默认值为 true。
- cssClass:表单组件对应的 CSS 样式类名。
- cssErrorClass:表单组件的数据存在错误时,采取的样式
- form:radiobutton:单选框组件标签,当表单POJO对象对应的属性值和 value 值相等时,单选框被选中
- path:bean中对应的字段
- value:提交到bean中的属性值
- label:显示的名称
- form:radiobuttons:单选框组标签,用于构造多个单选框
- items:可以是一个 List、String[] 或 Map。
- itemValue:指定 radio 的 value 值。可以是集合中 bean 的一个属性值。
- itemLabel:指定 radio 的 label 值。
- 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” /> :显示特定表单对象属性的错误。
7. 请求数据转换
HttpMessageConverter 是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为 T),将对象(类型为 T)输出为响应信息。
注意:SpringMVC的配置文件中一定要配置mvc:annotation-driven标签。如果不配置将只装四个转换器。
6.1 使用方法:
- @ResponseBody:对处理方法进行注解
- 加到方法签名上
- 会直接将内容响应到浏览器中,不会经过视图处理器
- 加到方法签名上
- @RequestBody:对处理方法进行注解
- 加在方法参数值中
- 可以获取请求体的内容,且将内容转化为指定类型,如:String
- 可以将请求体中的信息转化为指定类行的java对象
- 加在方法参数值中
- 不需要称对出现
示例代码:
加载参数声明中
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String info) {
System.out.println(info);
return "success";//跳转到success界面
}
加载方法上
@ResponseBody
@RequestMapping("/testJson")
public Employee testJson() {
Employee employee = new Employee(1001, "zhangsan", "zhangsan@163.com", new Department(0001, "销售"));
return employee;//如果配置json时,就会返回json
}
- HttpEntity :作为返回值或参数
- 可以获取请求头信息
- 也可以获取请求体信息
- ResponseEntity :作为返回值或参数
- 可以将请求体中的信息转化为指定类型的java对象
- 也可以将请求头中的信息转化为指定类型的java对象
示例代码:
HttpEntity
@RequestMapping("/testHttpEntity")
public String testHttpEntity(HttpEntity<String> info) {
System.out.println("info.getBody() = " + info.getBody());
System.out.println("info.getHeaders() = " + info.getHeaders());
return "success";
}
使用ResponseEntity,进行文件下载
@ResponseBody
@RequestMapping("/testResponseBody")
public ResponseEntity testResponseBody(HttpServletRequest request) throws IOException {
String realPath = request.getServletContext().getRealPath("/download/jxs.png");
InputStream is = new FileInputStream(realPath);
byte[] bytes = new byte[is.available()];
is.read(bytes);
MultiValueMap<String, String> map = new HttpHeaders();
map.set("content-disposition", "attachment;filename=机械师.png");
HttpStatus httpStatus = HttpStatus.OK;
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, map, httpStatus);
is.close();
return responseEntity;
}
6.2 返回JSON数据
有了HttpMessageConverter之后,使用json更加方便了
使用方法:
- 导入相关jar包
- jackson-annotations-2.10.0.jar
- jackson-core-2.10.0.jar
- jackson-databind-2.10.0.jar
- Spring 5 不能使用 jackson 2.2.3版本的jar包,不然会出现bean初始化异常
- 必须要配置【<mvc:annotation-driver>】标签
- 设置对应的实体类
- 给处理方法添加@ResponseBody注解
- 返回bean对象即可
示例代码:
@ResponseBody
@RequestMapping("/testJSON")
public Employee testJson(){
//创建Employee对象
Employee employee = new Employee(1, "zhangsan", "zhangsan@atguigu.com", 1, new Department(1001, "Teacher"));
return employee;//此处返回的就是json对象,可以在jsp页面中直接【点】
}
6.3 【<mvc:annotation-driver>】标签
在配置了静态资源处理器或指定页面跳转的标签之后,会使一些功能失效,比如 :DispatcherServlet中装配的handlerAdapters变少等等,所以我们要配置<mvc:annotation-driver>该标签。建议每个工程都有
- <mvc:view-controller/>:指定页面跳转标签
- <mvc:default-servlet-handler/>:静态资源处理器
8. 拦截器
如何自定义拦截器
创建自定义拦截器类
- 通过实现接口【HandlerInterceptor】
- 通过集成类【HandlerInterceptorAdapter】
重写拦截器中的三个方法
-
请求前调用【preHandle】
-
返回为ture:放行,但还是会被其他指定的拦截器拦截
-
返回为false:直接拦截,不会继续进行
-
-
处理器处理完成后,渲染视图前【postHandle】
-
请求后调用,也就是视图渲染后【afterCompletion】
package com.atguigu.springmvc.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FirstInterceptor implements HandlerInterceptor {
@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()......");
}
}
拦截器的拦截顺序为springmvc.xml中的配置顺序
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 配置拦截器们-->
<mvc:interceptors>
<!-- 该拦截器可以拦截所有请求-->
<bean id="interceptor" class="com.atguigu.springmvc.interceptor.FirstInterceptor"></bean>
<!-- 配置单个路径拦截器-->
<mvc:interceptor>
<mvc:mapping path="/testInterceptor2"/>
<!--配置不拦截的路径-->
<!-- <mvc:exclude-mapping path="/testInterceptor"/>-->
<bean class="com.atguigu.springmvc.interceptor.SecondInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
多个拦截器执行流程图
9. 异常处理
默认异常处理器 HandlerExceptionResolver 中有三个对象
通过在spring.xml种配置【SimpleMappingExceptionResolver】bean
使用步骤:
- 在SpringMVC的配置文件中配置SimpleMappingExceptionResolver
<!-- 配置异常跳转至指定页面-->
<bean id="simpleMappingExceptionResolver"
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--该种错误,渲染到error视图页面种-->
<prop key="ArithmeticException">error</prop>
</props>
</property>
<!--设置向request域中存放异常信息的key,默认是exception-->
<property name="exceptionAttribute" value="e"></property>
</bean>
- 创建处理器方法
@RequestMapping("/testSimple")
public String testSimpleMappingExceptionResolver(@RequestParam("i") Integer i){
System.out.println("测试SimpleMappingExceptionResolver");
int result = 10 / i;
System.out.println(result);
return "success";
}
-
测试路径
<a href="${pageContext.request.contextPath}/testSimple?i=0">测试异常</a>
-
创建视图页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>错误页面</title> </head> <body> <h1>异常信息是:${requestScope.e}</h1> </body> </html>
-
页面信息
10. SpringMVC运行流程
- 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。也就是被mvc的调度员所劫持,等待调度员的派遣。
- DispatcherServlet对请求URL进行解析,得到请求资源标识符,判断该URI是否存在
- 如果不存在,
- 查看是否配置静态资源处理器【mvc:default-servlet-handler】
- 如果没有配置,控制台报映射查不到,客户端404
- 如果有配置,会去直接访问该资源,如果没有找到依旧会报404,但是这时的404会告诉你找不到的路径
- 如果存在,
- 根据该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(…)方法【倒叙】。
- 将渲染结果返回给客户端。
- 如果不存在,
执行流程图:
11. Spring整合Springmvc
两种解决方法,
第一种:
- 不整合:
- 将所有的配置都配置到Springmvc的配置文件中
- 也可以创建多个配置文件,将配置文件放入到Springmvc的配置文件中
第二种:
-
整合:
- Spring负责管理Dao、Service、数据源及其他的第三方框架等。
- Springmvc负责controller、视图解析器、静态资源等。
-
但是依旧存在两个问题:
- 问题一:Spring的配置文件如何初始化
- 解决:
- 在Java工程中,ioc容器为手动创建
- ApplicationContext ioc = new ClassPathXmlApplicationContext(“XXX.xml“)
- 在Web工程中,可以在web.xml中配置一个监听器【ContextLoaderListener】,用来加载Spring的配置文件
- 在Java工程中,ioc容器为手动创建
- 解决:
- 问题二:Controller与Service层的Bean被创建了两次
- 解决:
- 让Springmvc只扫描controller
- 让Spring不扫描controller
- 解决:
- 问题一:Spring的配置文件如何初始化
在Spring与Springmvc整合当中web.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置 CharacterEncodingFilter 用来处理post请求乱码问题-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</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>
<!-- 过滤所有非jsp路径请求-->
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置上下文的初始化参数,用来注入spring-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<!-- 配置 ContextLoaderListener 监听器用来加载Spring配置文件-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
在Spring与Springmvc整合当中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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--设置扫描的controller-->
<context:component-scan base-package="com.atguigu.springmvc.ss" use-default-filters="false">
<!--让Springmvc只扫描controller-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--视图解析器-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--配置静态文件解析器-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<!--配置注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
在Spring与Springmvc整合当中spring.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--不扫描Controller东西-->
<context:component-scan base-package="com.atguigu.springmvc.ss">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--引入druid配置文件-->
<context:property-placeholder location="classpath:druid.properties"></context:property-placeholder>
<!--配置数据源-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"/>
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="password" value="${jdbc.password}"/>
<property name="initialSize" value="${jdbc.initialSize}"/>
<property name="minIdle" value="${jdbc.minIdle}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
<property name="maxWait" value="${jdbc.maxWait}"/>
</bean>
<!-- 配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="druidDataSource"></property>
</bean>
</beans>
DuridDataSource配置文件内容
#MySQL 8.0
#key=value
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/bookstore1214?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true
#url=jdbc:mysql://localhost:3306/mysqldb
jdbc.username=root
jdbc.password=root
jdbc.initialSize=10
jdbc.minIdle=5
jdbc.maxActive=20
jdbc.maxWait=5000