SpringBoot

1、简介

  • Spring Boot诞生于大数据微服务时代
  • 设计理念:用来简化新Spring应用的初始搭建以及开发过程,约定大于配置,去繁从简
  • 官网:https://spring.io/projects/spring-boot

2、特征

  • 可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的JARs和WARs;
  • 内嵌Tomcat或Jetty等Servlet容器;
  • 提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置;
  • 尽可能自动配置Spring容器;
  • 提供准备好的特性,如指标、健康检查和外部化配置;
  • 绝对没有代码生成,不需要XML配置。

3、微服务

1、什么是微服务

  • 微服务是一种架构风格,一个应用拆分
  • 服务之间使用轻量级HTTP交互
  • 服务围绕业务功能拆分
  • 由全自动部署机制独立部署

2、微服务的缺点

  • 运维开销及成本增加
  • 必须有坚实的DevOps开发运维一体化技能
  • 隐式接口及接口匹配问题
  • 代码重复
  • 分布式系统的复杂性
  • 异步机制
  • 可测性的挑战

4、第一个springboot

1、环境需要

  • jdk1.8
  • maven 3.x :maven3.3以上
  • IDEA2020
  • SpringBoot 2.3.12.RELEASE

2、创建一个maven文件

3、需要添加的maven依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!--指定一个父项目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent>

<dependencies>
<!--配置web场景的启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

<!--插入插件,没有这个插件,打包后运行不了-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

4、创建一个main方法

1
2
3
4
5
6
@SpringBootApplication  //告诉springboot这是一个应用程序
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

5、配置一个Controller

1
2
3
4
5
6
7
8
@RestController
public class Controller {

@GetMapping("/text")
public String text(){
return "text "+new Date();
}
}

6、启动main方法后,可以直接进入浏览器访问127:0:0:1:8080/text(springboot的默认端口为8080)

5、springboot自动配置

1、springboot自动配置原理

1、进入注解@SpringBootApplication

里面有这些注解

1
2
3
4
5
6
@Target({ElementType.TYPE})//标识为放于类上
@Retention(RetentionPolicy.RUNTIME)//注解运行时,可以获取注解的一些元数据
@Documented
@Inherited
@SpringBootConfiguration//标识这是springboot的一个配置类
@EnableAutoConfiguration//启用自动配置

2、进入注解@EnableAutoConfiguration

里面有又俩个核心注解

1
2
@AutoConfigurationPackage//自动装配应用程序下面的所有子包
@Import({AutoConfigurationImportSelector.class})//导入自动配置选择器

3、进入@AutoConfigurationPackage注解

1
@Import({Registrar.class})//将Registrar导入spring ioc容器
  • Registrar.java中有个registerBeanDefinitions方法,通过内部类构造方法调用了PackageImports方法

    1
    2
    3
    4
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }
  • PackageImports方法中有个

    1
    2
    3
    if (packageNames.isEmpty()) {
    packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
    }

    将应用程序的包名获取中,并且通过反射获取包名下的所有子包,扫描进spring ioc容器中

4、@Import({AutoConfigurationImportSelector.class})

这个文件内包含很多配置类,导入这个场景需要的组件

进入AutoConfigurationImportSelector.java方法找到

进入找到

这个常量的值为

Spring Boot在启动的时候从类路径下的META-INF/spring.factorys中获取的EnableAutoConfiguration指定的值;

将这些值作为自动配置类导入到容器中,自动配置就生效了。

2、springboot自动配置原理总结

1、sping boot在启动的时候加载主配置类开启了自动配置,自动配置里面有俩个核心注解

2、一个@AutoConfigurationPackage

3、一个@Import(AutoConfigurationImportSelector.class)

4、@AutoConfigurationPackage内部还有个@Import(AutoConfigurationPackages.Registrar.class)

5、Registrar方法调用了registerBeanDefinitions通过内部内调用了PackageImports,这个类通过反射获取应用程序的包以及下面的子包扫描到spirng ioc容器中

6、@Import(AutoConfigurationImportSelector.class)

7、AutoConfigurationImportSelector这个类通过调用loadFactoryNames中的方法获取到meta-info下面的spring.factorys中自动配置类获取EnableAutoConfiguration键对应的值,并且将他装配到spring ioc容器中

6、快速创建springboot项目

1、创建一个新到的项目选择

2、下一步后输入包名,项目名,选择jdk版本

3、下一步,选择项目,根据springboot版本

4、下一步则创建结束

5、有可能不是maven项目,你需要选择这个转换为maven项目

7、Yaml

1、基本语法

k:(空格)v:表示一堆键值对(空格必须有);

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • #表示注释

1
2
server:
port: 9000

2、数据类型

YAML 支持以下几种数据类型:

  • 对象:键值对的集合,又称为字典(dictionary)
  • 数组:一组按次序排列的值,又称为列表(list)
  • 纯量(scalars):字符串,布尔值,整数,小数,null,时间,日期等类型

3、数组

数组以-开头区分,

例:

1
2
3
- A
- B
- C

也可以一行显示

例:

1
key: [value1, value2]

8、配置文件(基本完成)

创建springboot项目后,在resources下面会生成一个application.properties

推荐将properties改为yml配置文件,因为对比properties没有重复代码

具体感觉你对比一下则出来了

1
2
server:
port: 8088
1
server.port=8088

端口号则变成了8088

更多对的配置文件请查看Common Application properties (spring.io)

1、配置文件值注入

yml也支持属性类与配置文件对应,使用方便,并且支持校验 ,

属性之间可以通过${}里面存放完整路径可以调用

通过ConfigurationProperties注解的prefix的与yml的头对应,则可以自动配置

例:

1
user:  id: 100  last-name: "张三"  age: 14  email: 111@@qq.com  map:    k1: 李四    k2: '王五'    k3: v3  list:    - 10    - 20    - 30
1
@Configuration@ConfigurationProperties(prefix="user") //prefix跟yml的头对应@Validated //校验注解public class User {    private int id;    private String lastName;    private int age;    private Map<String,Object> map;    private List list;    @Email    private String email;    }

在加入一个配置依赖,则还会提示

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐configuration‐processor</artifactId>
<optional>true</optional>
</dependency>

校验依赖,使用校验依赖时候,在属性上还需要添加@Validated否则不起效果

1
2
3
4
5
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.14.Final</version>
</dependency>

yml支持松散绑定(语法),例:yml中你为last-name,则你的配置文件可以lastName获取到值

2、配置文件占位符

yml为我们提供了很方便的占位符,让我们使用

1、随机数

1
${random.value} 、${random.int}、${random.long}${random.int(10)}、${random.int[100,200]}

2、多种环境配置

1、不同的yml文件(推荐使用)

你可以创建多个yml文件,但是必须以application-开头

application.yml要存在

例:

  • application-dev.yml
  • application-prod.yml

当你在application.yml中可以输入指令选择其中一个

1
2
3
spring:
profiles:
active: dev #active的值为application-的名字

2、同一个yml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server:
port: 8081
spring:
profiles:
active: dev

---

server:
port: 9000
spring:
profiles: dev

---
server:
port: 80
spring:
profiles: prod

3、访问

打成jar包后,可以通过下面格式访问

1
java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev

9、日志(未完成全面)

​ 有时我们用System.out.println(“”)输入主要数据来查看,但是项目一旦太大,我们则查找删除不方便,springboot日志正好的帮我们解决了这个问题

​ springboot会根据不一样的开发输入不一样的日志

​ 日志的排行级别:trace-debug-info-warn-error

​ 上一级别的会显示下面级别的所有日志

1、使用

记录器

1
Logger logger= LoggerFactory.getLogger(getClass());

使用logger点不同的日志级别,则会输出

1
2
3
4
5
logger.trace("这是trace的日志信息");
logger.debug("这是debug的日志信息");
logger.info("这是info的日志信息");
logger.warn("这是warn的日志信息");
logger.error("这是error的日志信息");

2、配置文件

1、设置指定包名访问的权限

1
2
3
logging:
level:
com.znsd: debug

则com.znsd这个包,只能范围debug跟debug以下的日志信息

2、配置日志输出格式

1
logging:  pattern:    console: "%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n"

那么输入的格式则会按console这个格式来

例:

1
2021-06-25 [main] ERROR c.z.s.s.SpringbootLoggerApplicationTests - 这是error的日志信息

3、将日志输入到指定文件

1、默认存储路径(当前项目根目录)

1
2
3
logging:
file:
name: springboot-log

将输入的日志,保存达到springBoot-log文件里面

2、指定存储到文件内的日志格式

1
2
3
pattern:
console: "%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n"
file: "%d{yyyy-MM-dd } file----[%thread] %-5level %logger{50} - %msg%n"

在控制台辉输入console格式的日志文件,存入文件则存入file格式

3、指定存储路径

1
2
3
4
5
logging:
level:
com.znsd: debug
file:
path: D:/spring-boot/log

如果file内的name跟path同时存在,则会执行name,不会执行path

10、简化属性类

导入依赖

1
2
3
4
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

在属性类上添加注解

1
2
3
4
5
6
7
8
9
@Data //自动生成setget方法
@ToString //自动生成toString方法
@AllArgsConstructor//自动生成全参构造方法
@NoArgsConstructor//自动生成无参构造方法
@Slf4j //生成日志
public class User {
private Integer id;
private String name;
}

这样能减少我们大量的属性类代码

11、web开发

1、静态文件

springboot帮我们自动配置了4个文件夹,用于存放静态文件,所有的资源都放于resources文件夹下

  • META-INF/resources/ 这个文件夹创建后有可能是META-INF.resources这是正常的
  • resources
  • static
  • public

1、配置欢迎页面

在任意静态文件夹内添加index.html,那么当我们进入后,默认跳转到index.html

优先访问顺序:META-INF/resources/ —> resources —> static —-> public

2、配置ico图标

将名为favicon.ico的ico文件导入在静态文件即可使用

3、配置静态资源前缀

有时我们使用拦截器会把css 、js等静态文件拦截,将静态资源加个前缀,则可以直接不拦截以这个前缀开头即可

1
2
3
spring:
mvc:
static-path-pattern: /static/**

这样子我们的所有静态资源都得以static开头

4、配置自定义静态文件

1
2
3
spring:
resources:
static-locations: [classpath:/aaa/]

配置以后,springboot帮我们创建的4个静态文件则失效

5、注意

springboot会优先进入controller控制器,然后才会进入静态文件

2、模板引擎

SpringBoot推荐Thymeleaf;语法简单,功能强大

引入模板引擎依赖

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

1、使用

首先在html上导入xmlns:th=”http://www.thymeleaf.org"

1
<html xmlns:th="http://www.thymeleaf.org">

2、语法

3、错误页面

springboot有个默认的错误页面

1、配置错误页面

springboot也可以自定义配置错误页面

1、在静态文件(static、public…)内创建error文件夹里面存放错误页面

2、在模板文件夹(templates)内创建error文件夹里面存放错误页面(优先进入模板文件夹)

错误页面名

1、spirngboot会根据错误状态码,自动寻找错误页面,例:404.html

2、找不到指定的错误页面,则会去找4xx.html或5xx.html,根据错误状态码开头进入

3、若还没有找到则会自主生成一个错误页面

2、ErrorMvcAutoConfiguration解析

  • 找到架包

  • 找到web –> servlet —> error —>ErrorMvcAutoConfiguration(错误自动配置类)

springbooterror源码分析2

  • 进入ErrorMvcAutoConfiguration

    • ErrorMvcAutoConfiguration类上的注解

      1
      2
      3
      4
      5
      6
      7
      8
      @Configuration(proxyBeanMethods = false)  //标识是配置类
      @ConditionalOnWebApplication(type = Type.SERVLET) //web程序才会生效
      @ConditionalOnClass({Servlet.class, DispatcherServlet.class})
      //当项目有Servlet 并且注入DispatcherServlet 的时候生效
      @AutoConfigureBefore({WebMvcAutoConfiguration.class})
      //在运行完WebMvcAutoConfiguration之前生效
      @EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class, WebMvcProperties.class})
      //这个配置类需要依赖ServerProperties(服务的配置属性)、ResourcePropertie(资源的配置属性)、WebMvcProperties(webmvc的配置属性)
    • errorAttributes方法 获取默认异常信息

      1
      2
      3
      4
      5
      6
      7
       @Bean
      @ConditionalOnMissingBean(value = {ErrorAttributes.class},search = SearchStrategy.CURRENT)
      //在你项目中没找到自定义异常信息则生效,找到了则用自己的
      //自己定义的类必须要实现ErrorAttributes
      public DefaultErrorAttributes errorAttributes() {
      return new DefaultErrorAttributes();
      }

      进入返回的对象通过debug在getErrorAttributes方法中断点即可查看到sprintboot返回的默认错误信息

      在html页面可以直接使用查看错误信息

      例:

      1
      2
      3
      4
      <h1 th:text="状态码:+${status}">500</h1>
      <h1 th:text="错误信息:+${message}">500</h1>
      <h1 th:text="错误发生时间:+${timestamp}">500</h1>
      <span th:text="错误所有信息:+${trace}">500</span>
    • basicErrorController方法 基本的错误控制器

      1
      2
      3
      4
      5
      @Bean
      @ConditionalOnMissingBean(value = {ErrorController.class},search = SearchStrategy.CURRENT)
      public BasicErrorController basicErrorController(ErrorAttributes errorAttributes, ObjectProvider<ErrorViewResolver> errorViewResolvers) {
      return new BasicErrorController(errorAttributes, this.serverProperties.getError(), (List)errorViewResolvers.orderedStream().collect(Collectors.toList()));
      }

      进入BasicErrorController方法

      BasicErrorController类上注解

      1
      2
      3
      @Controller//标识这是个配置类
      @RequestMapping("${server.error.path:${error.path:/error}}")
      //遇到错误辉访问server.error.path这个路径默认为/error,可以自定义

      在BasicErrorController类中有又俩个主要的方法,一个errorHtml方法(响应错误页面) 一个error(响应错误json数据)

    • errorPageCustomizer 错误页面定制器

      1
      2
      3
      4
      @Bean
      public ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {
      return new ErrorPageCustomizer(this.serverProperties, dispatcherServletPath);
      }

      进入ErrorPageCustomizer方法

      1
      2
      3
      4
      5
      6
      @Override
      public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
      ErrorPage errorPage = new ErrorPage(
      this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
      errorPageRegistry.addErrorPages(errorPage);
      }

      进入getPath()方法即可看到path的值为/error,所以默认查询地址为/error

    • conventionErrorViewResolver 默认错误视图解析器 根据错误状态码完成页面转发

      1
      2
      3
      4
      5
      6
      @Bean
      @ConditionalOnBean(DispatcherServlet.class)
      @ConditionalOnMissingBean(ErrorViewResolver.class)
      DefaultErrorViewResolver conventionErrorViewResolver() {
      return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
      }

      进入DefaultErrorViewResolver类,即可看到为什么只有4xx.html跟5xx.html生效,因为springboot只定义了这俩个

      1
      2
      3
      4
      5
      6
      static {
      Map<Series, String> views = new EnumMap<>(Series.class);
      views.put(Series.CLIENT_ERROR, "4xx");
      views.put(Series.SERVER_ERROR, "5xx");
      SERIES_VIEWS = Collections.unmodifiableMap(views);
      }

      DefaultErrorViewResolver还有三个核心方法,若最后返回null,springboot则会自动渲染一个错误页面

      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
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      @Override
      public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
      //调用resolve方法,将状态码跟状态信息传过去,寻找状态码页面,找不到则返回null
      ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
      //判断是否找到状态码页面,没找到则找4xx,5xx页面
      //status.series()我理解为寻找状态码开头数字
      if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
      //寻找4xx,5xx页面,找不到则返回null
      modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
      }
      //找到则返回找到的html文件,没找到则返回null,返回无后会有一个方法自动生成一个html页面
      return modelAndView;
      }
      //在模板寻找
      private ModelAndView resolve(String viewName, Map<String, Object> model) {
      //状态码跟error/拼接
      String errorViewName = "error/" + viewName;
      //在模板(templates)寻找 状态码.xml或4xx.xml、5xx.html是否存在,不存在则返回无
      TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,this.applicationContext);
      if (provider != null) {
      //页面找到则返回页面值,跟错误状态码
      return new ModelAndView(errorViewName, model);
      }
      //没找到页面则去静态文件中查找
      return resolveResource(errorViewName, model);
      }
      //在静态文件寻找
      private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
      //循环4个静态文件
      for (String location : this.resourceProperties.getStaticLocations()) {
      try {
      Resource resource = this.applicationContext.getResource(location);
      resource = resource.createRelative(viewName + ".html");
      if (resource.exists()) {
      //找到则返回找到的html文件
      return new ModelAndView(new HtmlResourceView(resource), model);
      }
      }
      catch (Exception ex) {
      }
      }
      //没找到则返回无
      return null;
      }

3、错误页面运行原理

1、浏览器或客户端发送请求,都会去DispatcherServlet类来进行处理,

2、如果报错了则会寻找错误解析器处理,如果没有错误解析器可以处理,则返回null,然后发送/error请求

3、由BasicErrorController接管,根据请求头判断是浏览器发送请求还是服务器发送请求,如果是服务器发送请求则会返回json数据,如果是浏览器发送请求则会

4、调用DefaultErrorViewResolver方法,这个方法首先会去根据错误状态码,

5、首先会根据对应的状态码去寻找页面,如果没找到则会找对应的4xx、5xx.html文件,如果还没有找到,则会调用render方法,进行页面拼接返回

4、嵌入式servlet容器

1、tomcat配置属性

找到ServerProperties.java,找到tomcat静态类就可以查看tomcat的一些配置属性以及默认值

2、通过java代码配置sevlet容器

1
2
3
4
5
6
7
8
9
@Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> webServerFactoryCustomizer() {
return new WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>() {
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
factory.setPort(8082);
}
};
}

3、spring boot servlet容器启动原理

1、ServletWebServerApplicationContext通过tomcat启动加载sping ioc容器

2、ServletWebServerApplicationContext中有个核心方法refresh,

1
2
3
4
5
6
7
8
9
10
11
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
} catch (RuntimeException var3) {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.stop();
}
throw var3;
}
}

3、他又调用了父类的refresh方法

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备刷新
prepareRefresh();

// 告诉子类刷新bean工厂.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// 准备使用bean工厂
prepareBeanFactory(beanFactory);

try {
//允许在上下文子类中处理bean工厂。
postProcessBeanFactory(beanFactory);

// 调用在上下文中注册为bean的工厂处理器。
invokeBeanFactoryPostProcessors(beanFactory);

// 注册bean处理器,可以拦截bean的创建。
registerBeanPostProcessors(beanFactory);

// 为此上下文初始化消息源。
initMessageSource();

// 在此背景下初始化事件多播器。
initApplicationEventMulticaster();

// 在特定的上下文子类中初始化其他特殊bean。
onRefresh();

// 检查监听器bean并注册它们。
registerListeners();

// 实例化所有剩余(非懒加载)单例。
finishBeanFactoryInitialization(beanFactory);

// 最后一步:发布相应的事件
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// Destroy already created singletons to avoid dangling resources.
destroyBeans();

// Reset 'active' flag.
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}

finally {
//完成初始化,tomcat启动成功
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

4、父类的refresh方法又调用了子类的onRefresh方法(也就是ServletWebServerApplicationContext中的onRefresh方法)

1
2
3
4
5
6
7
8
9
protected void onRefresh() {
super.onRefresh();
try {
//创建web服务器
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}

5、进入createWebServer方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void createWebServer() {
//赋值
WebServer webServer = this.webServer;
//获取servlet上下文
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
//获取sping bean工厂
ServletWebServerFactory factory = this.getWebServerFactory();
//通过工厂来获取web服务器
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var4) {
throw new ApplicationContextException("Cannot initialize servlet context", var4);
}
}

this.initPropertySources();
}

这里有三个spring bean 工厂

  • TomcatServletWebServerFactory Tomcat服务工厂(默认进)
  • JettyServletWebServerFactory Jetty服务工厂
  • UndertowServletWebServerFactory Unertow服务工厂

6、默认调用Tomcat,进入TomcatServletWebServerFactory里面的getWebServer方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
//new创建了一个tomcat对象
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}

7、结束之后又调用了getTomcatWebServer(tomcat)方法

1
2
3
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}

8、getTomcatWebServer有new了一个TomcatWebServer对象的构造方法

1
2
3
4
5
6
7
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
initialize();
}

9、又调用了initialize()初始化方法

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
34
35
36
37
38
39
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
addInstanceIdToEngineName();

Context context = findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
// Remove service connectors so that protocol binding doesn't
// happen when the service is started.
removeServiceConnectors();
}
});
//启动tomcat服务器
// Start the server to trigger initialization listeners
this.tomcat.start();

// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();

try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
}

// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
}
catch (Exception ex) {
stopSilently();
destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}

4、sevlet web服务工厂的自动配置

去架包找到ServletWebServerFactoryAutoConfiguration(servlet web 服务工厂自动配置)

  • ServletWebServerFactoryAutoConfiguration类上的注解
1
2
3
4
5
6
7
8
9
10
11
12
@Configuration(proxyBeanMethods = false)//标识这是个配置类,关闭单例模式
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)//配置类的顺序,优先级最高
@ConditionalOnClass(ServletRequest.class)//想要这个配置类生效必须要依赖ServletRequest
@ConditionalOnWebApplication(type = Type.SERVLET)//当前环境必须是服务器环境
@EnableConfigurationProperties(ServerProperties.class)//必须要激活ServerProperties环境
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
//导入三个内置容器的配置
public class ServletWebServerFactoryAutoConfiguration {
}
  • 默认的WebServerFactory配置类

    • ServletWebServerFactoryConfiguration.EmbeddedTomcat 想要这个配置类生效想要满足以下条件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      @Configuration(proxyBeanMethods = false) //标识是个配置类,并且关闭单例模式
      @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
      //必须要有Servlet、Tomcat、UpgradeProtocol 这些包bean才会生效
      @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
      //只能注册一个ServletWebServerFactory类的注解,配置多个,则只有第一次注册的生效
      static class EmbeddedTomcat {

      @Bean
      TomcatServletWebServerFactory tomcatServletWebServerFactory(
      ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
      ObjectProvider<TomcatContextCustomizer> contextCustomizers,
      ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
      TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
      factory.getTomcatConnectorCustomizers()
      .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
      factory.getTomcatContextCustomizers()
      .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
      factory.getTomcatProtocolHandlerCustomizers()
      .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
      return factory;
      }

      }
    • ServletWebServerFactoryConfiguration.EmbeddedJetty

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      @Configuration(proxyBeanMethods = false)//标识是个配置类,并且关闭单例模式
      @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
      //必须要有Servlet、Server、Loader、WebAppContext 这些包bean才会生效
      @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
      //只能注册一个ServletWebServerFactory类的注解,配置多个,则只有第一次注册的生效
      static class EmbeddedJetty {
      @Bean
      JettyServletWebServerFactory JettyServletWebServerFactory(
      ObjectProvider<JettyServerCustomizer> serverCustomizers) {
      JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
      factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
      return factory;
      }
      }
    • ServletWebServerFactoryConfiguration.EmbeddedUndertow

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      @Configuration(proxyBeanMethods = false)//标识是个配置类,并且关闭单例模式
      @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
      //必须要有Servlet、Undertow、SslClientAuthMode 这些包bean才会生效
      @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
      //只能注册一个ServletWebServerFactory类的注解,配置多个,则只有第一次注册的生效
      static class EmbeddedUndertow {

      @Bean
      UndertowServletWebServerFactory undertowServletWebServerFactory(
      ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
      ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
      UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
      factory.getDeploymentInfoCustomizers()
      .addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
      factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
      return factory;
      }

      @Bean
      UndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(
      ServerProperties serverProperties) {
      return new UndertowServletWebServerFactoryCustomizer(serverProperties);
      }
      }

      spirngboot默认导入了tomcat的包,Jetty跟Undertow的包未导入,所以默认运用的是tomcat

      如果需要Jetty生效,需要导入架包,并且将tomcat架包排除

      1
      2
      3
      4
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jetty</artifactId>
      </dependecy>

5、Svlet web服务工厂自动配置总结

1、ServletWebServerApplicationContext中有个核心方法refresh,

2、这个refresh方法又调用了父类的refresh方法调用了子类的onRefresh()方法,

3、子类的onRefresh()方法中创建了web服务器,

4、通过getWebServerFactory()方法来获取当前是什么服务工厂,有三个工厂 tomcat、Jetty、Undertow

5、默认为tomcat,因为springboot默认导入了tomcat的依赖,如果要进入别的方法,则需要导入依赖,并且排除tomcat依赖的导入,当条件满足后,这个工厂则会判断有没有其他工厂已经执行,如果有,则不初始化,如果没有则开始初始化,因为ConditionalOnMissingBean这个注解,

6、初始化后则会进入指定跟工厂的ServletWebServerFactory方法(因为默认进入的是tomcat所以就写tomcat的流程,其他的则是把tomcat改为另外来个即可),

7、调用getWebServer方法,结束的时候调用getTomcatWebServer方法,

8、getTomcatWebServer方法new一个TomcatWebServer对象,

9、构造方法结束的时候调用了initialize初始化方法,启动了tomcat服务器

5、注册Servlet三大组件

三大组件 Servlet(处理请求)、 Filter(过滤请求)、 Listener(监听tomcat,request等)

由于SprringBoot默认是以jar包启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml

1、配置一个配置类

1
2
3
@Configuration
public class MyConfig {
}

2、配置Servlet处理请求

1
2
3
4
5
6
7
8
9
10
public class TestServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("TestServlet.doPost");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
}

在配置类注册一个bean

1
2
3
4
5
6
7
8
9
10
11
@Bean
public ServletRegistrationBean<Servlet> servletRegistrationBean(){
//方法一
return new ServletRegistrationBean<> (new TestServlet(),"/test");

//方法二
//ServletRegistrationBean servletRegistrationBean=new ServletRegistrationBean();
////servletRegistrationBean.setServlet(new TestServlet());
//servletRegistrationBean.setUrlMappings(Arrays.asList(new String[]{"/text"}));
//return servletRegistrationBean;
}

3、配置Filter(过滤请求)

1
2
3
4
5
6
7
8
public class TestFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器开始");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("过滤器结束");
}
}

在配置类配置bean

1
2
3
4
@Bean
public FilterRegistrationBean<Filter> filterRegistrationBean(){
return new FilterRegistrationBean<>(new TestFilter(),servletRegistrationBean());
}

当然,这个配置也有俩个方法可以完成

4、配置Listener(监听tomcat,request等)

Listener有四个接口

简单点的配置监听上下文与监听请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TestListener implements ServletContextListener, ServletRequestListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("上下文初始化");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("销毁方法");
}

@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("请求初始化");
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("销毁请求");
}
}

在配置类注册一个bean

1
2
3
4
@Bean
public ServletListenerRegistrationBean<EventListener> servletListenerRegistrationBean(){
return new ServletListenerRegistrationBean<>(new TestListener());
}

5、实现原理(未写)

6、实现原理总结(未写)

6、使用外置的Servlet容器

这个在快速创建springboot项目时,需要选择war包

需要导入web.xml

1、导入web.xml

1、使用ctrl+回车跳出项目结构

2、配置xml的路径与版本

3、查看下面那个是爆红,如果红了直接点击,保存结束

4、结束后web.xml创建成功

2、使用外置Servlet原理

  • 运用了servlet3.0新特性会将spring -web 下面的meta-inf下面的services里面的内容在启动的时候运行

springboot引入外部servlet原理1

点进去这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
  • 在使用onStartup方法的时候,会找到servlet所有容器中实现WebApplicationInitializer这个接口的具体实现类

  • 现在看项目中的ServletInitializer方法

1
2
3
4
5
6
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringbootJspApplication.class);
}
}
  • ServletInitializer方法继承了SpringBootServletInitializer类,SpringBootServletInitializer类又实现了WebApplicationInitializer接口
1
public abstract class SpringBootServletInitializer implements WebApplicationInitializer {}
  • 所以在启动的时候会将ServletInitializer会执行SpringBootServletInitializer的onStartup方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Override//参数为获取servlet上下文
    public void onStartup(ServletContext servletContext) throws ServletException {
    //获取日志记录器
    this.logger = LogFactory.getLog(getClass());
    //调用createRootApplicationContext方法创建root上下文
    WebApplicationContext rootApplicationContext = createRootApplicationContext(servletContext);
    if (rootApplicationContext != null) {
    servletContext.addListener(new SpringBootContextLoaderListener(rootApplicationContext, servletContext));
    }
    else {
    this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not "
    + "return an application context");
    }
    }
    • createRootApplicationContext方法

      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
      34
      35
      36
      37
      38
      39
      protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
      //创建spring应用程序构建器
      SpringApplicationBuilder builder = createSpringApplicationBuilder();
      //告示springboot这个类的的class对象的方法是哪个方法
      builder.main(getClass());
      //获取已经存在的web上下文(spring ioc容器)
      ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
      if (parent != null) {
      this.logger.info("Root context already created (using as parent).");
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
      builder.initializers(new ParentContextApplicationContextInitializer(parent));
      }
      //设置springboot初始化器
      builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
      //告示spirngboot当前环境以什么形式来启动springboo上下文(xml/注解、web上下文的注解)
      builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
      //告示springboot我们的主配置类class对象是哪一个
      builder = configure(builder);
      //初始化springboot的监听器
      builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
      //构造一个SpringApplication对象
      SpringApplication application = builder.build();
      if (application.getAllSources().isEmpty()
      && MergedAnnotations.from(getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent(Configuration.class)) {
      application.addPrimarySources(Collections.singleton(getClass()));
      }
      //设置日志
      Assert.state(!application.getAllSources().isEmpty(),
      "No SpringApplication sources have been defined. Either override the "
      + "configure method or add an @Configuration annotation");
      // Ensure error pages are registered
      //判断是否是错误配置页面过滤器
      if (this.registerErrorPageFilter) {
      application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
      }
      application.setRegisterShutdownHook(false);
      //调用run方法开始交给spirngboot执行后续流程(初始化ioc容器,初始化springboot自动配置等)
      return run(application);
      }
    • run方法

      1
      2
      3
      protected WebApplicationContext run(SpringApplication application) {
      return (WebApplicationContext) application.run();
      }
    • 这个run方法又调用了SpringApplication的run方法

      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
      34
      35
      36
      37
      38
      39
      40
      41
      public ConfigurableApplicationContext run(String... args) {
      StopWatch stopWatch = new StopWatch();
      stopWatch.start();
      ConfigurableApplicationContext context = null;
      configureHeadlessProperty();
      //获取监听器
      SpringApplicationRunListeners listeners = getRunListeners(args);
      //监听器启动
      listeners.starting();
      try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      //开启创建spirng ioc容器
      context = createApplicationContext();
      //准备上下文
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      //准备刷新上下文,初始化单例对象
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
      new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      listeners.started(context);
      callRunners(context, applicationArguments);
      }
      catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
      }
      try {
      listeners.running(context);
      }
      catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
      }
      return context;
      }
    • createApplicationContext() //创建应用上下文对象

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      protected ConfigurableApplicationContext createApplicationContext() {

      Class<?> contextClass = this.applicationContextClass;
      if (contextClass == null) {
      try {
      switch (this.webApplicationType) {
      case SERVLET:
      contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
      break;
      case REACTIVE:
      contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
      break;
      default:
      contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
      }
      }
      catch (ClassNotFoundException ex) {
      throw new IllegalStateException(
      "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
      }
      }
      //通过class创建并返回一个上下文对象
      return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
      }
    • refreshContext(ConfigurableApplicationContext context)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      private void refreshContext(ConfigurableApplicationContext context) {
      if (this.registerShutdownHook) {
      try {
      context.registerShutdownHook();
      }
      catch (AccessControlException ex) {
      // Not allowed in some environments.
      }
      }
      refresh((ApplicationContext) context);
      }
    • refresh(ApplicationContext applicationContext)刷新上下文

      1
      2
      3
      4
      5
      @Deprecated
      protected void refresh(ApplicationContext applicationContext) {
      Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
      refresh((ConfigurableApplicationContext) applicationContext);
      }

12、spirngboot运行原理

1
2
3
4
5
6
@SpringBootApplication
public class SprringbootCrudApplication {
public static void main(String[] args) {
SpringApplication.run(SprringbootCrudApplication.class, args);
}
}

首先这个run方法内的参数不能乱给,因为自动配置的时候会去找应用程序的包,

args必须要给,因为这个是用于有时候我们切换场景时候要用,如果不传这个参数,则会不起效果

  • 进入run方法

    这是一个静态方法,调用了自己另外一个重载的方法

    1
    2
    3
    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class<?>[] { primarySource }, args);
    }
  • run(new Class<?>[] { primarySource }, args);

    1
    2
    3
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
    }
    • new了一个构造方法,进入这个构造方法

      1
      2
      3
      public SpringApplication(Class<?>... primarySources) {
      this(null, primarySources);
      }
    • 这个构造方法显时调用了另外一个构造方法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
      //将资源加载器赋值
      this.resourceLoader = resourceLoader;
      //在main方法传对象的时候,不能为空,否则会报错PrimarySources must not be null
      Assert.notNull(primarySources, "PrimarySources must not be null");
      this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
      //获取当前运行环境的类型
      this.webApplicationType = WebApplicationType.deduceFromClasspath();
      setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
      setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
      this.mainApplicationClass = deduceMainApplicationClass();
      }

13、自定义监听器

在resoures下面创建META-INF下面创建spring.factories,因为springboot在启动的时候会自动去找

通过ApplicationContextInitializer来获取自定义监听器

1
2
org.springframework.context.ApplicationContextInitializer=\
com.znsd.springboot.springbootintalizertext.initialize.MyInitializer

14、mybatis-plus使用

mybatis-plus就是mybatis的封装,将单表中的一些查询封装起来

意味着我们只需要写多表的sql语句,大大的简化了代码

官方地址:

使用:

1、maven依赖

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
<!--mybatis-plus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.2</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>

<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<!--简化属性类依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

2、yml配置

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
34
35
36
37
38
spring:
datasource:
url: jdbc:mysql://192.168.0.25:3306/test?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true&nullCatalogMeansCurrent=true
username: znsd_test
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
platform: mysql
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 1
minIdle: 3
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 30000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,wall,slf4j
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

mybatis-plus:
type-aliases-package: com.znsd.api
type-aliases-super-type: java.lang.Object
configuration:
map-underscore-to-camel-case: true
cache-enabled: true
lazy-loading-enabled: true
multiple-result-sets-enabled: true
use-generated-keys: true
default-statement-timeout: 60
default-fetch-size: 100
mapper-locations: classpath*:mybatis-mappings/*.xml
logging:
level:
com.znsd.springboot: debug # 查看执行sql

3、添加配置类

1
2
3
4
5
6
7
8
9
@Configuration
@EnableTransactionManagement
@MapperScan("com.znsd.springboot.springbootmybatisplus.dao")
public class MyConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}

4、dao层

1
2
3
@Repository
public interface UserDao extends BaseMapper<User> {
}

5、server

1
2
public interface UserService extends IService<User> {
}

6、server实现

1
2
3
@Service
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService {
}