《Spring in action 4》(七)SpringMVC高级技术
SpringMVC高级技术
通过web.xml加载JavaConfig配置
我们前面使用了纯web.xml形式加载applicationContext.xml和springmvc.xml配置文件来搭建一个web项目,也使用AbstractAnnotationConfigDispatcherServletInitializer纯java配置的形式加载ServletConfig.java和RootConfig.java配置类来搭建web项目,这次我们使用web.xml形式结合ServletConfig.java和RootConfig.java搭建项目。
web.xml文件如下:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--使用 Java配置形式来加载ApplicationContext/RootConfig.java配置类-->
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.ooyhao.spring.config.RootConfig</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置 Java配置形式的DispatcherServlet-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.ooyhao.spring.config.ServletConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
web加载配置类实现上传文件
POM文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ooyhao.spring</groupId>
<artifactId>spring-in-action-07-01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>spring-in-action-07-01 Maven Webapp</name>
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--导入Servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!--导入SpringMVC依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--导入Jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
<!--Junit测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--hibernate参数校验依赖-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Alpha3</version>
</dependency>
<!--整合Thymeleaf-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>spring-in-action-07-01</finalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
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_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>Archetype Created Web Application</display-name>
<!--使用 Java配置形式来加载ApplicationContext/RootConfig.java配置类-->
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name> contextConfigLocation</param-name>
<param-value>com.ooyhao.spring.config.RootConfig</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!--配置 Java配置形式的DispatcherServlet-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.ooyhao.spring.config.ServletConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<multipart-config>
<location>D:\data</location>
<max-file-size>52428800</max-file-size>
<max-request-size>52428800</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
我们这里使用了web.xml文件来加载配置类的形式搭建web项目。为了实现文件上传,我们可以看出这个web.xml中与前一节不同的是,多个下面几行代码:
<multipart-config>
<location>D:\data</location>
<max-file-size>52428800</max-file-size>
<max-request-size>52428800</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>
这个就是用于配置文件上传的一些信息,
- location:表示文件上传存储的位置。
- max-file-size:上传文件的最大容量(以字节为单位)。默认是没有限制的。
- max-request-size:整合multipart请求的最大容量(以字节为单位),不会关心有多少个part以及每一个part的大小,默认是没有限制的。
- file-size-threshold:在上传过程中,如果文件大小达到了一个指定最大容量(以字节为单位),将会写入到临时文件路径中。默认为0,也就是所有上传的文件都会写入到磁盘上。
ServletConfig
/**
* 描述:
* 类【ServletConfig】
*
* @author ouYangHao
* @create 2019-09-09 16:19
*/
/*相当于springmvc.xml*/
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.ooyhao.spring.**.controller")
public class ServletConfig{
@Bean
public MultipartResolver multipartResolver(){
StandardServletMultipartResolver resolver =
new StandardServletMultipartResolver();
return resolver;
}
@Bean
public SpringResourceTemplateResolver templateResolver(){
SpringResourceTemplateResolver resolver =
new SpringResourceTemplateResolver();
resolver.setPrefix("/WEB-INF/templates/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("utf-8");
resolver.setCacheable(true);
resolver.setTemplateMode(TemplateMode.HTML);
return resolver;
}
@Bean
public SpringTemplateEngine templateEngine(){
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
engine.setEnableSpringELCompiler(true);
return engine;
}
@Bean
public ThymeleafViewResolver viewResolver(){
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setCharacterEncoding("utf-8");
resolver.setTemplateEngine(templateEngine());
return resolver;
}
}
配置文件中与以往不同的是,增加了一个MultipartResolver。
register.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Blog</title>
<meta charset="utf-8">
</head>
<style>
span.error{
color:red;
}
div.errors{
background-color: #ffcccc;
border: 2px solid red;
}
label.error{
color:red;
}
input.error{
background-color: #ffcccc;
}
</style>
</head>
<body>
<h1>欢迎加入Spring的大家庭</h1>
<form method="post" th:object="${user}" enctype="multipart/form-data" >
<div class="errors" th:if="${#fields.hasErrors('*')}" >
<ul>
<li th:each="err : ${#fields.errors('*')}" th:text="${err}" >
Input is Incorrect
</li>
</ul>
</div>
<label th:class="${#fields.hasErrors('username')} ? 'error' ">
账号:
</label>
<input type="text" th:field="*{username}"
th:class="${#fields.hasErrors('username')} ? 'error'"/><br>
<label th:class="${#fields.hasErrors('password')} ? 'error' ">
密码:</label>
<input type="password" th:field="*{password}"
th:class="${#fields.hasErrors('password')} ? 'error'" /><br>
<label th:class="${#fields.hasErrors('age')} ? 'error' ">年龄:</label>
<input th:field="*{age}"
th:class="${#fields.hasErrors('age')} ? 'error'"/><br>
<label> 图片:</label>
<input type="file" name="file"
accept="image/jpeg,image/png,image/gif" ><br>
<input type="submit" value="提交" ><br>
</form>
</body>
</html>
IndexController.java
package com.ooyhao.spring.controller;
import com.ooyhao.spring.bean.User;
import com.ooyhao.spring.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.Valid;
import java.io.File;
import java.io.IOException;
@Controller
public class IndexController {
@Autowired
private UserService userService;
@GetMapping("/")
public String home(){
return "home";
}
@GetMapping("/register")
public String toRegister(Model model){
model.addAttribute("user",new User());
return "register";
}
/*处理表单数据,并验证*/
@PostMapping("/register")
public String register(@Valid User user, MultipartFile file,
BindingResult bindingResult, Model model)
throws IOException {
if (bindingResult.hasErrors()){
System.out.println("错误数目:" + bindingResult.getErrorCount());
model.addAttribute(user);
return "register";//注册失败,重新返回到注册页面
}
if (!file.isEmpty()){
file.transferTo(
new File(System.currentTimeMillis()
+file.getOriginalFilename().substring(
file.getOriginalFilename().indexOf("."))));
}
userService.saveUser(user);
return "redirect:/registerSuccess";
}
@GetMapping("/registerSuccess")
public String registerSuccess(){
return "registerSuccess";
}
@GetMapping("/registerFail")
public String registerFail(){
return "registerFail";
}
}
Controller接收前端上传文件普通方式是使用byte[],或是Part接收,如下:
@PostMapping("/register")
public String register(@RequestPart("file") byte[] file){}
@PostMapping("/register")
public String register(@RequestPart("file") Part file){}
但是,我们一般是使用SpringMVC的MultipartFile来接收文件。下面看一下这个接口的方法:
public interface MultipartFile extends InputStreamSource {
String getName();
@Nullable
String getOriginalFilename();
@Nullable
String getContentType();
boolean isEmpty();
long getSize();
byte[] getBytes() throws IOException;
@Override
InputStream getInputStream() throws IOException;
default Resource getResource() {
return new MultipartFileResource(this);
}
void transferTo(File dest) throws IOException, IllegalStateException;
default void transferTo(Path dest) throws IOException, IllegalStateException {
FileCopyUtils.copy(getInputStream(), Files.newOutputStream(dest));
}
}
效果图:
纯配置文件形式实现文件上传
POM依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ooyhao.spring</groupId>
<artifactId>spring-in-action-07-02</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>spring-in-action-07-02 Maven Webapp</name>
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--导入Servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!--导入SpringMVC依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--导入Jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
<!--Junit测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--hibernate参数校验依赖-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Alpha3</version>
</dependency>
<!--整合Thymeleaf-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>spring-in-action-07-02</finalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
WebInit
public class WebInit extends
AbstractAnnotationConfigDispatcherServletInitializer {
/*配置文件上传的一些参数*/
/**
* MultipartConfigElement与 <multipart-config></multipart-config> 的默认值相同。
* 都必须配置保存路径。
* @param registration
*/
@Override
protected void customizeRegistration(
ServletRegistration.Dynamic registration){
MultipartConfigElement configElement = new MultipartConfigElement(
"D:\\data\\upload\\",
52428800,
52428800,
0);
registration.setMultipartConfig(configElement);
}
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{
RootConfig.class
};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{
ServletConfig.class
};
}
@Override
protected String[] getServletMappings() {
return new String[]{
"/"
};
}
}
RootConfig.java
package com.ooyhao.spring.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@ComponentScan(basePackages = "com.ooyhao.spring", useDefaultFilters = true,excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = EnableWebMvc.class)
})
@Configuration
public class RootConfig {
}
ServletConfig.java
package com.ooyhao.spring.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
@EnableWebMvc //开启webmvc
@ComponentScan(basePackages = "com.ooyhao.spring.**.controller")
@Configuration //标注为一个配置类
public class ServletConfig implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
/*配置文件上传*/
@Bean
public MultipartResolver multipartResolver(){
StandardServletMultipartResolver resolver = new StandardServletMultipartResolver();
return resolver;
}
/*配置模板解析器*/
@Bean
public SpringResourceTemplateResolver templateResolver(){
SpringResourceTemplateResolver resolver =
new SpringResourceTemplateResolver();
resolver.setCacheable(true);
resolver.setPrefix("/WEB-INF/templates/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("utf-8");
resolver.setTemplateMode(TemplateMode.HTML);
return resolver;
}
/*配置模板引擎*/
@Bean
public SpringTemplateEngine templateEngine(){
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setEnableSpringELCompiler(true);
engine.setTemplateResolver(templateResolver());
return engine;
}
/*配置视图解析器*/
@Bean
public ThymeleafViewResolver thymeleafViewResolver(){
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding("utf-8");
return resolver;
}
}
IndexController
package com.ooyhao.spring.controller;
import com.ooyhao.spring.bean.User;
import com.ooyhao.spring.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.Valid;
import java.io.File;
import java.io.IOException;
@Controller
public class IndexController {
@Autowired
private UserService userService;
@GetMapping("/")
public String home(){
return "home";
}
@GetMapping("/register")
public String toRegister(Model model){
model.addAttribute("user",new User());
return "register";
}
/*处理表单数据,并验证*/
@PostMapping("/register")
public String register(@Valid User user, MultipartFile file, BindingResult bindingResult, Model model) throws IOException {
if (bindingResult.hasErrors()){
System.out.println("错误数目:" + bindingResult.getErrorCount());
model.addAttribute(user);
return "register";//注册失败,重新返回到注册页面
}
if (!file.isEmpty()){
file.transferTo(new File(System.currentTimeMillis()+file.getOriginalFilename().substring(file.getOriginalFilename().indexOf("."))));
}
userService.saveUser(user);
return "redirect:/registerSuccess";
}
@GetMapping("/registerSuccess")
public String registerSuccess(){
return "registerSuccess";
}
@GetMapping("/registerFail")
public String registerFail(){
return "registerFail";
}
}
效果
总结
至此,我们已经实现了使用纯Java配置的方式搭建了一个Springweb项目,并且通过配置的方式实现了文件上传。
处理异常
使用@ResponseStatus
我们看一下下面这个代码:
/*将参数写到路径上*/
@GetMapping("/article/{id}")
@ResponseBody
public Article article(@PathVariable(value = "id") Integer id){
Article article = null;
try{
article = articleService.findArticleById(id);
}catch (ArticleNotFoundException e){
e.printStackTrace();
}
return article;
}
//Service:
public Article findArticleById(Integer id) {
Article article = articles.get(id);
if (article == null){
throw new ArticleNotFoundException();
}
return article;
}
public class ArticleNotFoundException extends RuntimeException {}
当我们访问时,如果findArticleById(id)查询出来为空时,按照当前的代码,会报500错误,但是正常情况我们希望抛出的是404.所以,在定义异常的时候,可以定义为:
@ResponseStatus(value = HttpStatus.NOT_FOUND,reason = "Not Found")
public class ArticleNotFoundException extends RuntimeException {}
我们看一下@ResponseStatus注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseStatus {
@AliasFor("code")
HttpStatus value() default HttpStatus.INTERNAL_SERVER_ERROR;
@AliasFor("value")
HttpStatus code() default HttpStatus.INTERNAL_SERVER_ERROR;
String reason() default "";
}
测试结果:
编写异常处理方法
如上一节中处理异常方法所示,在Handler中既包含正常业务逻辑代码,有包含处理异常的代码,那么有什么方法可以将异常处理代码剥离出来,Handler中只处理业务逻辑。
@GetMapping("/article/{id}")
@ResponseBody
public Article article(@PathVariable(value = "id") Integer id){
Article article = articleService.findArticleById(id);
return article;
}
编写异常方法,即在当前controller中添加下列方法:
@ExceptionHandler(ArticleNotFoundException.class)
@ResponseBody
public Map<String,Object> handleNotFoundException(){
Map<String,Object> map = new HashMap<>();
map.put("code",404);
map.put("message","数据未查询到");
return map;
}
测试结果:
控制器通知处理异常
我们都知道,在正常的项目中,我们不可能将所有的处理方法都写在一个Controller中,为了区分业务模块,我们会将所有的controller方法划分在不同的controller类中。如果此时还是使用上述处理方法来处理异常的话,那么我们会发现很多相同的处理异常的代码。Spring3.2之后,有了统一的处理方案就是使用@ControllerAdvice。
包含@ControllerAdvice的方法可以包含一个或多个如下类型的方法:
- @ExceptionHandler标注的方法
- @InitBinder标注的方法
- @ModelAttribute标注的方法
@ExceptionHandler
/**
@ExceptionHandler的作用主要在于声明一个或多个类型的异常,当符合条件的Controller抛出这些异常之后将会对这些异常进行捕获,然后按照其标注的方法的逻辑进行处理,从而改变返回的视图信息。
*/
@ControllerAdvice
public class SpringControllerAdvice {
@ExceptionHandler(RuntimeException.class)
public ModelAndView runtimeExceptionHandler(RuntimeException e) {
e.printStackTrace();
return new ModelAndView("error");
}
}
@InitBandler
/**
对于@InitBinder,该注解的主要作用是绑定一些自定义的参数。一般情况下我们使用的参数通过@RequestParam,@RequestBody或者@ModelAttribute等注解就可以进行绑定了,但对于一些特殊类型参数,比如Date,它们的绑定Spring是没有提供直接的支持的,我们只能为其声明一个转换器,将request中字符串类型的参数通过转换器转换为Date类型的参数,从而供给@RequestMapping标注的方法使用。
*/
@ControllerAdvice
public class SpringControllerAdvice {
@InitBinder
public void globalInitBinder(WebDataBinder binder) {
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
}
}
@ModelAttribute
/**
关于@ModelAttribute的用法,除了用于方法参数时可以用于转换对象类型的属性之外,其还可以用来进行方法的声明。如果声明在方法上,并且结合@ControllerAdvice,该方法将会在@ControllerAdvice所指定的范围内的所有接口方法执行之前执行,并且@ModelAttribute标注的方法的返回值还可以供给后续会调用的接口方法使用。
*/
public class GlobalExceptionHandler {
@ModelAttribute
//应用到所有@RequestMapping注解方法
//此处将键值对添加到全局,注解了@RequestMapping的方法都可以获得此键值对
public void addUser(Model model) {
model.addAttribute("msg", "此处将键值对添加到全局,注解了@RequestMapping的方法都可以获得此键值对");
}
}
实例
@ControllerAdvice
public class GlobalExceptionHandler {
private final static String ERROR_PAGE = "error";
//异常处理
@ExceptionHandler(Exception.class)
public ModelAndView handle(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("message", e.getMessage());
mv.setViewName(ERROR_PAGE);
return mv;
}
@ModelAttribute
//应用到所有@RequestMapping注解方法
//此处将键值对添加到全局,注解了@RequestMapping的方法都可以获得此键值对
public void addUser(Model model) {
model.addAttribute("msg", "此处将键值对添加到全局,注解了@RequestMapping的方法都可以获得此键值对");
}
@InitBinder
//应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
//用来设置WebDataBinder,用于自动绑定前台请求参数到Model中。
public void initBinder(WebDataBinder binder) {
}
}
全局异常处理
/**
* 描述:
* 类【GlobalExceptionHandler】
*
* @author ouYangHao
* @create 2019-09-10 10:51
*/
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ArticleNotFoundException.class)
@ResponseBody
public Map<String,Object> handleNotFoundException(){
Map<String,Object> map = new HashMap<>();
map.put("code",404);
map.put("message","数据未查询到");
return map;
}
}
跨重定向请求传递数据
使用URL模板进行重定向
可以使用下列方式来传递参数
/*将参数写到路径上*/
@GetMapping("/article/{id}")
public String article(@PathVariable(value = "id") Integer id, Model model){
model.addAttribute("id",id);
model.addAttribute("username","ooyhao");
return "redirect:/article/id/{id}";
}
@RequestMapping("/article/id/{id}")
@ResponseBody
public String articleId(@PathVariable(value = "id") Integer id){
return "id:"+id;
}
但是上述方式只能传递普通参数,当我们需要传递对象的时候,这种方式就无能为力了。此时我们可以使用Spring flash来传递。
代码如下:
@GetMapping("/article/{id}")
public String article(@PathVariable(value = "id") Integer id, RedirectAttributes attributes){
Article article = articleService.findArticleById(id);
attributes.addAttribute("id",id);
attributes.addAttribute("username","ooyhao");
attributes.addFlashAttribute(article);
return "redirect:/article/id/{id}";
}
@RequestMapping("/article/id/{id}")
@ResponseBody
public Map<String,Object> articleId(@PathVariable(value = "id") Integer id,String username, Model model){
Map<String,Object> map = new HashMap<>();
map.put("id",id);
map.put("username",username);
map.put("article",model.asMap().get("article"));
return map;
}
测试结果:
总结:本节实现了文件上传,异常处理,以及跨重定向请求传递参数。
源码:
https://gitee.com/ooyhao/JavaRepo_Public/tree/master/Spring-in-Action/spring-in-action-07
最后
如果觉得不错的话,那就关注一下小编哦!一起交流,一起学习