05-SpringBoot整合Spring MVC

nobility 发布于 2022-06-01 2350 次阅读


SpringBoot整合Spring MVC

使用官方提供的spring-boot-starter-web启动器,默认配好了SpringMVC全套功能,该启动器依赖了spring-boot-starter根启动器,以及spring-boot-starter-jsonJSON序列化和spring-boot-starter-tomcatweb容器

静态资源访问

  • 静态资源访问:默认开启;可使用spring.web.resources.add-mappings配置项进行修改
  • 资源目录:默认静态资源目录存约定的是/static,也可以是/public/resources/META-INF/resources;可使用spring.web.resources.static-locations配置项进行更改
  • 访问前缀:默认静态资源访问URL无前缀;可使用spring.mvc.static-path-pattern配置项进行更改
  • 默认首页:默认欢迎页是从静态资源目录中寻找index.html,若找不到则会去模板目录中找index模板
  • 网站图标:默认favicon.ico图标在静态资源目录的根下
  • 静态资源缓存:可使用spring.web.resources.cache.period配置静态资源的缓存时间,单位秒

特殊的请求参数

REST风格表单提交

默认关闭,开启后可使用在表单提交中使用一个name值为_methodvalue值为一个Http请求动词的一个表单项再进行POST提交(一般使用隐藏表单);可使用spring.mvc.hiddenmethod.filter.enabled配置项进行修改

html代码:

<h1>REST风格测试:</h1>
<form action="/get" method="get">
    <input type="submit" value="get">
</form>
<form action="/post" method="post">
    <input type="submit" value="post">
</form>
<form action="/put" method="post">
    <input type="submit" value="put">
    <input type="hidden" name="_method" value="put">
</form>
<form action="/delete" method="post">
    <input type="submit" value="delete">
    <input type="hidden" name="_method" value="delete">
</form>

控制器代码:

import org.springframework.web.bind.annotation.*;

@RestController
public class MyController {
  @GetMapping("/get")
  public String get() {
    return "get";
  }

  @PostMapping("/post")
  public String post() {
    return "post";
  }

  @PutMapping("/put")
  public String put() {
    return "put";
  }

  @DeleteMapping("/delete")
  public String delete() {
    return "delete";
  }
}

矩阵变量

默认关闭,开启后可在请求路径使用分号分隔添加矩阵变量,相当于是路径变量又携带了变量,就像这样:

  • 多个矩阵变量再使用分号进行分隔:/path;name=zhangsan;message=message
  • 多个矩阵变量的值使用逗号进行分隔:/path;message=message1,message2
  • 多个路径变量携带矩阵变量直接反斜杠分隔:/path1;message=message1/path2/path3;message=message2

可自定义配置类,进行下面配置,开启该功能

@Configuration
public class MyConfiguration {
  @Bean
  public WebMvcConfigurer webMvcConfigurer() {
    return new WebMvcConfigurer() {  //创建自定义WebMvc配置类对象添加到容器中,以覆盖原有的
      @Override
      public void configurePathMatch(PathMatchConfigurer configurer) {  //重写路径解析方法
        UrlPathHelper urlPathHelper = new UrlPathHelper();  //创建URL路径解析器对象
        urlPathHelper.setRemoveSemicolonContent(false);  //设置为不删除分号
        configurer.setUrlPathHelper(urlPathHelper);  //将该URL路径解析对象设置到路径匹配器中
      }
    };
  }
}

使用下面代码进行测试

@RestController
public class Main {
  @GetMapping("/test1/{path}")
  //测试路径:/test1/path;name=zhangsan;message=message
  public String test1(@MatrixVariable("name") String name,
                      @MatrixVariable("message") String message) {
    return name + ":" + message;
  }

  @GetMapping("/test2/{path}")
  //测试路径:/test2/path;message=message1,message2
  public String test2(@MatrixVariable("message") List<String> message) {
    return message.toString();
  }

  @GetMapping("/test3/{path1}/path2/{path3}")
  //测试路径:/test3/path1;message=message1/path2/path3;message=message2
  public String test3(@MatrixVariable(pathVar = "path1", value = "message") String message1,   //pathVar指定是那个路径变量
                      @MatrixVariable(pathVar = "path2", value = "message") String message2) {
    return message1 + ":" + message2;
  }
}

拦截器

同时设置多个拦截器的执行顺序为,请求时按照配置顺序进行执行,响应时按照配置逆序执行,若期间某个拦截器出现了异常,则后续拦截器不会被执行,而是直接逆序执行afterCompletion()方法,而不会执行postHandle()方法

为SpringMVC添加拦截器,向SpringMVC中添加HandlerInterceptor实例,重写preHandle()postHandle()afterCompletion()方法,这三个方法都是默认方法

  • preHandle():前置执行处理,进入控制器之前执行,若返回false则后续不会执行
  • postHandle():目标资源已被SpringMVC进行处理,即控制器方法被执行完(return了之后)后执行
  • afterCompletion():响应文本已产生,即响应HTML字符串或JSON字符串生成后执行

自定义的拦截器类如下:

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("前置处理");
    return true;  //返回true后续会接着执行
  }

  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    System.out.println("控制器方法执行完毕");
  }

  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    System.out.println("响应文本已经生成");
  }
}

可自定义配置类,进行下面配置:

@Configuration
public class MyConfiguration {
  @Bean
  public WebMvcConfigurer webMvcConfigurer() {
    return new WebMvcConfigurer() {  //创建自定义WebMvc配置类对象添加到容器中,以覆盖原有的
      @Override
      public void addInterceptors(InterceptorRegistry registry) {  //重写添加拦截器方法,为SpringMVC添加拦截器
        registry.addInterceptor(new MyInterceptor())  //添加自己的拦截器对象,可链式调用
            .addPathPatterns("/**")  //要拦截的路径,拦截全部请求,静态资源也会被拦截器拦截到
            .excludePathPatterns("*.js", "*.css", "*.png");  //不拦截的路径
      }
    };
  }
}

错误处理

默认情况下,SpringBoot提供了/error请求路径,用于处理所有为捕获的异常,浏览器客户端直接请求则返回HTML错误页,否则返回JSON格式错误数据

自定义错误页

错误页面可自定义,放在静态资源目录或模板文件目录下error目录下,文件名如下:模板文件目录下可以是对应的模板引擎后缀名

  • 404.html500.html:精确匹配到404和500错误
  • 4xx.html5xx.html:无法精确匹配到404或500错误,会响应概括性的页面4xx或5xx错误

全局异常处理器

在异常处理器类上使用@ControllerAdvice注解,异常处理器方法上使用@ExceptionHandler注解指定要处理的异常,之后在控制器中抛出异常时,异常处理器中匹配的方法就会执行

@ControllerAdvice
public class MyExceptionHandler {
  @ExceptionHandler(value = {Exception.class})  //数组形式,可指定多个异常
  @ResponseBody
  public String error(Exception e) {  //与控制器中的方法用法一致
    return "出现异常" + e.getMessage();
  }
}

自定义异常

使用@ResponseStatus来指定该异常Http响应状态码和响应信息,当抛出该异常给/error时会以定义好的Http状态码进行返回

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "404 Not Found")
public class MyException extends RuntimeException {
  public MyException(String message) {
    super(message);
  }
}

嵌入式容器

若导入多个或没有导入Web容器时,就会抛出异常

服务器常用配置项如下

属性名 默认值 描述
server.servlet.session.timeout 30m 默认的session超时时间
server.port 8080 服务器监听端口
server.tomcat.accesslog.directory logs 日志文件的目录。可以是绝对路径,也可以相对于Tomcat路径
server.tomcat.accesslog.enabled false 启用访问日志

导入web开发场景包,SpringBoot就会创建一个web版本是IOC容器ServletWebServApplicationContext,该容器启动时会使用ServletWebServerFactory接口的实例来创建Web容器,默认导入的是tomcat,若想使用其他服务器,就需要先将默认导入的tomcat排除,在导入想要导入的服务器即可,具体做法如下

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <exclusions>  <!--要排除的依赖-->
    <exclusion>  <!--排除tomcat-->
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
    </exclusion>
  </exclusions>
</dependency>

<dependency>  <!--导入jetty服务器-->
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

常见问题与解决

自定义格式转化器

为SpringMVC添加自定义格式转化器,向SpringMVC中添加Converter实例,重写convert()方法,可用于转化任意类型,可自定义配置类,进行下面配置

@Configuration
public class MyConfiguration {
  @Bean
  public WebMvcConfigurer webMvcConfigurer() {  //并为覆盖所有的配置,想要全面接管SpringMVC的配置就需要在配置类上添加@EnableWebMvc注解
    return new WebMvcConfigurer() {  //创建自定义WebMvc配置类对象添加到容器中,以覆盖原有的,其实是合并
      @Override
      public void addFormatters(FormatterRegistry registry) {  //重写添加格式转换器方法,为SpringMVC添加格式转换器
        registry.addConverter(new Converter<String, Date>() {  //向SpringMVC中增加转换器实例,泛型表示将String转化为Date类型
          @Override
          public Date convert(String string) {  //实现格式转化方法
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
            try {
              return simpleDateFormat.parse(string);  //返回转化后的日期对象
            } catch (ParseException e) {
              return null;  //解析错误返回null
            }
          }
        });
      }
    };
  }
}

使用下面代码进行测试

import org.springframework.web.bind.annotation.*;

import java.util.Date;

@RestController
public class Main {
  @GetMapping("/test")
  //测试路径:/test?date=1970-01-01 00:00:00.000
  public String test1(@RequestParam Date date) {
    return date.toString();
  }
}

文件上传

默认开启,但是限制大小,可通过下面配置项进行更改

属性名 默认值 描述
spring.servlet.multipart.max-file-size 1MB 单个文件的最大值
spring.servlet.multipart.max-request-size 10MB 上传文件总的最大值

控制器代码

@PostMapping("/upload")
@ResponseBody
public String upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) {  //若是多文件上传使用List集合接收参数即可
  String uploadDir = "upload";  //上传文件目录
  if (!file.isEmpty()) {  //若有上传
    String filename = file.getOriginalFilename();  //获取原来的文件名
    String path = request.getServletContext().getRealPath(uploadDir);  //获取上传目录的的绝对路径

    int idx = filename.lastIndexOf(".");
    String exName = filename.substring(idx);  //截取文件后缀名
    filename = UUID.randomUUID().toString().replace("-", "") + exName;  //生成随机UUID拼接后缀名,修改文件名避免上传重名文件被覆盖

    try {
      file.transferTo(new File(path + File.separator + filename));  //文件转存
      String fileRPath = request.getServletContext().getContextPath() + "/" + uploadDir + "/" + filename; //拼接文件符合src的相对路径
      return "上传成功,URL地址为:" + fileRPath;
    } catch (IOException e) {
      e.printStackTrace();
      return "上传失败";
    }
  }
  return "未上传";
}

用于上传的测试页面

<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
    <!-- 必须指定enctype为multipart/form-data -->
    <input type="text" name="name">
    <input type="file" name="file">
    <input type="submit" value="上传">
</form>

模板引擎

freemarker

freemarker模板的具体语法可从官方文档中查看,在pom.xml中引入freemarker的启动器即可

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

freemarker常用配置项如下:

属性名 默认值 描述
spring.freemarker.suffix .ftlh 模板文件后缀
spring.freemarker.template-loader-path [classpath:/templates/] 模板文件所在目录
spring.freemarker.content-type text/html 写入HTTP响应的Content-Type值
spring.freemarker.charset UTF-8 模板文件字符编码
spring.freemarker.cache false 是否启用模板缓存
thymeleaf

thymeleaf模板的具体语法可从官方文档中查看,在最外层的<html>标签上添加xmlns:th="http://www.thymeleaf.org"名称空间即可有代码提示,在pom.xml中引入freemarker的启动器即可

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

thymeleaf常用配置项如下:

属性名 默认值 描述
spring.thymeleaf.suffix .html 模板文件后缀
spring.thymeleaf.prefix classpath:/templates/ 模板文件所在目录
spring.thymeleaf.encoding UTF-8 模板文件字符编码
spring.thymeleaf.servlet.content-type text/html 写入HTTP响应的Content-Type值
spring.thymeleaf.cache true 是否启用模板缓存
jsp

SpringBoot官方已经不推荐使用JSP了,确实操作起来也比较麻烦

<dependency>  <!--内嵌的tomcat对jsp的支持模块 -->
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-jasper</artifactId>
  <scope>provided</scope>
</dependency>
<dependency>  <!-- 对jstl的支持 -->
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
</dependency>

在配置文件下需要再进行视图引擎的配置,配置项如下:再手动创建/src/main/webapp目录,将其设置为源文件目录,再此目录中进行jsp页面的编写

属性名 描述 设置为
spring.mvc.view.suffix 模板文件后缀 /WEB-INF/view(对应的是webapp目录)
spring.mvc.view.prefix 模板文件所在目录 .jsp

原生组件注入

所谓原生组件是Servlet、Filter、Listener这些组件,这些组件的定义和原生Web开发一样,使用@WebServlet@WebFilter@WebListener注解进行标识

原生组件不会经过SpringMVC的拦截器等专属SpringMVC的组件

不同的是之前是在web.xml中进行配置的,在SpringBoot中有两种方式可以注册这些组件

  1. 在SpringBoot启动类中配置ServletComponentScan注解指定原生组件的包,进行原生组件的包扫描
  2. 自定义配置类,向容器中注入ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean的实例
@Configuration
public class MyConfiguration {
  @Bean
  public ServletRegistrationBean servletBean() {
    Servlet servlet = new MyServlet();  //自定义的原生组件对象
    return new ServletRegistrationBean(servlet, "/url");  //绑定的url访问路径
  }

  @Bean
  public FilterRegistrationBean filterBean() {
    Filter filter = new MyFilter();  //自定义的原生组件对象
    //FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(filter, servletBean());  //可绑定servlet
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(filter);  //也可单独进行设置
    filterRegistrationBean.setUrlPatterns(Arrays.asList("/url"));  //绑定的url访问路径
    return filterRegistrationBean;
  }

  @Bean
  public ServletListenerRegistrationBean listenerBean() {
    ServletContextListener servletContextListener = new MyServletContextListener();  //自定义的原生组件对象
    return new ServletListenerRegistrationBean(servletContextListener);  //注册监听器
  }
}

全局跨域请求

为SpringMVC添加全局跨域请求配置,向SpringMVC中添加跨域请求映射,可自定义配置类,进行下面配置

@Configuration
public class MyConfiguration {
  @Bean
  public WebMvcConfigurer webMvcConfigurer() {  //并为覆盖所有的配置
    return new WebMvcConfigurer() {  //创建自定义WebMvc配置类对象添加到容器中,以覆盖原有的
      @Override
      public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/prefix/**")  // 对 /prefix 父路径下的所有路径都可以跨域访问
            .allowedOrigins("http://www.baidu.com");  //允许所有域名
      }
    };
  }
}
此作者没有提供个人介绍
最后更新于 2022-06-01