《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。

https://www.cnblogs.com/yanggb/p/10859907.html

包含@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

最后

如果觉得不错的话,那就关注一下小编哦!一起交流,一起学习

posted @ 2019-09-21 10:45  ooyhao  阅读(413)  评论(0编辑  收藏  举报