Spring MVC 框架
Spring MVC 是 在 Servlet 框架的基础上,进行了 深度的 封装 和 扩展, 形成了 MVC 框架, Spring MVC 框架 底层是 Servlet 。
MVC (M模型-V视图-C控制器)
MVC 是一种设计思想 , 不是 针对某一种语言。 MVC 是一种 开发模式 , 将一个应用程序 人为的 划分为 模型层 、 视图层 、 和 控制层 。
M (模型) : 一般 认为 和 数据 打交道的 层 被 成为 模型层 、所以 通常 会把 实体层 、 持久层 、业务层 划分到 模型层 中 。
V (视图) : 在 Java 语言中, V 通常 由 JSP 充当 。
C (控制器) : 一般负责 接收页面参数、处理业务 和 跳转页面
MVC 框架一般 会有一个 统一的入口 (核心控制器、前端控制器) 、负责 接收转发 请求、 负责 处理 结果、并将结果 统一进行渲染, 呈现在 浏览器中 。
搭建一个 SpringMVC (WEB)项目
-
引入 项目需要的依赖包
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> </dependencies>
-
在 web.xml 配置 Spring MVC 的 核心控制器
<!-- 配置 SpringMVC 核心控制器, 该核心控制器 需要一个 SpringMVC 容器(XML) 该 容器的 默认名为 <servlet-name>-servlet.xml ,且默认存放在 /WEB-INF 下 如果 想要更改 配置文件的 路径,可以通过 init-param 进行修改, 通过 contextConfigLocation进行修改 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.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>
-
在 classpath下,添加 spring-mvc.xml 配置 SpringMVC 容器
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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"> <!-- 扫描 com.haredot 下的所有被spring管理的类 --> <context:component-scan base-package="com.haredot" /> <!-- 配置 视图解析器, 负责 解析视图 , 设置前缀 和后缀 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/" /> <property name="suffix" value=".jsp" /> </bean> </beans>
-
编写 控制器
@Controller @RequestMapping("/test") public class TestController { @RequestMapping("/") public String test() { // 希望 找到 /WEB-INF/pages/test.jsp return "test" ; } }
-
在 WEB-INF/ 目录下, 新建一个 pages 文件夹, 在 该文件夹下 新建一个 test.jsp 文件 。
-
部署项目到 Tomcat, 启动 并 访问 http://localhost:8080/test/
配置 Spring 容器
<!-- 读取 Spring 容器, ContextLoaderListener 默认读取的Spring容器是在 WEB-INF 下的名字叫 applicationContext.xml
可以通过 context-param 标签 配置 spring容器的位置和名字
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Spring 容器 扫描 除 控制层之外的其他所有层
<context:component-scan base-package="com.haredot">
<!-- 配置黑名单,不扫描 @Controller 注解 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
Spring MVC 容器 只扫描 控制层
<!--
component-scan 默认扫描的注解(白名单)是
@Component, @Service , @Repository, @Controller, @Named , @Configuration 等注解。
如果要禁用 该默认白名单,可以通过 use-default-filters 设置 为 false
-->
<context:component-scan base-package="com.haredot" use-default-filters="false">
<!--通过 白名单配置,默认只扫描@Controller注解-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
SpringMVC 零配置(少量配置)环境搭建
-
添加依赖包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.27</version> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-servlet-api</artifactId> <version>9.0.75</version> <scope>provided</scope> </dependency>
-
编写 启动类
public class Application extends AbstractAnnotationConfigDispatcherServletInitializer { /** * 配置 Spring 容器 */ @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{RootConfig.class}; } /** * 配置 SpringMVC 容器 * * @return */ @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{WebMvcConfig.class}; } /** * urlpattern = "/" * @return */ @Override protected String[] getServletMappings() { return new String[] { "/" }; } }
-
配置 Spring 容器
/** * Spring容器 */ @ComponentScan(value = "com.haredot", excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class) }) public class RootConfig { }
-
配置 Spring MVC 容器
@ComponentScan(value = "com.haredot", useDefaultFilters = false, includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class) }) public class WebMvcConfig { @Bean public ViewResolver viewResolver() { return new InternalResourceViewResolver("/WEB-INF/pages/", ".jsp"); } }
-
编写控制器
@Controller public class TestController { @RequestMapping("/test") public String test() { return "test" ; } }
-
部署项目到 Tomcat , 启动 并访问 http://localhost:8080/test
@RequestMapping 注解
- GetMapping
- PostMapping
- PutMapping
- DeleteMapping
- PatchMapping
上述 五个注解 都是 @RequestMapping 的替代注解, 可以更快捷的指定请求地址和 请求方式。
@RequestMapping 注解常见的成员
- value / path : 设置 请求地址 , 请求地址 应该以 / 开头
- method : 设置请求方式, @GetMapping , @PostMapping 等注解中 没有该属性
- params : 设置 请求地址 中 那些 请求参数 必传 , 如果不满足条件, 返回 400
- headers : 设置 请求地址中 必须 包含 那些 请求头,如果不满足条件, 返回 404
- consumes : 设置 请求的 类型,例如 application/json , multipart/form-data , …
- produces : 设置 响应的 类型 , 例如 application/json , text/html , …
SpringMVC 如何 接收 请求发送的参数
- 路径参数 @PathVariable
在 使用 @RequestMapping 注解 定义 请求路径的时候, 可以通过
{ key }
占位符的手段 来定义 路径参数@GetMapping("/{type}") public void test(@PathVariable String type) { System.out.println("type = " + type) } // http://localhost:8080/java type = java // http://localhost:8080/python type = python
@PathVariable 用来接收 路径 参数, 可以 通过 value 成员 指定 占位符的 名字 ,例如 @PathVariable(value=“type”) , 如果 没有指定名字, 则 @PathVariable 标记的参数 名 必须和 {key} 中的 key 保持完全一致。
路径 参数 {key } 支持 正则表达式 , 例如 {key : [0-9]+} , 表示 占位符 传入的值 必须是 数字
- 矩阵参数 @MatrixVariable
矩阵参数 是建立 在 路径参数的基础上的一种 参数传递方式 。它的特点是 在 路径参数的后面 通过
;
进行分割,后面多个参数使用;
分割 , 格式例如 /user/1;a=1;b=2SpringMVC 默认没有开启 矩阵参数的支持 ,如果需要 启用,则 需要进行对应的配置
- Java Config 启用 矩阵参数
@ComponentScan(value = "com.haredot", useDefaultFilters = false, includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class) }) @EnableWebMvc public class WebMvcConfig implements WebMvcConfigurer { @Override public void configureViewResolvers(ViewResolverRegistry registry) { registry.jsp("/WEB-INF/pages/", ".jsp"); } /** * 启用 矩阵参数的支持 * @param configurer */ @Override public void configurePathMatch(PathMatchConfigurer configurer) { UrlPathHelper helper = new UrlPathHelper() ; // 启用矩阵参数 helper.setRemoveSemicolonContent(false); configurer.setUrlPathHelper(helper) ; } }
- XML 启用 矩阵参数
<!-- 引用 mvc 命名空间 , mvc:annotation-driven 等价于 @EnableWebMvc 注解 --> <mvc:annotation-driven enable-matrix-variables="true"/>
- @MatrixVariable 注解的成员
- value : 设置 矩阵参数的 键名
- required : 设置 矩阵参数是否必传,默认为 true
- defaultValue : 设置 矩阵参数当没有传值的时候,默认值
- pathVar : 设置 矩阵参数的 值来自 哪一个 路径参数, 值 和 路径参数的占位符 名 保持一致 。
- 表单参数 @RequestParam
- value : 用来设置 接收参数的键名
- required: 是否必传, 默认 必传
- defaultValue : 如果 required 设置为 false, 那么 当 用户 没有传递对应参数 或者 传递的参数是 空字符串, 会 使用 默认值 。
@RequestParam 可以 接收 字面量 类型 的参数 , @RequestParam 也可以省略。
表单参数 ,默认 只支持 GET , POST 请求, 如果使用 PUT , PATCH , DELETE 请求 ,那么 表单参数 接收不到。 表单参数 在传递 中文的时候,会产生 中文乱码 。
-
接收 一键 多值
使用 数组 进行接收 多个参数 、 如果 没有用 数组接收, 且类型如果是 字符串, 那么 默认会使用 逗号 进行 拼接多个值, 形成一个字符串。
-
使用 Pojo 对象 接收 表单参数
- 使用 Pojo 不能在 参数前 ,使用 @RequestParam 注解
- 传递的参数 键 和 Pojo 对象中的 set 方法 对应的 属性名 保持一致 。
- Pojo 可以 减少 参数的定义 。但 无法限制 参数是否 必传 , 但可以借助 后期学习的 参数 校验 或者 @GetMapping(params = {}) 来限定某些参数必传。
-
使用 Map<String, String> 接收表单参数
- 在 Map参数上必须 添加 @RequestParam 注解, 且 该注解 不能设置 value
- 如果 接收的参数 对应的值 有多个, 可以 使用 MultiValueMap<String, String> 进行接收。
-
旧版日期Date 参数的 接收方式
- 传入 的 日期格式 默认需要满足 yyyy/MM/dd HH:mm:ss 格式
- 可以通过 @DateTimeFormat(pattern = “yyyy-MM-dd”) 设置 传入的 日期 字符串格式 。
@GetMapping(value = "/f") public String test5(@RequestParam("birth") @DateTimeFormat(pattern = "yyyy-MM-dd") Date birth) { return "test" ; }
-
新版日期 LocalDate 参数的 接收方式
- 传入的 日期格式 默认需要 满足 yyyy/MM/dd
- 可以通过 @DateTimeFormat(pattern = “yyyy-MM-dd”) 设置 传入的 日期 字符串格式 。
-
接收 JSON 格式的 参数 @RequestBody
- JSON 格式的参数 ,请求头信息中 必须 包含 Content-Type = “application/json”
- 在 日期 类型上使用 @JsonFormat(pattern = “yyyy-MM-dd”) 解决日期格式问题。
- Spring框架 如果要处理 JSON 数据, 需要添加 jackson 依赖包
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.15.2</version> </dependency>
-
接收 指定Cookie 的 value @CookieValue
-
接收 指定请求头信息 @RequestHeader
@GetMapping(value = "/c") public String test3(@CookieValue(value = "JSESSIONID", required = false, defaultValue = "") String cookieValue, @RequestHeader(value="User-Agent") String userAgent) { // 使用 CookieValue 获取 JSESSIONID 对应的 Cookie value 值 // 使用 RequestHeader 获取 请求头信息中 User-Agent 对应的值 return "test" ; }
@ModelAttribute
-
用在 请求 方法的 参数 上 : 会将 传入的表单参数(使用pojo接收) , 放入到 request作用域中。
@GetMapping("/k") public ModelAndView test6(@ModelAttribute("user") User user) { ModelAndView mav = new ModelAndView("test"); System.out.println(user); return mav ; }
**@ModelAttribute注解 不能和 @RequestParam 注解 混用, 如果混用,会导致 无法将 接收到的参数 存储到 request作用域 **
@ModelAttribute注解 不能和 @RequestBody 注解混用, 如果混用, 会导致 无法接收到 JSON参数,但可以将输入存储到作用域中
-
@ModelAttribute 可以 定义在一个
返回模型对象的方法
上, 单独使用意义不大, 需要配合一个 @SessionAttributes 注解@Controller @RequestMapping("/test") public class TestController { @ModelAttribute("user") public User getUser() { return new User() ; } .... }
如果@ModelAttribute 使用在一个返回模型对象的方法上,那么该方法 会在 当前控制器类中的每一个请求方法执行前,先执行该模型方法, 注释的模型属性会被整个控制器所有请求共享,并在请求结束后把模型对象存储到 request作用域中
-
@SessionAttributes 注解 : 将 @ModelAttribute 定义的属性 提升为 会话属性 , 当 请求方法参数上使用 了 @ModelAttribute 注解,且注解中定义的名字 和 SessionAttributes 中定义的名字一致的时候,必须 使用ModelAttribute定义一个模型方法
-
@SessionAttribute 注解 : 该注解作用域 请求方法的参数上, 用来 从 session 作用域中 获取 指定的 对象。
Spring 中常见的过滤器
-
CharacterEncodingFilter : 字符集编码过滤器
- 配置方式一 : web.xml 中 进行配置
<filter> <filter-name>encodingFilter</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>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
- 配置方式二 : 重写 AbstractAnnotationConfigDispatcherServletInitializer 中的 getServletFilters 方法
重写 getServletFilters 方法, 配置的过滤器 ,默认 拦截的 是 dispatcher 核心控制器, 不能修改 。
@Override protected Filter[] getServletFilters() { CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter("UTF-8", true); return new Filter[]{characterEncodingFilter}; }
- 配置方式一 : web.xml 中 进行配置
-
FormContentFilter : 解决 PUT, PATCH , DELETE 无法接收 表单参数 问题
自定义转换器
- 编写一个 转换器类 、实现 Converter<S, T> 接口
public class StringToUserConverter implements Converter<String, User> { @Override public User convert(String source) { // 按照 : 进行字符串的拆分 String[] split = source.split(":", 2); if (split.length != 2) return null ; return User.builder() .username(split[0]) .password(split[1]) .build(); } }
- 注册转换器
- XML 配置方式
<mvc:annotation-driven conversion-service="conversionService" /> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <!-- 注册自定义的转换器 --> <property name="converters"> <set> <bean class="com.haredot.converter.StringToUserConverter" /> </set> </property> </bean>
- Java 配置方式
@EnableWebMvc public class WebMvcConfig implements WebMvcConfigurer { /** * 配置 用户的 自定义转换器 * @param registry */ @Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new StringToUserConverter()); } }
- XML 配置方式
- 使用效果
// http://localhost:8080/login?user=admin:123456 也可以使用 @RequestParam 限定传入的键名 @GetMapping("/login") public String login(User user) { System.out.println("user = " + user); return "test" ; }
SpringMVC 中使用静态资源
- 在 web.xml 中, 可以通过
default
Servlet 来 读取 静态资源 。该解决方案,依赖于 tomcat 容器 , 如果 使用 其它服务器 部署项目 、可能 会失效 。
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/css/*</url-pattern>
<url-pattern>/images/*</url-pattern>
<url-pattern>/js/*</url-pattern>
</servlet-mapping>
-
SpringMVC 框架 解决 静态资源 (依赖于 tomcat服务器的配置)
- XML配置
<mvc:default-servlet-handler />
- javaconfig 配置
@EnableWebMvc public class WebMvcConfig implements WebMvcConfigurer { @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }
- XML配置
-
SpringMVC 通过 其他方式 解决静态资源 访问
- XML配置
<mvc:resources mapping="/css/**" location="/css/" cache-period="30" /> <mvc:resources mapping="/images/**" location="/images/" cache-period="30" /> <mvc:resources mapping="/js/**" location="/js/" cache-period="30" />
- JavaConfig 配置
public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/css/**") // addResourceHandler 设置访问请求地址,支持 Pattern 模式 .addResourceLocations("/css/") // addResourceLocations 设置 资源在项目中的位置信息 .setCachePeriod((int)TimeUnit.SECONDS.toSeconds(30)); registry.addResourceHandler("/images/**").addResourceLocations("/images/") .setCachePeriod((int)TimeUnit.DAYS.toSeconds(1)) ; registry.addResourceHandler("/js/**").addResourceLocations("/js/") .setCachePeriod((int)TimeUnit.SECONDS.toSeconds(30)); ; }
- XML配置
SpringMVC 上传文件
-
使用 file-upload 实现文件上传 (比较常用)
- 添加 一个 commons-fileupload 依赖包
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.5</version> </dependency>
- 配置 文件上传 通用 解析器
- 用 XML 配置
<!-- 配置文件上传解析器, bean的ID 名字必须叫 multipartResolver (名字是固定的) --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="utf-8" /> <!-- maxUploadSize 允许上传的最大值,如果设置为 -1 则代表不限制文件大小,默认为 -1 --> <property name="maxUploadSize" value="-1" /> <!--默认文件上传只支持 POST --> <property name="supportedMethods" value="POST,PUT" /> </bean>
- 用 JavaConfig 配置
@Bean public CommonsMultipartResolver multipartResolver() { CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); multipartResolver.setDefaultEncoding("UTF-8"); multipartResolver.setMaxUploadSize(-1); multipartResolver.setSupportedMethods("POST", "PUT"); return multipartResolver ; }
- 用 XML 配置
- 使用 MutipartFile 类 来 接收 上传的文件对象, 允许使用 @RequestParam 注解
@PutMapping("/upload") public R<String> upload(@RequestParam("resource") MultipartFile file) throws Exception { // 获取上传的 文件 名 System.out.println("file.getOriginalFilename() = " + file.getOriginalFilename()); // 获取上传 的 表单名 (resource) System.out.println("file.getName() = " + file.getName()); // 获取 文件对应的输入流 System.out.println("file.getInputStream() = " + file.getInputStream()); // 获取文件上传的媒体类型 System.out.println("file.getContentType() = " + file.getContentType()); // 获取上传文件的大小 System.out.println("file.getSize() = " + file.getSize()); // 将 上传的文件存储到 磁盘的指定文件中 file.transferTo(new File("D:/a.zip")); return R.<String>ok().data("xxxxxx") ; }
- MutipartFile 类 常见的方法
- getOriginalFilename() : 获取上传的文件名
- getInputStream() : 获取 文件输入流
- getContentType() : 获取文件的媒体类型
- size() : 获取文件大小
- transferTo(file) : 将上传的文件存储到指定的磁盘路径
- 添加 一个 commons-fileupload 依赖包
-
使用 Servlet API 实现文件上传
-
添加 servlet 3.0 依赖包
<dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-servlet-api</artifactId> <version>9.0.75</version> <scope>provided</scope> </dependency>
-
配置 Servlet 文件上传解析器
- XML配置方式
<!-- web.xml --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <multipart-config /> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!--springmvc 容器 --> <bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver" />
- JavaConfig 配置
public class Application extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected void customizeRegistration(ServletRegistration.Dynamic registration) { MultipartConfigElement configElement = new MultipartConfigElement(""); registration.setMultipartConfig(configElement); } } @EnableWebMvc public class WebMvcConfig implements WebMvcConfigurer { @Bean public StandardServletMultipartResolver multipartResolver() { return new StandardServletMultipartResolver() ; } }
- XML配置方式
-
使用 Part 接收参数
@PutMapping("/upload") public @ResponseBody R<String> upload(@RequestParam("resource") Part file) throws Exception { // 获取上传的 文件 名 System.out.println("file.getOriginalFilename() = " + file.getSubmittedFileName()); // 获取上传 的 表单名 (resource) System.out.println("file.getName() = " + file.getName()); // 获取 文件对应的输入流 //System.out.println("file.getInputStream() = " + file.getInputStream()); // 获取文件上传的媒体类型 System.out.println("file.getContentType() = " + file.getContentType()); // 获取上传文件的大小 System.out.println("file.getSize() = " + file.getSize()); // 将 上传的文件存储到 磁盘的指定文件中 file.write("D:/a.zip"); return R.<String>ok().data("xxxxxx") ; }
- getContentType() 获取 文件的媒体类型
- getSize() 获取 上传的文件 大小,单位是 字节, 返回 long
- getSubmittedFileName() 获取 上传的文件名, 例如 img.jpg
- getInputStream() 获取上传文件的 文件 输入流,可以通过该流 获取 上传的 文件对象
- write(file) 将上传的文件存储到指定的磁盘路径
SpringMVC 数据校验
-
-
引入 校验需要的依赖包
<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.4.3.Final</version> </dependency>
-
常见的 校验注解
- @Email : 用来校验邮箱格式的数据
- @NotNull : 用来校验 必传
- @Max : 校验数字、设置数字允许的最大值
- @Min : 校验数字、设置数字允许的最小值
- @NotEmpty : 校验 字符串、数组、Map , List 等容器 非空
- @Future : 用来校验 传入的日期是否是 未来日期, 大于当前日期
- @Past : 用来校验 传入的日期 是否是 过去日期, 小于 当前日期
- @Size : 可以限定 字符串、 Map 、数组、 List 等容器 的 最大值 和 最小值
- @DecimalMin : 设置 数字的 最小值
- @DecimalMax : 设置数字的最大值
- @AssertTrue : 限定传入的 值 必须是 true
- @AssertFalse : 限定传入的 值 必须是 false
- @Pattern : 支持 自定义 正则表达式
- @Length : 限定长度
- @Range : 限定数字范围
- @URL : 限定网址 格式
上述注解 均 提供了 group 成员, 用来 指定 组, 组 必须是一个 接口 。
-
控制器 接口参数 并进行数据校验
-
@Valid 用来标记 该参数 需要 校验
-
BindingResult : 将 校验的结果 绑定到 该对象中。 BingingResult 对象 必须写在 @Valid 校验的参数 后面 , BingingResult 和 @Valid 注解之间 不能有其他参数。
@PostMapping("/upload")
public @ResponseBody R<?> upload(@Valid @RequestBody ResourceVo resourceVo, BindingResult bindingResult) {
// 如果 校验 框架 校验完参数 后,有错误信息
if (bindingResult.hasErrors()) {
List<String> strings = bindingResult.getAllErrors().stream()
.map(error -> error.getDefaultMessage()).toList();
return R.fail("数据校验失败").data(strings);
}
return resourceService.saveResource(resourceVo);
}
@Valid , @Validated 用来标记 需要校验的参数。
@Validated 可以使用 value 指定 校验 组 ,组 必须是一个 接口 。
SpringMVC 文件下载 ResponseEntity<byte[]>
@GetMapping("/{resId:\\d+}/down")
public HttpEntity<byte[]> download(@PathVariable("resId") Long id) throws Exception{
// 根据资源ID ,查询资源对应的路径
R<Resource> ret = resourceService.queryResourcePathById(id);
if (ret.isSuccess()) {
String path = ret.getData().getResourceUrl();
// 获取 服务器上对应的资源路径
File file = uploadUtils.uploadFile(RssConst.RESOURCE_UPLOAD_PATH_SUFFIX, path);
// 将一个文件转成 byte[]
byte[] bytes = FileUtils.readFileToByteArray(file);
// 获取要下载的文件名
String filename = ret.getData().getResourceName() + "." + ret.getData().getExt() ;
// 对中文进行 路径编码
filename = URLEncoder.encode(filename, StandardCharsets.UTF_8);
// 下载文件
return ResponseEntity.ok()
.header("Content-Disposition", "attachment;filename=" + filename)
.body(bytes) ;
}
return null ;
}
SpringMVC 返回 JSON
@ResponseBody 可以写在 类上, 也可以 写在 方法上 , 如果写在 类 上, 则 会给 类中所有的 请求方法上自动添加 @ResponseBody
- @Controller + @ResponseBody(写在方法上) 注解 , 实现 返回JSON
- @RestController 实现返回 JSON (如果 请求方法 返回 ModelAndView, 则 会跳转页面,不会返回 JSON , 返回 HttpEntity<byte[]> 会下载 )
- 请求返回 HttpEntity<JSON格式的对象> 或者 ResponseEntity<JSON格式的对象> : 会将 泛型中的 对象 转成 JSON
SpringMVC要想返回 JSON , 必须添加 jackson 依赖包, 但 jackson依赖包 也可以 被其他 包 替换,例如 fastjson, 但如果用 fastjson,那么需要添加相关的配置, 具体配置请参考对应技术的官网
配置消息转换器
- JavaConfig 配置
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.forEach(converter -> {
if (converter instanceof StringHttpMessageConverter stringHttpMessageConverter) {
stringHttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);
}
if (converter instanceof MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
mappingJackson2HttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(List.of(MediaType.TEXT_HTML, MediaType.APPLICATION_JSON));
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule(){
{
// 配置 反序列化操作, 应用于 @RequestBody 注解
addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
// 配置 序列化操作, 应用于 @ResponseBody 注解
addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
}
} ;
// 添加新版日期的 模块处理
objectMapper.registerModule(simpleModule);
// 全局处理 新版日期格式问题
mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
}
});
}
}
- XML 配置
<mvc:annotation-driven> <mvc:message-converters> <ref bean="stringHttpMessageConverter" /> <ref bean="mappingJackson2HttpMessageConverter" /> </mvc:message-converters> </mvc:annotation-driven> <bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="defaultCharset" value="UTF-8" /> </bean> <bean id="mappingJackson2HttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="defaultCharset" value="utf-8" /> <property name="supportedMediaTypes"> <list> <value>text/html;charset=utf-8</value> <value>application/json;charset=UTF-8</value> </list> </property> </bean>
SpringMVC 跨域配置
- CrossOrigin
- 全局配置
- XML配置方式
<mvc:cors> <mvc:mapping path="/**" allowed-origin-patterns="*" allowed-headers="*" allowed-methods="*" max-age="30000" /> </mvc:cors>
- JavaConfig 配置方式
@EnableWebMvc public class WebMvcConfig implements WebMvcConfigurer { /** * 配置 全局跨域 * @param registry */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("*") .allowedHeaders("*") .allowCredentials(true) // 是否允许携带 Cookie .maxAge(TimeUnit.SECONDS.toMillis(30)); } }
- XML配置方式
SpringMVC 异常处理
-
当产生异常、跳转到 错误页面
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ModelAndView handler500(Exception e) { return new ModelAndView("500"); } }
-
当产生异常 、返回 错误 JSON 信息
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public R<?> handler500(Exception e) { return R.fail("接口调用失败"); } }
SpringMVC 拦截器 Interceptor
是 SpringMVC 框架所特有的技术 、和 Servlet 无关。 主要负责 拦截 Dispatcher 请求 和 响应 。
拦截器 的是 到 控制器 的 请求 和 响应 。拦截器 是 在 Servlet 容器 执行 后 触发的 。
自定义拦截器
-
编写一个类 、实现 HandlerIntercetor 接口 。
- boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) : 在 进入 控制器 前 执行的返回 , 如果返回 false, 则 会 中断 向下一个 拦截器或者 控制器 执行 。例如可以实现 权限检查
- void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception : 在 控制器对应的 请求方法执行完成后 触发的 代码。
- void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception : 控制器方法执行完(无论是否产生异常) 触发的代码
public class SessionInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 如果 拦截的是 控制器, 则 handler的类型是 HandlerMethod // 如果拦截的是 静态资源,则 handler的类型是 ResourceHttpRequestHandler // 如果 静态资源的处理 是通过 <mvc:default-servlet-handler /> 进行处理的,那么 是不会 拦截 静态资源的,如果是通过 <mvc:resource /> 处理的,那么 会拦截 静态资源。 // 判断 session 中 是否 有 登录 标记 Object loginFlag = request.getSession().getAttribute("LOGIN_FLAG"); if (loginFlag == null) { // 跳转到登录请求 response.sendRedirect(request.getContextPath() + "/login"); // 阻止继续执行 return false; } return true; } }
-
配置 拦截器
- XML 配置
<mvc:interceptors> <!-- 拦截所有请求 --> <!-- <bean class="com.haredot.interceptor.SessionInterceptor" /> --> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/login" /> <bean class="com.haredot.interceptor.SessionInterceptor" /> </mvc:interceptor> </mvc:interceptors>
- Java Config 配置
@EnableWebMvc public class WebMvcConfig implements WebMvcConfigurer { /** * 负责注册 拦截器 */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SessionInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/login"); } }
- XML 配置
SpringMVC 国际化
在项目中,可以切换不同的语言环境。
-
配置 管理 国际化配置文件的 Bean
@Bean public ResourceBundleMessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource() ; // 设置国际化文件的编码方式 messageSource.setDefaultEncoding("UTF-8"); // 设置 国际化文件的 前缀名 messageSource.setBasename("message"); return messageSource ; }
-
编写 国际化配置文件
- message_zh_CN.properties (中文)
- message_en_US.properties (英文)
-
配置 国际化 语言 解析器
- AcceptHeaderLocaleResolver (默认解析器) : 根据 浏览器的语言环境,选择显示的 语言。
- SessionLocaleResolver : 基于 Session 的语言解析器 , 会将 语言环境 写入到 Session 中
- CookieLocaleResolver : 基于 Cookie 的语言解析器, 会将 语言环境 写入 到 浏览器 Cookie 中
@Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setCookieMaxAge((int)TimeUnit.DAYS.toSeconds(30));
return cookieLocaleResolver ;
}
-
配置 切换语言的拦截器
public void addInterceptors(InterceptorRegistry registry) { // 配置 语言切换拦截器 LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); // 切换语言的参数默认是 locale, 可以通过 setParamName 修改 localeChangeInterceptor.setParamName("lang"); // 注册拦截器 registry.addInterceptor(localeChangeInterceptor); }
-
在 JSP 中 使用 国际化
<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %> <spring:message code="login" arguments="${sys}, ${username}"/>
-
在 代码中使用 国际化
@Resource private ResourceBundleMessageSource messageSource ; @GetMapping(value = "/g") public String test5(ModelMap modelMap, Locale locale) { modelMap.put("sys", "haredot") ; modelMap.put("username", messageSource.getMessage("test.name", null, locale)) ; return "test" ; }
SpringMVC 返回 XML
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>${jackson.version}</version>
</dependency>
SpringMVC 后缀模式
通过 在 请求地址上 添加 后缀(扩展)名的方法, 返回 指定类型结果。 常用的后缀名 是 json 和 xml . 在 Spring 5.3 版本之后,不再推荐该模式。
启用 后缀 模式的 步骤
-
启用 后缀支持 (可以解决 添加 后缀后 抛出 404 错误)
@EnableWebMvc public class WebMvcConfig implements WebMvcConfigurer { @Override public void configurePathMatch(PathMatchConfigurer configurer) { // 启用 后缀模式 configurer.setUseSuffixPatternMatch(true) ; } }
-
配置一个 内容导航 管理器
@EnableWebMvc public class WebMvcConfig implements WebMvcConfigurer { @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { // 启用 路径后缀的支持 configurer.favorPathExtension(true) ; // 设置 支持的媒体类型 configurer.mediaType("json", MediaType.APPLICATION_JSON) ; configurer.mediaType("xml", MediaType.APPLICATION_XML) ; configurer.ignoreAcceptHeader(true); // 设置 默认支持的媒体类型为 JSON configurer.defaultContentType(MediaType.APPLICATION_JSON) ; // 启用 参数格式化 configurer.favorParameter(true) ; // format=json , format=xml // configurer.parameterName() ; 设置参数名,默认是 format } }
SpringMVC 导出 Excel
-
导入 依赖包
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.3</version> </dependency>
-
编写一个Excel , 用来 存储 和 导出 Excel 相关的配置信息
@Data @NoArgsConstructor @AllArgsConstructor @Builder public class Excel { private List<List<String>> titles ; private List<Map<String, Object>> data ; private String filename ; private String sheetName ; }
-
编写一个类、继承 AbstractXlsView
@Component("excel") public class ResourceExportExcel extends AbstractXlsView { /** * model : 存储在 request 作用域中的 所有数据 * * workbook : 代表一个 excel 对象 * */ protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { Excel excel = (Excel) model.get("excel"); // 获取 生成 Excel 的 标头 List<List<String>> titles = excel.getTitles(); // 创建一个 sheet Sheet sheet = workbook.createSheet(excel.getSheetName()); // 通过 sheet 创建 一 行 Row titleRow = sheet.createRow(0); // 将 表头 写入到 Excel 中 int cellIndex = 0 ; for(List<String> title : titles) { // 通过 row 创建一个单元格 Cell cell = titleRow.createCell(cellIndex++); // 给单元格中添加数据 cell.setCellValue(title.get(1)); } // 获取 要导入的数据 List<Map<String, Object>> resourceVoList = excel.getData(); // 遍历数据 int rowIndex = 1 ; for(Map<String, Object> resource : resourceVoList) { // 创建 行 Row row = sheet.createRow(rowIndex++); // 遍历列 cellIndex = 0 ; for(List<String> title : titles) { // 通过 row 创建一个单元格 Cell cell = row.createCell(cellIndex++); // 给单元格中添加数据 Object val = resource.get(title.get(0)); // 将值存入到 单元格中 cell.setCellValue(String.valueOf(val)); } } String filename = excel.getFilename() ; filename = URLEncoder.encode(filename, StandardCharsets.UTF_8); // 设置下载头信息 response.setHeader("Content-Disposition", "attachment;filename="+ filename +".xls"); // 将 excel 进行下载 workbook.write(response.getOutputStream()); } }
-
编写 控制器 , 负责 实现 Excel 数据查询 ,并 导出 Excel
@GetMapping("/export") public String exportExcel(ModelMap model) { // 查询 要导出的所有资源 List<Map<String, Object>> resources = resourceService.queryResources(); // 获取 生成 Excel 的 标头 List<List<String>> titles = List.of( List.of("id", "编号"), List.of("resourceName", "资源名"), List.of("score", "资源积分"), List.of("ext", "后缀"), List.of("size", "资源大小"), List.of("keywords", "关键字"), List.of("typeName", "所属类型") ); Excel excel = Excel.builder() .filename("resource") .sheetName("资源信息") .data(resources) .titles(titles).build(); // 将查询的信息放入到 request作用域 model.put("excel", excel) ; // 返回 excel (excel 是一个 beanID) return "excel" ; }
-
配置 BeanName 解析器
@Bean public BeanNameViewResolver beanNameViewResolver() { BeanNameViewResolver beanNameViewResolver = new BeanNameViewResolver(); // 设置 beanName 解析器的优先级, 优先级传入的值越大,优先级越低 。默认值是 Integer.MAX_VALUE // 优先级 必须 比 InternalResourceViewResolver 高才可以 beanNameViewResolver.setOrder(10); return beanNameViewResolver ; }
SpringMVC 实现 WebSocket
- 引入 spring-websocket 依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
- 编写一个类 、继承 TextWebSocketHandler
- afterConnectionEstablished : 建立 链接后 执行的 方法
- handleTextMessage : 接收 客户端 发送的 消息
- handleTransportError : 当产生异常 、进行的逻辑处理
- afterConnectionClosed : 关闭 链接后 执行的 方法
- 编写一个 WebSocket 配置类
@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { /** * 注册 websocket处理器 * @param registry */ @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new ChatWebSocketHandler(), "/chat") ; } }
- 在 页面中 、链接 websocket 进行聊天 WebSocket