一.SpringMVC框架整理学习
1.SpringMVC-框架概述
📕 什么是SpringMVC ?
- SpringMVC是Spring的子模块,为展现层提供的基于MVC设计理念的优秀的web框架,是目前主流的MVC框架之一。
- SpringMVC通过一套MVC注解,让POJO成为处理请求的控制器,而无须实现任何接口。
- 支持REST风格的URL请求
- 采用了松散耦合可拔插的组件结构,比其他MVC框架更具扩展性和灵活性。
- SpringMVC是Servlet的轻量级封装,在控制层使用控制器Controller代理传统的JavaWeb开发中的Servlet或Struts框架中的Action
📕 什么是MVC ?
- MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。
- 将业务逻辑、数据、显示分离的方法来组织代码。降低了视图和业务逻辑的双向耦合。
- MVC是一种架构模式,不同的MVC存在不同的差异。
2. SpringMVC-搭建开发环境
🎯 创建基于Maven的JavaWeb项目TestSpringMVC
🎯 在pom文件中添加jar包的依赖
<!-- Spring Context Spring核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- Spring Web 包含eb应用开发时,用到的Spring框架时所需的核心类 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- Spring MVC 实现SpringMVC的操作-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- Servlet API 对于Servlet的开发环境支持 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- JSP API Jsp的支持-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<!-- JSTL JSP标签库-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
🎯 在web.xml中添加SpringMVC的前端控制器的配置
<!--SpringMVC的前端控制器,用来处理全部的HTTP请求-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--指定配置文件的位置和名称,classpath:表示从项目的类路径开始开始-->
<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>
<url-pattern>/</url-pattern>
</servlet-mapping>
【注意】如果不指定contextConfigLocation,则配置文件名称默认为servlet名称-servlet.xml,而且文件位置必须在WEB-INF文件夹下
🎯 在web.xml中添加POST请求中文乱码过滤器
SpringMVC内置了一个处理POST请求中文乱码的过滤器CharacterEncodingFilter
【位置】spring-web.jar中的org.springframework.web.filter包中
<!--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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingfilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
🎯 创建springmvc.xml的配置文件添加context和mvc的名称空间,以及注解驱动的相关配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
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.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<!--开启注解组件扫描-->
<context:component-scan base-package="com.wisedu.controller"/>
<!--处理器映射器
<bean class="org.springframewok.web.servlet.handler.BeanNameUrlHandlerMapping"/>
处理器适配器
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
-->
<!--视图渲染器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--开启MVC注解扫描 ,一般来说我们只需要配置视图解析器,通常,我们只需要手动配置视图解析器, 而处理器映射器和处理器适配器只需要开启注解驱动即可-->
<mvc:annotation-driven/>
</beans>
🎯 mvc:annotation-driven/标签作用
-
开启注解驱动
( 1 )自动向SpringIOC容器中注册RequestMappingHandlerMapping类的Bean和RequestMappingHandlerAdapter类的Bean
( 2 )HandlerMapping接口的实现类RequestMappingHandlerMapping处理url请求的映射器,解析@RequestMapping注解,并将其注册到请求映射表中
( 3 )HandlerAdapter接口的实现类RequestMappingHandlerAdapter处理请求的适配器,确定调用哪个处理器的哪个方法
-
其他功能
( 1 )支持使用ConversionService实例对表单参数进行类型转换
( 2 )支持使用@NumberFormat注解和@DataTimeFormat注解完成数据类型的格式化
( 3 )支持使用@Valid注解进行JSR303数据校验
( 4 )支持使用@RequestBody和@ResponseBody注解处理JSON数据
( 5 )自动注册JSON数据解析的适配器HttpMessageConverter
2.1 @RequestBody和@ResponseBody详解
@RequestBody
作用:
i) 该注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上;
ii) 再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上。
使用时机:
A) GET、POST方式提时, 根据request header Content-Type的值来判断:
- application/x-www-form-urlencoded, 可选(即非必须,因为这种情况的数据@RequestParam, @ModelAttribute也可以处理,当然@RequestBody也能处理);
- multipart/form-data, 不能处理(即使用@RequestBody不能处理这种格式的数据);
- 其他格式, 必须(其他格式包括application/json, application/xml等。这些格式的数据,必须使用@RequestBody来处理);
B) PUT方式提交时, 根据request header Content-Type的值来判断:
- application/x-www-form-urlencoded, 必须;
- multipart/form-data, 不能处理;
- 其他格式, 必须;
说明:request的body部分的数据编码格式由header部分的Content-Type指定;
@ResponseBody
@responseBody注解的作用是将controller的方法返回的对象通过适当的HttpMessageConverter转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML
数据,需要注意的呢,在使用此注解之后不会再走试图处理器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。
💨如果返回的类型为String,会造成乱码。
【原因】
不同的数据类型,前后端交互的时候通过指定的HttpMessageConverter转换格式,String数据类型对应的HttpMessageConverter的默认编码格式为 ISO-8859-1【解决方案第二节HttpMessageConverter中解决】
2.2 HttpMessageConverter 消息转换器
@RequestBody、@ResponseBody注解,可以直接将输入解析成Json、将输出解析成Json,但HTTP 请求和响应是基于文本的,意味着浏览器和服务器通过交换原始文本进行通信,而这里其实就是HttpMessageConverter发挥着作用。
Http请求响应报文其实都是字符串,当请求报文到java程序会被封装为一个ServletInputStream流,开发人员再读取报文,响应报文则通过ServletOutputStream流,来输出响应报文。
从流中只能读取到原始的字符串报文,同样输出流也是。那么在报文到达SpringMVC / SpringBoot和从SpringMVC / SpringBoot出去,都存在一个字符串到java对象的转化问题。这一过程,在SpringMVC / SpringBoot中,是通过HttpMessageConverter来解决的。HttpMessageConverter接口源码:
public interface HttpMessageConverter<T> {
//可读
boolean canRead(Class<?> clazz, MediaType mediaType);
//可写
boolean canWrite(Class<?> clazz, MediaType mediaType);
//获取支持的媒体类型
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
下面以一例子来说明:
@RequestMapping("/test")
@ResponseBody
public String test(@RequestBody String param) {
return "param '" + param + "'";
}
在请求进入test方法前,会根据@RequestBody注解选择对应的HttpMessageConverter实现类来将请求参数解析到param变量中,因为这里的参数是String类型的,所以这里是使用了StringHttpMessageConverter类,它的canRead()方法返回true,然后read()方法会从请求中读出请求参数,绑定到test()方法的param变量中。
同理当执行test方法后,由于返回值标识了@ResponseBody,SpringMVC / SpringBoot将使用StringHttpMessageConverter的write()方法,将结果作为String值写入响应报文,当然,此时canWrite()方法返回true。
HttpMessageConverters的实现类:
StringHttpMessageConverter问题场景:
<!-- 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>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在web.xml中设置了POST请求过滤器以后,但是Controller中返回类型为String字符串转变为JSON格式,此时会出现???乱码
通过源码分析:
public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {
public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");
String字符串转JSON格式使用的转换器是StringHttpMessageConverters
默认使用的编码及是IOS--8859-1;所以出现乱码格式。我们可以设置里面的属性supportedMediaTypes来修改编码格式:
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
2.3 DispatcherServlet的执行原理
(1) DispatcherServlet表示前端控制器,是整个SpringMVC的控制中心。用户发起请求,DispatcherServlet都会接收并拦截请求
·我们假设请求url地址为:http://localhost:8080/SpringMVC/hello
·如上url拆分成三个部分:
·http://localhost:8080为服务器域名
·SpringMVC部署在服务器上的web站点
·hello表示控制器
·通过分析,如上url表示为:请求位于localhost:8080上的SpringMVC站点的hello控制器
(2) HandlerMapping为处理器映射器。DispatcherServlet调用HandlerMapping,HandlerMapping根据url请求查找Handler
(3) HandlerExecution表示具体的Handler,其作用是根据url查找控制器,如上url被查找控制器是:hello
(4) HandlerExecution将解析后的信息传递给DispatcherServlet
(5) HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler
(6) Handler让具体的Controller执行
(7) Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView
(8) HandlerAdapter将视图逻辑名或模型传输给DispatcherServlet
(9) DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名
(10) 视图解析器将解析的逻辑视图名传给DispatcherServlet
(11) DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图
(12) 最终视图呈现给用户。
2.4 映射请求路径
📕 @RequestMapping注解
使用@RequestMapping注解映射请求url,根据url查找匹配的控制器及其内部的业务方法
-
value属性
【作用】用于对url进行分类管理,以免出现url冲突,url的完整格式为"/项目名/模块名/方法名"
( 1 ) @RequestMapping用在类上时对应的业务的模块名,提供初步的请求映射,即所有请求方法都以该地址作为父路径
( 2 ) @RequestMapping用在方法上时对应具体的业务方法,提供更加具有(窄化)的请求映射,即所有的请求方法是以该地址作为子路径
注意 : 如果类上没有标注@RequestMapping注解,只是在方法上使用@RequestMapping,则方法上的url相对于项目的根路径
//写法1 @RequestMapping("/user") //@RequestMapping用在类上,value属性写模块的名称 public class UserController { @RequestMapping("/login") //@RequestMapping用在方法上,value属性写具体操作的名称 public void login(){ System.out.println("login()..."); } @RequestMapping("/reg") public void reg(){ System.out.println("reg()..."); } } //写法2 public class UserController { @RequestMapping("/user/login") //只在方法上使用@RequestMapping注解,value属性写完整路径 /模块名/方法名 public void login(){ System.out.println("login()..."); } @RequestMapping("/user/reg") public void reg(){ System.out.println("reg()..."); } }
-
method属性 ---限定HTTP的请求方式类型
@RequestMapping(method={RequestMethod.POST, RequestMethod.GET})
-
Params属性 --- 限定访问url中必须包含的参数名和参数值 了解
@RequestMapping(value="/login", params={"username", "password!=123"})
-
@RequestMapping注解支持带有通配符的Ant风格的Url 了解
( 1 ) ? --- 匹配路径或文件名中的任意一个字符
( 2 ) * --- 匹配路径或文件名中的任意多个字符
( 3 ) **---匹配任意多层路径
📕 @GetMapping注解和@PostMapping注解
-
@GetMapping注解---专门用来处理Get请求
@GetMapping("url地址") 相当于@RequestMapping(value="url地址",method=RequestMethod.GET)
-
@PostMapping注解---专门用来处理Post请求
@PostMapping("url地址")相当于@RequestMapping(value="url地址",mehtod=RequestMethod.Post)
3. SpringMVC-控制器的数据输入
3.1 绑定简单的数据类型---基本数据类型+String
-
方式一 : 使用request对象获取请求参数值
在方法中添加类型为HttpServletRequest参数,SpringMVC会自动将request对象注入到方法中
在业务方法中调用request对象的getParameter()方法获取请求参数的值
-
方式二 : 使用@RequestParam注解获取请求参数值
SpringMVC默认支持简单数据类型绑定,只需要在业务方法中添加对应的形参即可。
【注意】方法的形参名称要和请求参数名保持一致,否则在形参上添加@RequestParam注解,并在注解的value属性中指定请求参数名
( 1 ) 在方法参数中定义和请求参数同名的方法参数,SpringMVC会自动将请求参数值注入到方法形参中。
( 2 ) 如果请求参数名和方法形参名不一致时,使用@RequestParam注解,并在注解的name属性中指定请求参数名【相当于HttpServletRequest接口的getParameter()方法】
-
@RequestParam注解
( 1 ) name或value属性---指定请求参数的名称
( 2 ) required属性--- 指定参数是否必须,默认为true,表示请求参数中必须包含对应的参数,如果参数不存在,则报错--- Required parameter 'xxx' is not present
( 3 ) defaultValue属性 --- 为请求参数提供默认值
-
@PathVariable注解
( 1 ) Restful风格的请求url --- 将参数直接写在路径中,而无需?携带
( 2 ) 带占位符的url是Spring3.0新增的功能,该功能是SpringMVC支持REST编程风格的重要基础
@RequestMapping 直接中的url使用{}括起参数名
@PathVariable将请求url中的{}占位符参数绑定到控制业务方法的形参中
@RequestMapping("/delete/{userId}")
public String pathVariable(@PathVariable("userId") String userId){
}
【注意】@RequestMapping中的占位符{}中的名称要和@PathVariable注解中的value属性值保持一致,与方法参数名无关,该注解取出url中占位符的数据并自动注入到方法形参中
3.2 绑定原生Servlet API对象
-
如果需要在控制器的方法中使用原生的Servlet API对象,只需直接在业务方法中添加对应的形参即可。
-
SpringMVC支持以下类型对象的自动注入
HttpServletRequest 常用
HttpServletResponse 常用
HttpSession 常用
InputStream和OutputStream
Reader和Writer
3.3 绑定POJO
SpringMVC支持POJO的自动注入,只需直接在业务方法中添加对应的POJO的类型的参数即可,并支持级联属性的注入
【工作原理】 SpringMVC根据请求参数名和POJO类的属性名自动匹配,因此要求请求参数名和POJO对象的属性名保持一致,否则无法注入成功。
3.4 绑定数组
SpringMVC支持POJO的自动注入,只需直接在业务方法中添加对应的数组类型的参数即可。
【工作原理】
( 1 ) 自动调用getParameterValues()方法获取到数组参数值,并注入到数组类型的同名形参中
( 2 ) 如果形参声明为String类型,还能自动将数组元素拼接成一个以逗号分隔的字符串
( 3 ) 如果形参声明为非String类型的数组,还能自动进行类型转换。
3.5 数据绑定的工作原理
📕 SpringMVC通过JDK动态代理或CGLIB技术为控制器类创建一个代理对象
📕 在代理对象中,通过HttpServletRequest接口的获取提交的参数名和参数值
getParameterNames()方法返回所有请求参数的名称
getParameterValues()方法返回指定参数名对应的所有参数值
getParameterMap()方法返回参数名作为key,参数值作为value的Map
📕 根据获取到的String类型的参数,通过各种类型转换器(Converter)将参数自动转换为对应的数据类型,同时允许程序员自定义类型转换器并添加到类型转换器集合中
📕 通过反射调用控制器的业务方法,将转换好的数据注入到形参变量中,这样业务方法就能实现自动接收数据的效果了
【总结】 底层通过动态代理技术将获取表单参数,类型转换、数据校验等代码动态织入到控制器的业务方法中。
3.6 从属性文件中读取初始化参数
( 1 ) 创建属性文件config.properties,存放在resources文件夹下
loginScope=2
( 2 ) 使用<context:property-placeholder >标签加载属性文件
<context:property-placeholder location="classpath:config.properties"/>
【注意】此标签只能在配置文件中使用一次,如果项目存在多个属性文件时,可以使用通配符*
( 3 ) 在控制器类中声明成员属性,并使用@Value注解获取属性值
@Value("${loginScope}")
private int loginScope;
3.7自定义类型转换器
🌳 查看spring-core.jar文件中内置的转换器类
(1)org.springframework.core.convert.converter包下的Converter接口
(2)org.springframework.core.convert.support包下的各种转换器的源码---StringToNumberConverterFactory类 StringToArrayConverter类
(3)org.springframework.context.support包下的ConversionServiceFactoryBean类,其中有一个converters集合属性
Spring MVC上下文中内置了大量的类型转换器,实现不同数据类型之间的转换,可以完成大部分的类型转换工作
java.lang.Boolean -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter
java.lang.Character -> java.lang.Number : CharacterToNumberFactory
java.lang.Character -> java.lang.String : ObjectToStringConverter
java.lang.Enum -> java.lang.String : EnumToStringConverter
java.lang.Number -> java.lang.Character : NumberToCharacterConverter
java.lang.Number -> java.lang.Number : NumberToNumberConverterFactory
java.lang.Number -> java.lang.String : ObjectToStringConverter
java.lang.String -> java.lang.Boolean : StringToBooleanConverter
java.lang.String -> java.lang.Character : StringToCharacterConverter
java.lang.String -> java.lang.Enum : StringToEnumConverterFactory
java.lang.String -> java.lang.Number : StringToNumberConverterFactory
java.lang.String -> java.util.Locale : StringToLocaleConverter
java.lang.String -> java.util.Properties : StringToPropertiesConverter
java.lang.String -> java.util.UUID : StringToUUIDConverter
java.util.Locale -> java.lang.String : ObjectToStringConverter
java.util.Properties -> java.lang.String : PropertiesToStringConverter
java.util.UUID -> java.lang.String : ObjectToStringConverter
🌳 类型转换器的工作原理
(1)ConversionService是类型转换体系的核心接口,可以利用ConversionServiceFactoryBean在IoC容器中定义一个ConversionService
Spring MVC会自动识别出IoC容器中的转换服务ConversionService,并在Bean属性配置和业务方法形参绑定时使用ConversionService进行数据的转换
(2)为了满足项目的业务需求,程序员可以提供自定义的类型转换器类,并通过ConversionServiceFactoryBean的converters属性注册自定义的类型转换器,将其加入到转换器集合中
🌳 自定义的类型转换器的开发步骤
(1)创建自定义的类型转换器,实现Converter接口,指定泛型参数,并重写convert()方法实现转换逻辑
(2)在mvc:annotation-driven标签中指定类型转换服务,并注册自定义的类型转换器类
<!-- 开启注解驱动,同时指定类型转换服务的bean名称 -->
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 配置类型转换服务,注册自定义的类型转换器 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="自定义类型转换器的完整类名"/>
</set>
</property>
</bean>
4. SpringMVC-控制器的数据输出
4.1 控制器方法的三种返回类型
-
返回void类型
不使用处理器映射器进行页面跳转,直接在控制器中使用response对象完成响应输出此方法一般用于验证码、Ajax、文件下载等业务场景
-
返回String类型
直接返回要跳转的视图页面文件名,默认是请求转发,重定向要加上redirect:前缀
-
返回ModelAndView类型
调用setViewName()方法,将跳转视图的信息封装到ModelAndView对象中。
4.2 处理模型数据的四种方法
-
方法返回Stirng时
如果控制器的业务方法返回String类型时,可以使用以下四种方式向request属性范围保存数据:
( 1 ) 添加Mdeol类型的输入参数,并调用addAttribute()方法,填充模型数据
( 2 ) 添加Map类型的输入参数,并调用put()方法,填充模型数据
( 3 ) 添加ModelMap类型的输入参数,并调用put()或addAttribute()方法,填充模型数据
( 4 ) 在方法形参中添加@ModelAttribute注解,同时指定value属性为请求参数名,则此参数会自动填充到模型数据中
【注意】重定向操作无法使用以上四种方法保存数据,必须将数据保存在session属性范围中。
-
方法返回ModelAndView类型时
如果控制器的业务方法返回ModelAndView类型,可以调用addObject()方法,填充模型数据
4.3 配置视图解析器
-
视图解析器 --- ViewResolver类
配置InternalResourceViewResolver视图解析器,可以简化视图名称的写法,该类在webmvc这个jar包
<!--配置视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--指定页面所在的文件,即视图的前缀--> <property name="prefix" value="/WEB-INF/"></property> <!--指定页面的扩展名,即视图的后缀--> <property name="suffix" value=".jsp"></property> </bean>
(1)为了提高系统的安全性,往往将JSP页面放到项目的WEB-INF文件夹中,这样客户端就 无法直接访问这些页面
(2)使用以上配置后,控制器业务方法返回的是逻辑视图名(仅仅是页面的主文件名), InternalResourceViewResolver负责在获取到的逻辑视图名的前面和后面拼接上前缀 prefix和后缀suffix,最终形成完整的路径
【注意】此配置只针对服务器请求转发操作有效
-
视图 --- View类
( 1 ) 所有视图类都继承了AbstractView类,该类实现了View接口,并重写了render()方法实现视图的渲染【缺省适配器模式】
( 2 ) AbstractView类的render()方法中调用了renderMergedOutputModel()方法,该方法是抽象方法,等待具体的视图子类重写【体现模板方法设计模式】
( 3 ) IntermalResourceView类是AbstractView类的具体子类,renderMergedOutModel()方法中封装了原始的request.getRequestDispatcher().formard()方法完成服务器请求转发
( 4 ) RedirectView类是AbstractView类的具体子类,renderMergedOutputModel()方法中封装了原始的response.sendRedirect()方法返回客户端重定向
4.4 处理静态资源请求的三种方式
📕 问题产生的原因
如果前端控制器DispatcherServlet中的url-pattern设置为/,Spring MVC会拦截全部的请求,其中 也包含对静态资源(如图片,js文件,css文件)的请求, Spring MVC会将其当成一个普通请求处理, 因为找 不到对应的处理器,所以导致404错误。
📕 解决方案
【方案1】修改web.xml,将前端控制器DispatcherServlet中的url-pattern设置为 .do或.action
表示只拦截.do后缀的url,此方式需要修改项目中所有请求路径,比较麻烦,不推荐使用
【方案2】修改Spring MVC的配置文件---添加mvc:resources标签处理静态资源请求
<mvc:resources mapping="/image/**" location="/image/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
【方案3】修改Spring MVC的配置文件---添加mvc:default-servlet-handler/标签处理静态资源请求
<!-- 处理静态资源 -->
<mvc:default-servlet-handler />
【工作原理】
<mvc:default-servlet-handler>标签用于指定对项目中的静态资源的处理器DefaultServletHttpRequestHandler类会对进入前端控制器DispatcherServlet的请求进行过滤, 如果发现请求的是静态资源(image,css,js,html等)无法匹配处理器映射器, 就将该请求交由Web容器中默认的Servlet处理(Tomcat中是DefaultServlet),只有非静态资源才交给前端控制器DispatcherServlet继续处理
5. SpringMVC-文件的上传和下载
5.1 文件的上传
-
添加上传文件所需的jar包依赖
SpringMVC内置了commons-fileupload作为文件上传组件,该依赖自动导入commons-id组件
-
在springmvc.xml文件中配置文件上传的解析器,id属性必须为multipartResolver
<!-- 文件上传解析器 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="所有上传文件的最大字节数总和" />* <property name="maxUploadSizePerFile" value="单个上传文件的最大字节数" /> <property name="defaultEncoding" value="默认编码" /> <property name="maxInMemorySize" value="内存中的最大值" /> </bean>
-
文件上传的三个条件
( 1 ) 标签必须指定method属性为post
( 2 )标签必须指定entype属性为multipary/form-data,表示以二进制流的形式传输文件内容
( 3 )使用类型为file的标签,供用户选择文件
-
在业务方法中添加MultipartFile类型的形参,调用transferTo()方法
(1)org.springframework.web.multipart.MultipartFile类封装了上传文件对应的二进制信息
(2)为了防止同名文件的覆盖,保存文件前需要将主文件名进行改名处理
(3)文件保存在项目中的指定目录,使用servletContext接口的getReaPath()方法返回真实的物理路径
-
MultipartFile类的API
byte[] getBytes() //获取文件内容对应的字节数组
String getContentType() //获取文件的内容类型
InputStream getInputStream() //返回文件内容对应的输入流
String getName() //返回临时文件名
String getOriginalFilename() //返回原始文件名 常用
long getSize() //返回文件大小 常用
boolean isEmpty() //判断文件是否为空 常用
void transferTo(File dest) //将临时文件保存到指定位置 常用
-
多文件上传
多文件上传时,业务方法的参数类型是MultipartFile的数组,通过循环遍历数组的每一个MultipartFile对象进行上传操作
5.2 文件的下载
1. 业务方法的返回类型为void
2. 通过request对象获取下载资源对应的字节输入流对象
3. 通过response对象获取字节输出流对象
4. 添加名为Content-Disposition的响应头,设置文件的处理方式为下载
5. 使用拷贝字节流或使用IOUtils类提供的copy()方法完成文件的传输
@RequestMapping("/download")
public void download(String filename, HttpServletRequest request, HttpServletResponse response) throws Exception {
//构造字节输入流
InputStream is = request.getServletContext().getResourceAsStream("/download/" + filename);
//获取字节输出流
OutputStream os = response.getOutputStream();
//添加响应头,设置文件的处理方式为下载
response.addHeader("Content-Disposition", "attachment;filename=" + filename); //设置下载时的文件名
//拷贝字节流---方式1
byte[] buffer = new byte[1024];
BufferedInputStream bis = new BufferedInputStream(is);
int length = 0;
while ((length = bis.read(buffer))!= -1) {
os.write(buffer, 0, length);
}
//拷贝字节流---方式2
//IOUtils.copy(is, os);
bis.close();
os.close();
}
6. SpringMVC-处理JSON数据
SpringMVC内置了Jackson作为JSON数据的解析器
-
在pom.xml文件中导入Jackson数据解析的jar包依赖
<!-- Jackson JSON数据解析 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency>
该Jar包自动导入了jackson-annotation.jar、jackson-core.jar、jackson-databind.jar这三个jar包
-
确保在springmvc.xml中添加了mvc:annotation-driven/标签
该标签会自动注册JSON数据解析的适配器HttpMessageConverter
-
前台页面发送JSON字符串给后台控制层
前台页面需要发送JSON字符串给控制器的业务方法时,只需在业务方法形参上标注@RequestBody注解,就可以自动完成JSON字符串到POJO对象或集合的转换
(1)ajax()函数中的data参数要写为JSON字符串的形式 (2)ajax()函数中要指定contentType:"application/json" (3)ajax()函数中的type参数要写为POST,不能发送GET请求 (4)业务方法形参上标注@RequestBody注解,表示该参数从POST请求的请求体中获取
-
后台控制器发送JSON字符串给前台页面 常用
(1)控制器的业务方法需要返回JSON字符串给前台页面时,只需在业务方法上标注 @ResponseBody注解,就可以自动完成POJO对象或集合到JSON字符串的转换
(2)前后端分离开发模式中,控制器只需返回JSON数据,可以在类上使用组合注解@RestController @RestController=@Controller+@ResponseBody
7. SpringMVC-拦截器
🌳 拦截器的基本概念
( 1 )拦截器(Interceptor)类似于Java Web的过滤器(Filter),可以在业务方法执行前后,添加预处理和后处理操作,也是AOP思想的实现
( 2 ) 拦截器拦截的是控制器的业务方法,不能像过滤器那样拦截JSP页面和静态资源
( 3 ) 前端核心控制器ServletDispatcher在执行某个控制器的业务方法之前,会根据配置的顺序依次执行所有自定义的拦截器中的方法,最终才到达控制器的业务方法中
🌳 拦截器的开发步骤
( 1 ) 创建拦截器,实现HandlerInterceptor接口
根据具体的业务需求,重写preHandle()方法,postHandle()方法,afterHandle()方法
(1.1)boolean preHandle()方法---业务方法执行之前执行的方法
返回true表示继续执行后续流程,返回false表示流程结束
(1.2)void postHandle()方法---业务方法执行后前执行的方法
(1.3)void afterCompletion()方法---响应页面渲染后执行的方法
( 2 ) 在springmvc.xml中配置拦截器
(2.1)使用标签指定拦截器类的完整类名
(2.2)使用mvc:mapping标签指定拦截的路径范围,**表示所有请求
如/user/**表示拦截user下的全部请求
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="拦截的路径范围"/>
<mvc:exclude-mapping path="不拦截的路径范围"/>
<bean class="拦截器的完整类名"></bean>
</mvc:interceptor>
</mvc:interceptors>
🌳 多个拦截器的执行顺序 (1)preHandle()方法的执行顺序和配置文件的定义顺序相同,postHandle()方法和afterCompletion()方 法正好相反
(2)preHandle()方法返回true,则afterCompletion()方法肯定会执行
8. SpringMVC-执行过程和源码分析
一. Spring MVC的四大核心组件
1. 前端控制器---DispatcherServlet
接收客户端的请求,并将请求配发给其它组件完成操作,最终将结果响应给客户端
2. 处理器映射器---HandlerMapping
根据请求的url查找匹配的Handler/Controller
业务处理器Handler/控制器Controller---需由程序员编写的,处理业务逻辑的控制器类
3. 处理器适配器---HandlerAdapter
根据特定的规则,真正执行Handler/Controller,以及配置的拦截器
4. 视图解析器---ViewResolver
根据逻辑视图名生成真正的View视图对象,支持多种View视图技术
二. SpringMVC的执行流程 重点 【企业面试题】
1. 客户端发送请求,前端控制器(DispatcherServlet)接收客户的请求,并将请求派发给处理器映射器(HandlerMapper)
【DispatcherServlet相当于Struts 2的StrutsPrepareAndExecuteFilter】
【HandlerMapper相当于Struts 2的ActionMapper】
2. HandlerMapper根据请求的url和匹配规则,找到具体的业务处理器(Handler/Controller),并根据SpringMVC的配置文件中相关的拦截器配置,获取到一系列的拦截器(Interceptor),并将Handler和这些Inteceptor串联成一个HanderExecutionChain,返回给DispatcherServlet
【Handler/Controller---业务处理器,也叫后端控制器,由程序员编写,相当于Struts的Action】
3. DispatcherServlet委托处理器适配器(HandlerAdapter)按照特定规则执行处理器中的业务方法
【HandlerAdapter相当于Struts 2的ActionInvocation】
4. 业务方法返回一个ModelAndView对象给HandlerAdapter,该对象内部封装了视图所需的模型数据和视图的逻辑名,HandlerAdapter将这个ModelAndView对象继续返回给DispatcherServlet
5. DispatcherServlet将ModelAndView对象派发给视图解析器(ViewResolver)去解析ModelAndView对象中的内容,返回View视图对象给DispatcherServlet
【ModelAndView相当于Struts 2的result + 放入属性范围中的数据】
6. DispatcherServlet从ModelAndView对象中取出数据部分(Model),并将数据填充到视图中,此过程叫视图渲染
7. DispatcherServlet根据视图类型,进行相应的请求转发或重定向操作,最终将结果响应给客户端