SpringBoot Web开发

1. SpringBoot Web开发

简介

使用SpringBoot的步骤:

  1. 创建一个SpringBoot应用,选择需要的模块,SpringBoot就会默认将需要的模块自动配置好。
  2. 手动在配置文件中配置部分配置项目就可以运行起来了。
  3. 专注编写业务代码,不需要考虑以前那样一大堆的配置了。

xxxxAutoConfigurartion:自动配置类,给容器中添加组件

xxxxProperties:封装配置文件中相关属性

☆SpringBoot Web开发需要解决的问题:

  • 导入静态资源
  • 首页
  • jsp,模板引擎Thymeleaf
  • 增删改查
  • 拦截器
  • 国际化

2. 静态资源处理

2.1 第一种静态资源映射规则

写请求非常简单,要引入前端资源,项目中有许多的静态资源,比如css,js等文件,这 个SpringBoot怎么处理呢?

如果是一个web应用,main下会有一个webapp,以前都是将所有的页面导在这里面的。但是现在的pom呢,打包方式是为jar的方式,这种方式SpringBoot也是可以写页面的。但是SpringBoot对于静态资源放置的位置,是有规定的。

静态资源映射规则:

SpringBoot中,SpringMVC的web配置都在 WebMvcAutoConfiguration 这个配置类里面。

WebMvcAutoConfigurationAdapter 中有很多配置方法,有一个方法: addResourceHandlers 添加资源处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(new Resource[]{resource});
}

});
}
}

源代码理解:所有的 /webjars/** ,都需要去 classpath:/META-INF/resources/webjars/ 找对应的资源。

什么是WebJars

webjars官网:https://www.webjars.org

概述:

对于日常的web开发而言,像css、js、images、font等静态资源文件管理是非常的混乱的、比如jQuery、Bootstrap、Vue.js等,可能每个框架使用的版本都不一样、一不注意就会出现版本冲突或者重复添加的问题。所以找到了一个叫做WebJars的技术,下面我将介绍如何将静态资源打包成jar。
原本我们在进行web开发时,一般上都是讲静态资源文件放置在webapp目录下,在SpringBoot里面,一般是将资源文件放置在src/main/resources/static目录下。而在Servlet3中,允许我们直接访问WEB-INF/lib下的jar包中的/META-INF/resources目录资源,即WEB-INF/lib/{*.jar}/META-INF/resources下的资源可以直接访问。
所以其实,WebJars也是利用了此功能,将所有前端的静态文件打包成一个jar包,这样对于引用放而言,和普通的jar引入是一样的,还能很好的对前端静态资源进行管理。

WebJars是一个很神奇的东西,可以让大家以jar包的形式来使用前端的各种框架、组件。

什么是WebJars:

WebJars是将客户端(浏览器)资源(JavaScript,Css等)打成jar包文件,以对资源进行统一依赖管理。WebJars的jar包部署在Maven中央仓库上。

为什么使用:

我们在开发Java web项目的时候会使用像Maven,Gradle等构建工具以实现对jar包版本依赖管理,以及项目的自动化管理,但是对于JavaScript,Css等前端资源包,我们只能采用拷贝到webapp目录下的手工方式,这样做就无法对这些资源进行依赖管理。而且容易导致文件混乱、版本不一致等问题。那么WebJars就提供给我们这些前端资源的jar包形式,我们就可以进行依赖管理。

WebJars是将这些通用的Web前端资源打包成Java的Jar包,然后借助Maven工具对其管理,保证这些Web资源版本唯一性,升级也比较容易。关于webjars资源,有一个专门的网站http://www.webjars.org/,我们可以到这个网站上找到自己需要的资源,在自己的工程中添加入maven依赖,即可直接使用这些资源了。

WebJars如何使用:

例如:SpringBoot要使用jQuery,我们只要要引入jQuery对应版本的pom依赖。

1
2
3
4
5
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.6.1</version>
</dependency>

底层源码映射原理:

QQ截图20221007202505

访问Jquery.js文件:只要是静态资源,SpringBoot就会去对应的路径寻找资源

访问:http://localhost:8080/webjars/jquery/3.6.1/jquery.js

测试结果:可以访问到静态资源

QQ截图20221007202838

2.2 第二种静态资源映射规则

项目中要是使用自己的静态资源该怎么导入呢?

找staticPathPattern发现第二种映射规则:/** , 访问当前的项目任意资源,它会去找 resourceProperties 这个类。

源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 进入方法
public String[] getStaticLocations() {
return this.staticLocations;
}
// 找到对应的值
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
// 找到路径
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
};

ResourceProperties 可以设置和静态资源有关的参数,这里面指向了它会去寻找资源的文件夹,即上面数组的内容。

得出结论:以下四个目录存放的静态资源可以被我们识别:

1
2
3
4
"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"

可以在resources根目录下新建对应的文件夹,都可以存放静态文件。

QQ截图20221007204211

优先级:resources > static > public

例如访问 http://localhost:8080/1.js,他就会去这些文件夹中寻找对应的静态资源文件。

自定义静态资源路径

也可以自己通过配置文件来指定一下,哪些文件夹是需要我们放静态资源文件的,在 application.properties中配置。

1
spring.resources.static-locations=classpath:/coding/classpath:/**/

一旦自己定义了静态文件夹的路径,原来的自动配置就都会失效了。

3. 首页和图标处理

3.1 自定义首页

源码分析:

1
2
3
4
5
6
7
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());
return welcomePageHandlerMapping;
}
1
2
3
4
5
6
7
8
9
10
11
12
private Optional<Resource> getWelcomePage() {
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
// ::是java8 中新引入的运算符
// Class::function的时候function是属于Class的,应该是静态方法
// this::function的funtion是属于这个对象的
// 简而言之,就是一种语法糖而已,是一种简写
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
// 欢迎页就是一个location下的index.html而已
private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
}

结论:静态资源文件夹下的所有 index.html 页面被 /** 映射。 映射为首页。

SpringBoot默认首页:

QQ截图20221007205739

自定义首页:

  1. public目录下新建index.html文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>首页</title>
    </head>
    <body>
    <h1>首页</h1>
    </body>
    </html>
  2. 测试

    访问 http://localhost:8080/ ,就会找静态资源文件夹下的 index.html

    QQ截图20221007210218

3.2 自定义图标

网站自定义图标说明:

与其他静态资源一样,Spring Boot在配置的静态内容位置中查找 favicon.ico。如果存在这样的文件,它将自动用作应用程序的favicon。

  1. 关闭SpringBoot默认图标

    1
    2
    #关闭默认图标
    spring.mvc.favicon.enabled=false
  2. 自己放一个图标在静态资源目录下,放在 public 目录下

  3. 清除浏览器缓存刷新网页,图标变成自定义

4. Thymeleaf

Thymeleaf 官网:https://www.thymeleaf.org/

4.1 模板引擎

模板引擎

模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。

前端交给我们的页面,是html页面。如果是以前开发,需要把他们转成jsp页面,jsp好处就是当我们查出一些数据转发到JSP页面以后,我们可以用jsp轻松实现数据的显示及交互等。

jsp支持非常强大的功能,包括能写Java代码,但是现在的这种情况,SpringBoot这个项目首先是以jar的方式,不是war。SpringBoot用的还是嵌入式的Tomcat,所以现在默认是不支持 jsp 的。

问题:不支持jsp,如果直接用纯静态页面的方式,给开发会带来非常大的麻烦,

解决办法:SpringBoot推荐使用模板引擎

其实jsp就是一个模板引擎,还有以用的比较多的freemarker,包括 SpringBoot推荐的Thymeleaf,模板引擎有非常多,但再多的模板引擎,他们的思想都是一样的。

模板引擎思想:

模板引擎

模板引擎的作用:

模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这就大大提升了开发效率,良好的设计也使得代码重用变得更加容易。

当写一个页面模板,比如有些值是动态的,写一些表达式。而这些值从哪来呢,就是我们在后台封装一些数据。然后把这个模板和这个数据交给我们模板引擎,模板引擎按照我们这个数据帮你把这表达式解析、填充到我们指定的位置,然后把这个数据最终生成一个我们想要的内容给我们写出去,这就是我们这个模板引擎,不管是jsp还是其他模板引擎,都是这个思想。只不过呢,就是说不同模板引擎之间,他们可能这个语法有点不一样。

引入Thymeleaf

如何引入:

找到对应的pom依赖:

1
2
3
4
5
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

thymeleaf 分析

首先得按照SpringBoot的自动配置原理看一下Thymeleaf的自动配置规则,在按照规则进行使用。

去找一下Thymeleaf的自动配置类:ThymeleafProperties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
}

可以在其中看到默认的前缀和后缀。

只需要把我们的html页面放在类路径下的templates下,thymeleaf就可以帮我们自动渲染了。

使用thymeleaf什么都不需要配置,只需要将他放在指定的文件夹下即可。

Thymeleaf使用:

  1. 编写一个TestController

    1
    2
    3
    4
    5
    6
    7
    8
    @Controller
    public class TestController {
    @RequestMapping("/t1")
    public String test1(){
    //classpath:/templates/test.html
    return "test";
    }
    }
  2. 编写一个测试页面 test.html 放在 templates 目录下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Title</title>
    </head>
    <body>
    <h1>测试页面</h1>
    </body>
    </html>
  3. 启动项目请求测试

4.2 Thymeleaf 语法学习

参考官方文档学习:Thymeleaf 官网:https://www.thymeleaf.org/

例:我们需要查出一些数据,在页面中展示。

  1. 修改测试请求,增加数据传输

    1
    2
    3
    4
    5
    6
    7
    @RequestMapping("/t1")
    public String test1(Model model){
    //存入数据
    model.addAttribute("msg","Hello,Thymeleaf");
    //classpath:/templates/test.html
    return "test";
    }
  2. 使用thymeleaf,需要在html文件中导入命名空间的约束,方便提示。

    1
    xmlns:th="http://www.thymeleaf.org"
  3. 编写前端页面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="UTF-8">
    <title>林慕椿</title>
    </head>
    <body>
    <h1>测试页面</h1>

    <!--th:text就是将div中的内容设置为它指定的值-->
    <div th:text="${msg}"></div>
    </body>
    </html>
  4. 启动测试

Thymeleaf的使用语法:

  1. 可以使用任意的 th:attr 来替换Html中原生属性的值

    Thymeleaf

测试:

  1. 编写一个Controller,放一些数据

    1
    2
    3
    4
    5
    6
    7
    8
    @RequestMapping("/t2")
    public String test2(Map<String,Object> map){
    //存入数据
    map.put("msg","<h1>Hello</h1>");
    map.put("users", Arrays.asList("林慕椿","林思湫"));
    //classpath:/templates/test.html
    return "test";
    }
  2. 测试页面取出数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="UTF-8">
    <title>Thymeleaf</title>
    </head>
    <body>
    <h1>测试页面</h1>

    <div th:text="${msg}"></div>
    <!--不转义-->
    <div th:utext="${msg}"></div>

    <!--遍历数据-->
    <!--th:each每次遍历都会生成当前这个标签:官网#9-->
    <h4 th:each="user :${users}" th:text="${user}"></h4>

    <!--遍历数据行内写法-->
    <h4>
    <span th:each="user:${users}">[[${user}]]</span>
    </h4>

    </body>
    </html>
  3. 启动项目测试

5. MVC自动配置原理

官网阅读

在进行项目编写前,需要知道SpringBoot对我们的SpringMVC还做了哪些配置,包括如何扩展如何定制。

方法1:分析源码

方法2:阅读官方文档

官方文档

ContentNegotiatingViewResolver内容协商视图解析器

ContentNegotiatingViewResolver 这个视图解析器就是用来组合所有的视图解析器的。

内容协商视图解析器自动配置了ViewResolver,就是SpringMVC的视图解析器,即根据方法的返回值取得视图对象(View),然后由视图对象决定如何渲染(转发,重定向)。

源码分析:

……

自定义视图解析器

可以自己给容器中去添加一个视图解析器,这个类就会帮我们自动的将它组合进来。

实现步骤:

  1. 在主程序中写一个视图解析器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package com.study.config;

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.View;
    import org.springframework.web.servlet.ViewResolver;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

    import java.util.Locale;

    //如果想自定义一些功能,只需要写这个组件,然后把它交给SpringBoot,SpringBoot会自动装配
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
    //ViewResolver实现类视图解析器接口的类,可以把它看做视图解析器
    @Bean //放到IOC容器中
    public ViewResolver myViewResolver(){
    return new MyViewResolver();
    }
    //自定义视图解析器MyViewResolver
    private static class MyViewResolver implements ViewResolver {
    @Override
    public View resolveViewName(String s, Locale locale) throws Exception {
    return null;
    }
    }
    }
  2. 测试

结论:在SpringBoot中想要使用自己定制化的东西,只需要给容器中添加这个组件就好了,剩下的事情 SpringBoot就会帮我们做了。

修改SpringBoot的默认配置

SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(如果用户自己配置 @bean),如果有就用用户配置的,如果没有就用自动配置的。

如果有些组件可以存在多个,比如视图解析器,就将用户配置的和自己默认的组合起来。

全面接管SpringMVC

全面接管:SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己去配置。只需在我们的配置类中要加一个@EnableWebMvc。

如果全面接管了SpringMVC,之前SpringBoot给我们配置的静态资源映射一定会无效。

6. Swagger

6.1 Swagger概述

在前后端分离开发的过程中,前端和后端需要进行api对接进行交互,就需要一个api规范文档,方便前后端的交互,但api文档不能根据代码的变化发生实时动态的改变,这样后端修改了接口,前端不能及时获取最新的接口,导致调用出错,需要手动维护api文档,加大了开发的工作量和困难,而swagger的出现就是为了解决这一系列的问题。

swagger是一套基于OpenAPI规范构建的开源工具,使用RestApi

  1. 代码变,文档变
  2. 跨语言,支持多种语言
  3. swagger-ui 呈现出来的是一份可交互式的API文档,可以直接在文档页面尝试API的调用
  4. 可以将文档规范导入相关工具(postman、soapui),这些工具将会为我们自动地创建自动化测试

swagger主要包含了以下三个部分:

  • swagger editor:基于浏览器的编辑器,我们可以使用它编写我们OpenApi规范(yaml或者json配置)
  • Swagger UI:他会将我们编写的OpenApi规范呈现为交互式的API文档,后文我将使用浏览器来查看并且操作我们的RestApi
  • Swagger Codegen:它可以通过OpenApi规范定义的任何API生成服务器存根和客户端SDK来简化构建过程

使用swagger就是把相关信息存储在它定义的描述文件里面(yml或json格式),再通过维护这个描述文件可以去更新接口文档,以及生成各端代码。

Springfox:

使用swagger时如果碰见版本更新迭代时,只需要更改swagger的描述文件即可,但是在频繁的更新项目版本时很多开发人员认为即使修改描述文件(yml或json文件)也是一定的工作负担,久而久之就直接修改代码,而不去修改描述文件了,这样基于描述文件生成接口文档也失去了意义。
Marty Pitt编写了一个基于spring的组件 swagger-springmvc ,Spring-fox就是根据这个组件发展而来的全新项目;
Spring-fox是根据代码生成接口文档,所以正常的进行更新项目版本,修改代码即可,而不需要跟随修改描述文件(yml或json文件);
spring-fox利用自身AOP特性,把swagger集成进来,底层还是Swagger,但是使用起来却方便很多,所以在实际开发中,都是直接使用spring-fox。

6.2 SpringBoot集成Swagger

SpringBoot整合Swagger,版本不兼容报错问题解决方法。

参考博客:https://blog.csdn.net/hadues/article/details/123753888

  1. 新建一个SpringBoot项目

  2. 导入Swagger相关依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <!--注意SpringBoot和Swagger版本的兼容性,降低SpringBoot版本-->
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.6</version>
    <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2-->
    <dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
    <dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
    </dependency>
  3. 配置Swagger

    1
    2
    3
    4
    5
    6
    7
    8
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;

    @Configuration //配置类
    @EnableSwagger2 //开启Swagger2的自动配置
    public class SwaggerConfig {

    }
  4. 测试运行:http://localhost:8080/swagger-ui.html

    可以看见Swagger页面

    QQ截图20221021102132

6.3 配置Swagger

6.3.1 配置Swagger信息

  1. Swagger实例Bean是Docket,所以通过配置Docket实例来配置Swagger。

    1
    2
    3
    4
    5
    //配置Swagger Docket的Bean实例
    @Bean
    public Docket docket(){
    return new Docket(DocumentationType.SWAGGER_2)
    }
  2. 可以通过apiInfo()属性配置文档信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //配置Swagger信息:apiInfo
    private ApiInfo apiInfo(){
    Contact contact = new Contact("林慕椿", "https://yiqiangshiyia.cn", "2746200911@qq.com");
    return new ApiInfo(
    "林慕椿的Swagger API文档", //标题
    "为霞尚满天", //描述
    "v1.0", //版本
    "urn:tos",
    contact, //联系人信息
    "Apache 2.0", //许可
    "http://www.apache.org/licenses/LICENSE-2.0", //许可链接
    new ArrayList() //扩展
    );
    }
  3. Docket 实例关联上 apiInfo()

    1
    2
    3
    4
    5
    6
    //配置Swagger Docket的Bean实例
    @Bean
    public Docket docket(){
    return new Docket(DocumentationType.SWAGGER_2)
    .apiInfo(apiInfo());
    }
  4. 完整Swagger配置

    SwaggerConfig.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    @Configuration //配置类
    @EnableSwagger2 //开启Swagger2的自动配置
    public class SwaggerConfig {
    //配置Swagger Docket的Bean实例
    @Bean
    public Docket docket(){
    return new Docket(DocumentationType.SWAGGER_2)
    .apiInfo(apiInfo());
    }

    //配置Swagger信息:apiInfo
    private ApiInfo apiInfo(){
    Contact contact = new Contact("林慕椿", "https://yiqiangshiyia.cn", "2746200911@qq.com");
    return new ApiInfo(
    "林慕椿的Swagger API文档", //标题
    "为霞尚满天", //描述
    "v1.0", //版本
    "urn:tos",
    contact, //联系人信息
    "Apache 2.0", //许可
    "http://www.apache.org/licenses/LICENSE-2.0", //许可链接
    new ArrayList() //扩展
    );
    }
    }
  5. 测试

    访问:http://localhost:8080/swagger-ui.html

    QQ截图20221021105332

6.3.2 配置扫描接口

  1. 构建Docket时通过 select() 方法配置怎么扫描接口。

    1
    2
    3
    4
    5
    6
    7
    8
    @Bean
    public Docket docket() {
    return new Docket(DocumentationType.SWAGGER_2)
    .apiInfo(apiInfo())
    .select() //通过.select()方法去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
    .apis(RequestHandlerSelectors.basePackage("com.study.controller"))
    .build();
    }
  2. 重启项目测试,由于配置根据包的路径扫描接口,所以只能看到一个类。

  3. 除了通过包路径配置扫描接口外,还可以通过配置其他方式扫描接口

    1
    2
    3
    4
    5
    6
    7
    8
    /*
    RequestHandlerSelectors配置扫描接口方式:
    basePackage:扫描指定的包
    ang():扫描全部
    none():不扫描
    withClassAnnotation:扫描类上的注解,参数是注解的一个反射对象
    withAnnotation:扫描方法上的注解
    */
  4. 通过 paths() 方法配置接口扫描过滤

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //配置Swagger Docket的Bean实例
    @Bean
    public Docket docket(){
    return new Docket(DocumentationType.SWAGGER_2)
    .apiInfo(apiInfo())
    .select() //通过.select()方法去配置扫描接口
    /*
    RequestHandlerSelectors配置扫描接口方式:
    basePackage:扫描指定的包
    ang():扫描全部
    none():不扫描
    withClassAnnotation:扫描类上的注解,参数是注解的一个反射对象
    withAnnotation:扫描方法上的注解
    */
    .apis(RequestHandlerSelectors.basePackage("com.study.controller"))
    //通过.paths()方法去过滤路径
    .paths(PathSelectors.ant("/hello/**"))
    .build();
    }

    PathSelectors 可选方法:

    1
    2
    3
    4
    any() // 任何请求都扫描
    none() // 任何请求都不扫描
    regex(final String pathRegex) // 通过正则表达式控制
    ant(final String antPattern) // 通过ant()控制

6.3.3 配置Swagger开关

  1. 通过enable()方法配置是否启用swagger

    enable是否启动Swagger,如果为false,则swagger不能在浏览器中访问。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //配置Swagger Docket的Bean实例
    @Bean
    public Docket docket(){
    return new Docket(DocumentationType.SWAGGER_2)
    .apiInfo(apiInfo())
    //enable是否启动Swagger,如果为false,则swagger不能在浏览器中访问
    .enable(false)
    .select() //通过.select()方法去配置扫描接口
    .apis(RequestHandlerSelectors.basePackage("com.study.controller"))
    .build();
    }

    测试:

    QQ截图20221021191620

  2. 动态配置当项目处于test、dev环境时显示swagger,处于pro时不显示。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //配置Swagger Docket的Bean实例
    @Bean
    public Docket docket(Environment environment){

    //设置要显示的Swagger环境
    Profiles profiles = Profiles.of("dev","test");
    //通过environment.acceptsProfiles判断是否处在自己设定的环境中
    boolean flag = environment.acceptsProfiles(profiles);

    return new Docket(DocumentationType.SWAGGER_2)
    .apiInfo(apiInfo())
    //enable是否启动Swagger,如果为false,则swagger不能在浏览器中访问
    .enable(flag)
    .select() //通过.select()方法去配置扫描接口
    .apis(RequestHandlerSelectors.basePackage("com.study.controller"))
    .build();
    }
  3. 可以在项目中增加一个 application-dev.properties 的配置文件查看效果

6.3.4 配置API文档分组

  1. 如果API文档没有配置分组,默认是default。

    通过groupName()方法即可配置分组:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Bean
    public Docket docket(Environment environment){
    return new Docket(DocumentationType.SWAGGER_2)
    .apiInfo(apiInfo())
    .groupName("分组-01") //配置分组
    //enable是否启动Swagger,如果为false,则swagger不能在浏览器中访问
    .enable(true)
    .select() //通过.select()方法去配置扫描接口
    .apis(RequestHandlerSelectors.basePackage("com.study.controller"))
    .build();
    }
  2. 重启项目查看分组

    QQ截图20221021191732

  3. 配置多个分组

    配置多个分组只需要配置多个docket即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Bean
    public Docket docket1() {
    return new Docket(DocumentationType.SWAGGER_2).groupName("group1");
    }
    @Bean
    public Docket docket2() {
    return new Docket(DocumentationType.SWAGGER_2).groupName("group2");
    }
    @Bean
    public Docket docket3() {
    return new Docket(DocumentationType.SWAGGER_2).groupName("group3");
    }
  4. 重启项目查看

    QQ截图20221021192158

6.4 接口注释

  1. 新建一个实体类

    @ApiModel为类添加注释

    @ApiModelProperty为类属性添加注释

    1
    2
    3
    4
    5
    6
    7
    @ApiModel("用户实体")  //给实体类加中文注释
    public class User {
    @ApiModelProperty("用户名") //给属性加中文注释
    public String username;
    @ApiModelProperty("密码")
    public String password;
    }
  2. 只要这个实体在请求接口的返回值上(即使是泛型),都能映射到实体项中:

    1
    2
    3
    4
    5
    //只要接口中返回值中存在实体类,就会被扫描到Swagger中
    @PostMapping(value = "/user")
    public User user(){
    return new User();
    }
  3. 重启查看测试

    QQ截图20221021193033

Swagger常用注解:

Swagger的所有注解定义在io.swagger.annotations包下

Swagger注解 简单说明
@Api(tags = “xxx模块说明”) 作用在模块类上
@ApiOperation(“xxx接口说明”) 作用在接口方法上
@ApiModel(“xxxPOJO说明”) 作用在模型类上:如VO、BO
@ApiModelProperty(value = “xxx属性说 明”,hidden = true) 作用在类方法和属性上,hidden设置为true可 以隐藏该属性
@ApiParam(“xxx参数说明”) 作用在参数、方法和字段上,类似 @ApiModelProperty

6.5 Swagger总结

  1. 可以通过Swagger给一些比较难理解的属性或者接口增加注释信息
  2. 接口文档实时更新
  3. 可以在线测试

7. 异步任务

异步处理是非常常用的,比如在网站上发送邮件,后台会去发送邮件,此时前台会造成响应不动,直到邮件发送完毕,响应才会成功。一般会采用多线程的方式去处理这些任务,编写方法,假装正在处理数据,使用线程设置一些延时,模拟同步等待的情况。

实现步骤:

  1. 在 service 包下创建一个类AsyncService

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.study.service;

    import org.springframework.stereotype.Service;

    @Service
    public class AsyncService {
    public void hello(){
    try {
    Thread.sleep(3000); //停止3s
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("数据处理中....");
    }
    }
  2. 在 controller 包下编写AsyncController类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package com.study.controller;

    import com.study.service.AsyncService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    public class AsyncController {
    @Autowired
    AsyncService asyncService;

    @GetMapping("/hello")
    public String hello(){
    asyncService.hello();
    return "success";
    }
    }
  3. 测试

    访问http://localhost:8080/hello,3秒后出现success,这是同步等待的情况。

遇到问题:

如果想让用户直接得到消息,就在后台使用多线程的方式进行处理即可,但是每次都需要手动去编写多线程的实现太麻烦了,只需要用一个简单的办法,在方法上加一个简单的注解即可。

解决方法:

  1. 给hello方法添加@Async注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Async  //告诉Spring这是一个异步方法
    public void hello(){
    try {
    Thread.sleep(3000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("数据处理中....");
    }

    SpringBoot就会自己开一个线程池进行调用。但是要让这个注解生效,还需要在主程序上添加一个@EnableAsync注解,开启异步注解功能。

    1
    2
    3
    4
    5
    6
    7
    @EnableAsync //开启异步注解功能
    @SpringBootApplication
    public class SpringbootTaskApplication {
    public static void main(String[] args) {
    SpringApplication.run(SpringbootTaskApplication.class, args);
    }
    }
  2. 重启测试

    网页瞬间响应,后台代码依旧执行。

8. 邮件任务

Springboot支持邮件发送:

  • 邮件发送需要引入spring-boot-start-mail
  • SpringBoot 自动配置MailSenderAutoConfiguration
  • 定义MailProperties内容,配置在application.yml中
  • 自动装配JavaMailSender
  • 测试邮件发送

实现步骤:

  1. 引入pom依赖

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
  2. 分析自动配置类源码:MailSenderAutoConfiguration

    源码配置文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @ConfigurationProperties(
    prefix = "spring.mail"
    )
    public class MailProperties {
    private static final Charset DEFAULT_CHARSET;
    private String host;
    private Integer port;
    private String username;
    private String password;
    private String protocol = "smtp";
    private Charset defaultEncoding;
    private Map<String, String> properties;
    private String jndiName;
    }
  3. 配置文件

    1
    2
    3
    4
    5
    6
    spring.mail.username=27462009111@qq.com
    # 授权码
    spring.mail.password=yhkrgtqwbnrcbhcj
    spring.mail.host=smtp.qq.com
    # 开启加密验证,qq需要配置ssl
    spring.mail.properties.mail.smtp.ssl.enable=true
  4. 测试类中测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    @Autowired
    JavaMailSenderImpl mailSender;

    //邮件设置1:一个简单的邮件
    @Test
    public void contextLoads() {
    SimpleMailMessage message = new SimpleMailMessage();
    message.setSubject("UPC-放寒假通知");
    message.setText("UPC于2022.01.18开始放寒假!");

    message.setTo("321962327@qq.com");
    message.setFrom("2746200911@qq.com");
    mailSender.send(message);
    }

    //邮件设置2:一个复杂的邮件
    @Test
    public void contextLoads2() throws MessagingException {
    MimeMessage mimeMessage = mailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);

    helper.setSubject("UPC-放寒假通知");
    helper.setText("<b style='color:red'>UPC于2022.01.18开始放寒假!</b>",true);

    //发送附件
    helper.addAttachment("https://img.yiqiangshiyia.cn/blog/yiqiangshiyia.jpg",new File(""));
    helper.addAttachment("https://img.yiqiangshiyia.cn/blog/yiqiangshiyia.jpg",new File(""));

    helper.setTo("321962327@qq.com");
    helper.setFrom("2746200911@qq.com");

    mailSender.send(mimeMessage);
    }

9. 定时任务

9.1 cron表达式

Cron表达式生成器:https://www.bejson.com/othertools/cron/

cron表达式:

计划任务,是任务在约定的时间执行已经计划好的工作,这是表面的意思。在Linux中,我们经常用到 cron 服务器来完成这项工作。cron服务器可以根据配置文件约定的时间来执行特定的任务。

crontab文件的格式:M H D m d cmd.(倒着看便于理解)

M: 分钟(0-59)。

H:小时(0-23)。

D:天(1-31)。

m: 月(1-12)。

d: 一星期内的天(0~7,0,7为星期天,6为星期六)。

cmd: 要执行的命令。

这个格式的前一部分是对时间的设定,后面一部分是要执行的命令,如果要执行的命令太多,可以把这些命令写到一个脚本里面,然后在这里直接调用这个脚本就可以了,调用的时候记得写出命令的完整路径。时间的设定我们有一定的约定,前面五个*号代表五个数字,数字的取值范围和含义如下:

分钟 (0-59)

小时 (0-23)

日期 (1-31)

月份 (1-12)

星期 (0-7)// 0和7均代表星期天

除了数字还有几个个特殊的符号就是”“、”/“和”-“、”,”,代表所有的取值范围内的数字,”/“代表每的意思,”*/5”表示每5个单位,”-“代表从某个数字到某个数字,”,”分开几个离散的数字。

9.2 定时任务

项目开发中经常需要执行一些定时任务,Spring提供了异步执行任务调度的方式。

Spring提供了两个接口:

  • TaskExecutor接口 —- 任务执行
  • TaskScheduler接口 —- 任务调度

两个注解:

  • @EnableScheduling —- 开启定时功能的注解
  • @Scheduled —- 表示什么时候执行

实现定时任务:

  1. 创建一个ScheduledService

    定时执行一个方法

    1
    2
    3
    4
    5
    6
    7
    8
    @Service
    public class ScheduledService {
    //在一个特定的时间执行这个方法
    @Scheduled(cron = "0 * * * * 0-7")
    public void hello(){
    System.out.println("hello被执行了");
    }
    }
  2. 在主程序上增加@EnableScheduling 开启定时任务功能

    1
    2
    3
    4
    5
    6
    7
    8
    @EnableAsync //开启异步注解功能
    @EnableScheduling //开启基于注解的定时任务
    @SpringBootApplication
    public class SpringbootTaskApplication {
    public static void main(String[] args) {
    SpringApplication.run(SpringbootTaskApplication.class, args);
    }
    }

SpringBoot Web开发
https://yiqiangshiyia.cn/2022/10/07/SpringBoot Web开发/
作者
一腔诗意啊
发布于
2022年10月7日
许可协议