# 一.整体介绍

# 1.什么是 Springboot?

Springboot 是一个全新的框架,简化 Spring 的初始搭建和开发过程,使用了特定的方式来进行配置,让开发人员不再需要定义样板化的配置。此框架不需要配置 xml,依赖于 maven 这样的构建系统。

简单、快速、方便的搭建项目;对主流开发框架的无配置集成;极大提高了开发、部署效率。

# 2.Springboot 的优点

  • 提供一个快速的 Spring 项目搭建渠道。
  • 开箱即用 ,很少的 Spring 配置就能运行一个 Java EE 项目 。
  • 提供了生产级的服务监控方案。
  • 内嵌服务器,可以快速部署。
  • 提供了一系列非功能性的通用配置
  • 纯 Java 配置,没有代码生成,也不需要 XML 配置。

# 3.Springboot 的缺点

  • 将现有或传统的 Spring Framework 项目转换为 Spring boot 应用程序是一个非常困难和耗时的过程。
  • 使用简单, 学习成本高, 精通难。
  • 版本迭代速度很快,一些模块改动很大。
  • 由于不用自己做配置,报错时很难定位。
  • 网上现成的解决方案比较少。

# 4.Springboot 和 Spring

  • Springboot 可以建立独立的 spring 应用程序。

  • 内嵌了如 tomcat,Jetty 和 Undertow 这样的容器,也就是说可以直接跑起来,用不着再做部署工作。

  • 无需再像 spring 那样写一堆繁琐的 XML 配置文件

  • 可以自动配置 spring

  • 提供的 POM 可以简化 maven 的配置

# 5.Springboot 和 springMVC

  • SpringMVC 是基于 spring 的一个 MVC 框架。
  • springboot 的基于 spring 的条件注册的一套快速开发整合包。

# 6.为何可以快速集成?

因为启动器 start 包含一些依赖项,配置一下就可以用,所以快

底层原理是啥?

基于观察者模式,启动后会把相关配置加入到 environment 对象,监听到对象后 Springboot 去加载实现整体自动化。

# 7.SpringBoot 种启动方式?

1.main 启动:

直接main方法启动DemoApplication
1

2.maven 启动:

mvn spring-boot:run
1

3.jar 包:

java -jar tx_demo2-0.0.1-SNAPSHOT.jar
1

4.docker 启动:

FROM  openjdk:8-jdk-alpine
ARG  JAR_FILE
COPY  ${JAR_FILE}  app.jar
EXPOSE  10001
ENTRYPOINT  ["java","-jar","/app.jar"]
1
2
3
4
5
docker run -p 8080:8080 tx_demo2:1.0
1

# 8.Spring Boot devtools

Spring Boot 提供了一组开发工具 spring-boot-devtools 可以提高开发者的工作效率,开发者可以将该模块包含在任何项目中,spring-boot-devtools 最方便的地方莫过于热部署了。

要想在项目中加入 devtools 模块,只需添加相关依赖即可,代码如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>
1
2
3
4
5

注意:这里多了 一个 optional 选项,是为了防止将 devtools 依赖传递到其他模块中。当开发者将应用打包运行后,devtools 会被自动禁用 当开发者将 spring-boot-devtools 引入项目后,只要 classpath 路径下发生变化,项目就会自动重启,这极大地提高了项目的开发速度。如果开发者使用 Eclipse ,那么在修改完代码并保存之后,项目将自动编译井触发重启,而开发者如果使用 IntelliJ IDEA 默认情况下,需要开发者手动编译才会触发重启。手动编译时,单击 Build -> Build Project 菜单或者按 Ctrl+F9 快捷键进行编译,编译成功后就会触发项目重启。当然,使用 IntelliJ IDEA 开发者也可以配置项目自动编译,配置步骤:

单击 File -> settings 菜单,打开 settings 页面,在左边的菜单栏依次找到 Build,Execution,Deployment -> Compile,勾选 Build project automatically 如图所示。

image-20230817235735459

按住 Ctrl+Shift+Alt+/ 快捷捷键调出 Maintenance 页面

image-20230817235755668

选择 Registry,在新打开的 Registry 页面中,勾选 compiler.automake.allow.when.app.running 复选框。配置完成后,若开发者修改了代码则将会自动重启。

image-20230817235812452

注意:classpath 路径下的静态资源或者视图模板等发生变化时,并不会导致项目重启。

# 9.如何解决循环依赖

从 2.6 版本开始,如果你的项目里还存在循环依赖,SpringBoot 将拒绝启动!

image-20221221164250165

临时开启循环依赖

spring.main.allow-circular-references=true
1

# 二.底层原理

# 1.自动装配原理?

Spring Boot 的自动装配(Auto-Configuration)原理是该框架提供了一种自动配置机制,能够根据应用程序的依赖关系和需要,自动地为应用程序配置各种 Spring 框架的组件和功能。

下面是 Spring Boot 自动装配的基本原理:

  1. 条件装配(Conditional Configuration):Spring Boot 使用条件注解来判断是否应该自动配置某个组件。条件注解可以基于一组条件来控制组件是否应该被创建。例如,只有当某个特定的类在类路径下存在时,才会创建某个组件。这样可以确保只有满足特定条件的情况下才会触发自动配置。

  2. 自动配置类(Auto-Configuration Classes):Spring Boot 在内部定义了许多自动配置类,每个自动配置类都对应着一个特定的功能模块。这些类通常包含了一个或多个带有 @Configuration 注解的方法,这些方法会创建和配置所需的 bean。这些自动配置类会根据条件注解判断是否要被加载。

  3. META-INF/spring.factories 文件:Spring Boot 会在类路径下查找名为 META-INF/spring.factories 的文件。这个文件中列出了所有自动配置类的全限定类名。当 Spring Boot 启动时,它会加载这些自动配置类。

  4. 启动类的注解:通常,Spring Boot 项目会有一个主启动类,该类上会使用 @SpringBootApplication 注解,它本身就是一个组合注解,包含了 @EnableAutoConfiguration 注解。这个注解告诉 Spring Boot 启动时要自动应用配置。

总体来说,Spring Boot 的自动装配通过条件注解、自动配置类和 spring.factories 文件的组合,根据应用程序的需求,决定了哪些组件会被自动配置并加载到 Spring 的应用上下文中。这使得开发者可以更加专注于业务逻辑,而无需手动配置大量的 Spring 组件。如果需要自定义配置,开发者仍然可以通过提供自己的配置来覆盖默认的自动配置行为。

# 2.Spring Boot 的启动流程

启动分为3个阶段:

  • 第一个阶段是启动类的加载和执行

  • 第二个阶段是 Spring Boot 的自动配置

  • 第三个阶段是 Spring Boot 应用的启动和运行。

详细说明:

  • 第一个阶段,Spring Boot 启动类会被加载进 JVM 中,并调用其 main() 方法。在该方法中,会调用 SpringApplication.run() 方法来启动 Spring Boot 应用。这个方法中会创建一个 Spring 应用上下文,并加载所有的自动配置类
  • 第二个阶段是 Spring Boot 的自动配置。Spring Boot 通过在类路径下扫描 META-INF/spring.factories文件来自动发现所有的自动配置类,并根据其顺序进行自动配置。在这一阶段中,Spring Boot 会根据配置文件中的设置,自动装配数据源、事务管理器、Web 相关组件等。
  • 在第三个阶段,Spring Boot 应用启动完毕,并开始运行。在这个阶段,Spring Boot 会扫描所有的 Bean,并根据依赖注入的规则完成依赖注入。最终,Spring Boot 会启动 Web 容器,并将 Web 应用部署到容器中,等待请求的到来。

Spring Boot的启动流程可以分为以下主要步骤:

  1. 加载 Spring Boot 应用程序类:应用程序的入口是一个 Java 类,通常带有main方法。在该类中,使用SpringApplication.run(...)来启动 Spring Boot 应用程序。这将初始化 Spring 应用程序上下文并引导应用程序。

  2. 创建 Spring 应用程序上下文:Spring Boot 会创建一个 Spring 应用程序上下文(ApplicationContext)。这是一个 IoC(控制反转)容器,用于管理所有应用程序的 Bean(组件)。

  3. 自动配置:Spring Boot 会自动配置应用程序。这意味着根据类路径上的依赖关系,Spring Boot 会自动配置数据源、模板引擎、安全性等各种功能。您可以使用application.propertiesapplication.yml文件进行自定义配置。

  4. 扫描和注册 Bean:Spring Boot 会扫描应用程序中的所有类,并注册它们作为 Bean。这包括使用@Component@Service@Repository等注解标记的类。

  5. 运行应用程序:Spring Boot 会启动嵌入式的 Web 服务器(如 Tomcat、Jetty 或 Undertow)以运行应用程序。如果应用程序是非 Web 应用程序,它仍然会执行相关的初始化和运行操作。

  6. 处理 HTTP 请求:当应用程序接收到 HTTP 请求时,Spring Boot 的 DispatcherServlet(如果是 Web 应用程序)会根据 URL 路由到相应的控制器,然后执行业务逻辑。

  7. 处理业务逻辑:控制器处理业务逻辑并调用服务层、数据访问层等其他组件来处理请求。这些组件是由 Spring 容器管理的 Bean。

  8. 返回 HTTP 响应:控制器完成业务逻辑后,将结果返回给客户端。Spring Boot 自动将结果转换为 JSON、HTML 或其他所需的格式,并将其发送给客户端。

  9. 关闭应用程序上下文:当应用程序关闭时,Spring Boot 会关闭应用程序上下文并清理资源。这通常发生在ApplicationContextclose方法被调用时,例如在应用程序终止时。

  10. 应用程序停止:Spring Boot 应用程序停止运行,释放所有资源,并退出。

Spring Boot 的启动流程包括创建应用程序上下文、自动配置、注册 Bean、运行应用程序、处理 HTTP 请求、执行业务逻辑、返回 HTTP 响应、关闭应用程序上下文和应用程序停止等步骤。Spring Boot 的目标是通过简化配置和提供默认值来简化应用程序的开发和部署,使开发者可以更专注于编写业务逻辑。

# 三.配置

# 1.父依赖

其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件!

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
1
2
3
4
5
6

点进去,发现还有一个父依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>
1
2
3
4
5
6

这里才是真正管理 SpringBoot 应用里面所有依赖版本的地方,SpringBoot 的版本控制中心;

以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;

在没有父 POM 的情况下使用 Spring Boot

不是每个人都喜欢继承spring-boot-starter-parent POM。您可能拥有自己需要使用的公司标准父级,或者您可能希望明确声明所有 Maven 配置。

如果您不想使用spring-boot-starter-parent,您仍然可以通过使用scope=import依赖项来保持依赖项管理(但不是插件管理)的好处,如下所示:

<dependencyManagement>
		<dependencies>
		<dependency>
			<!-- Import dependency management from Spring Boot -->
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-dependencies</artifactId>
			<version>2.1.1.RELEASE</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>
1
2
3
4
5
6
7
8
9
10
11
12

此时,就可以不用继承 spring-boot-starter-parent 了,但是 Java 的版本、编码的格式等都需要开发者手动配置。 Java 版本的配置很简单,添加一个 plugin 即可:

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-compiler-plugin</artifactId>
   <version>3.1</version>
   <configuration>
       <source>1.8</source>
       <target>1.8</target>
   </configuration>
</plugin>
1
2
3
4
5
6
7
8
9

编码格式

 <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.reporting.outputEncodin>UTF-8</project.reporting.outputEncodin>
 </properties>
1
2
3
4

# 2.配置文件优先级

加载顺序

bootstrap.yml > bootstrap.properties > application.yml > application.properties

启动命令加 -D 参数名 = 参数值 覆盖配置文件中的配置

  • SpringBoot 提供了 2 种配置文件类型:properteisyml/yaml
  • 默认配置文件名称:application
  • 在同一级目录下优先级为:properties > yml > yaml

# 3.导入其他配置类

@Import注释可用于导入其他配置类。或者可以使用@ComponentScan自动选取所有 Spring 组件,包括@Configuration类。

# 4.导入 XML 配置

如果必须使用基于 XML 的配置,建议使用@Configuration类。然后可以使用@ImportResource注释来加载 XML 配置文件。

# 5.禁用特定的自动配置类

如果发现正在应用您不需要的特定自动配置类,则可以使用@EnableAutoConfiguration的 exclude 属性禁用它们,如以下示例所示:

import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.jdbc.*;
import org.springframework.context.annotation.*;

@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}
1
2
3
4
5
6
7
8

如果类不在类路径上,则可以使用注释的excludeName属性并指定完全限定名称。最后,您还可以使用spring.autoconfigure.exclude属性控制要排除的自动配置类列表。

# 6.Spring Beans 和依赖注入

您可以自由使用任何标准 Spring 框架技术来定义 beans 及其注入的依赖项。为简单起见,我们经常发现使用@ComponentScan(找到你的 beans)和使用@Autowired(做构造函数注入)效果很好。

如果按照上面的建议构建代码(在根包中定位应用程序类),则可以添加@ComponentScan而不带任何参数。您的所有应用程序组件(@Component@Service@Repository@Controller等)都会自动注册为 Spring Beans。

@ComponentScan可以使用替代@Import({ MyConfig.class, MyAnotherConfig.class })
1

# 7.自定义横幅

通过将banner.txt文件添加到类路径或将spring.banner.location属性设置为此类文件的位置,可以更改启动时打印的横幅。如果文件的编码不是 UTF-8,则可以设置spring.banner.charset。除了文本文件,您还可以将banner.gifbanner.jpgbanner.png图像文件添加到类路径或设置spring.banner.image.location属性。图像将转换为 ASCII 艺术表示,并打印在任何文本横幅上方。

Spring Boot 项目在启动时会打印一个 banner,这个 banner 是可以定制的,在 sources 目录下创建 banner.txt 文件,在这个文件中写入的文本将在项目启动时打印出来 如果想将 TXT 文本设成艺术字体,有以下几个在线网站可供参考

# 8.配置随机值

RandomValuePropertySource对于注入随机值(例如,进入秘密或测试用例)非常有用。它可以生成整数,长整数,uuids 或字符串,如以下示例所示:

my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}
1
2
3
4
5
6

random.int*语法为OPEN value (,max) CLOSE,其中OPEN,CLOSE为任意字符,value,max为整数。如果提供max,那么value是最小值,max是最大值(不包括)。

# 9.特定于配置文件的属性

除了application.properties文件之外,还可以使用以下命名约定来定义特定于配置文件的属性:application-{profile}.propertiesEnvironment有一组默认配置文件(默认情况下为[default]),如果未设置活动配置文件,则使用这些配置文件。换句话说,如果没有显式激活配置文件,则会加载application-default.properties中的属性。

特定于配置文件的属性从标准application.properties的相同位置加载,特定于配置文件的文件始终覆盖非特定文件,无论特定于配置文件的文件是在打包的 jar 内部还是外部。

如果指定了多个配置文件,则应用 last-wins 策略。例如,spring.profiles.active属性指定的配置文件将在通过SpringApplication API 配置的配置文件之后添加,因此优先。

# 10.属性中的占位符

application.properties中的值在使用时通过现有的Environment进行过滤,因此您可以返回先前定义的值(例如,从系统属性中)。

app.name=MyApp
app.description=${app.name} is a Spring Boot application
1
2

# 11.YAML

  • 大小写敏感
  • 数据值前边必须有空格,作为分隔符
  • 使用缩进表示层级关系
  • 缩进时不允许使用 Tab 键,只允许使用空格(各个系统 Tab 对应的空格数目可能不同,导致层次混乱)。
  • 缩进的空格数目不重要,只要相同层级的元素左侧收对齐即可
  • #表示注释,从这个字符一直到行尾,都会被解析器忽略。

# 12.配置加载顺序

内部配置加载顺序

Springboot 程序启动时,会从以下位置加载配置文件:

  1. file:./config/: 当前项目下的/config 目录下

  2. file:./: 当前项目的根目录

  3. classpath:/config/: classpath 的/config 目录

  4. classpath:/: classpath 的根目录 (上面文档中的方式)

image-20231218140120960

注意: 1、2 是不会打入项目的 jar 包的, 而 3、4 是会最终编入 jar 中。

加载顺序为上文的排列顺序,高优先级配置的属性会生效

指定位置加载配置文件

我们还可以通过 spring.config.location 来改变默认的配置文件位置

项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;这种情况,一般是后期运维做的多,相同配置,外部指定的配置文件优先级最高

java -jar spring-boot-config.jar --spring.config.location=F:/application.properties
1

外部配置的加载顺序

外部配置优先级高于内部的配置,内部配置是静态的可以看作是默认值,而外部配置是可以动态修改,这样极大提高灵活性。

# 四.注解

# 1.@SpringBootApplication

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}
1
2
3
4
5
6
7
8
9

@SpringBootApplication 是一个 Spring Boot 框架中非常重要的注解,通常被用于标注主类。这个注解是一个复合注解,本质上包括了以下三个主要的注解:

  1. @SpringBootConfiguration: 标注当前类是配置类,并会将其作为 Spring 应用上下文(Application Context)的源。
  2. @EnableAutoConfiguration: 使得 Spring Boot 自动配置生效。自动配置是一种非侵入式的配置,能自动为你配置 Spring 应用。
  3. @ComponentScan: 扫描当前包和其子包下的所有 Spring 组件。

工作过程:

  1. 启动入口: 当你运行一个包含 @SpringBootApplication 的主类的 main 方法,实际上你是在启动一个 Spring 应用。

  2. 初始化 Spring 应用上下文: Spring Boot 会初始化一个 Spring 应用上下文,它会包括项目中所有的 Spring 组件(比如被 @Component, @Service, @Controller 等注解的类)。

  3. 配置类注册: 由于 @SpringBootConfiguration 的存在,主类也被作为一个配置类(等同于一个用 @Configuration 标注的类)。

  4. 组件扫描: 因为有 @ComponentScan,Spring Boot 会扫描主类所在的包以及其子包,寻找所有的 Spring 组件。

  5. 自动配置: 由于 @EnableAutoConfiguration,Spring Boot 会尝试根据你添加的依赖自动配置你的应用。例如,如果你在 pom.xml 中加入了 spring-boot-starter-web,Spring Boot 会自动为你配置一个嵌入式的 Tomcat 服务器以及 Spring Web MVC。

  6. 属性绑定: Spring Boot 还会从各种属性文件(如 application.propertiesapplication.yml)中读取配置,进行更进一步的自定义配置。

  7. 运行应用: 最后,所有的组件和配置都准备好后,Spring Boot 会运行应用。

  8. 监听器与拦截器: 在应用运行期间,你可以通过各种监听器和拦截器来进行更细粒度的控制。

  9. 关闭与销毁: 当应用关闭时,Spring Boot 也会负责适当地关闭应用上下文和释放资源。

通过这一系列的步骤,@SpringBootApplication 注解简化了 Spring 应用的配置和启动过程,让开发者能更加专注于业务逻辑的开发。

# 2.四个元注解

  • @Target(ElementType.TYPE): 标志该注解可以作用于 interface、class、enum。
  • @Retention(RetentionPolicy.RUNTIME): 注解的保留策略;
    • 当前注解会出现在.class 字节码文件中
    • 并且在运行的时候可以通过反射被获得。
  • @Documented: 标志该注解可以被 javadoc 工具所引用。
  • @Inherited: 标志该注解可以被继承。

# 3.核心注解

  • @SpringBootConfiguration
  • @ComponentScan
  • @EnableAutoConfiguration

@SpringBootConfiguration 它继承了@Configuration;Spring3.0 开始增加了@Configuration、@Bean 注解,

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}
1
2
3
4
5
6
7
8
9
10

@ComponentScan@Component 这一类的注解配合使用,相当于以前 xml 配置中开启自动扫描的 context:component-scan 标签,可以配置 Spring 的扫描范围、Bean 的 ID 命名策略等属性;

  • 默认扫描范围为当前目录下的所有 class,
  • 这也是 SpringBoot 入口类要放在代码根目录的原因。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {}
1
2
3
4
5

@ComponentScan 会扫描并装配代码根目录下的,所有的带@Component 注解的 class;但是 SpringBoot 如何加载代码根目录之外的 Bean,比方说 classpath 下 maven 依赖中的;这个时候就需要了解一下

META-INF/spring.factories 文件的作用了

@import :Spring 底层注解@import , 给容器中导入一个组件

@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;

@EnableAutoConfiguration

AutoConfigurationImportSelector: 自动配置导入选择器

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}
1
2
3
4
5
6
7
8
9
10
11
12
13

单个@SpringBootApplication注释可用于启用这三个功能,即:

  • @EnableAutoConfiguration:启用 Spring Boot 的自动配置机制
  • @ComponentScan:对应用程序所在的软件包启用@Component 扫描
  • @Configuration:允许在上下文中注册额外的 beans 或导入其他配置类

@SpringBootApplication注释等效于使用@Configuration@EnableAutoConfiguration@ComponentScan及其默认属性。

META-INF/spring.factories ,核心配置文件,主要加载 maven 引入的包的类

结论: Springboot 所有自动配置都是在启动的时候扫描并加载, spring.factories 所有的自动配置类都在这里面

但是不一定生效, 要@ConditionalonXXXaa条件成立

image-20230818000500574

# 4.选择器注解

  • @Conditional,当指定的条件都满足时,组件才被注册
  • @ConditionalOnBean,指定 bean 在上下文中时,才注册当前 bean。用在方法上,则默认依赖类为方法的返回类型
  • @ConditionalOnClass,指定类在 classpath 上时,才初始化当前 bean。用在方法上,则默认依赖类为方法的返回类型
  • @ConditionalOnCloudPlatform,在指定云平台才注册配置
  • @ConditionalOnExpression,指定 spel 为 true 时注册配置
  • @ConditionalOnJava,在指定 java 版本时注册配置
  • @ConditionalOnJndi
  • @ConditionalOnMissingBean,指定 bean 不在上下文中时,才初始化当前 bean。用在方法上,则默认依赖类为方法的返回类型
  • @ConditionalOnMissingClass,指定类不在 classpath 上时,才初始化当前 bean。用在方法上,则默认依赖类为方法的返回类型
  • @ConditionalOnNotWebApplication,不是在 web 环境才注册配置
  • @ConditionalOnProperty,配置文件中的值与指定值是否相等,相等才注册配置
  • @ConditionalOnResource,指定 resources 都在 classpath 上才注册配置
  • @ConditionalOnSingleCandidate,上下文中只有一个候选者 bean 时才注册配置
  • @ConditionalOnWebApplication,是在 web 环境才注册配置

# 5.缓存注解

  • @EnableCaching,开启缓存配置,支持子类代理或者 AspectJ 增强
  • @CacheConfig,在一个类下,提供公共缓存配置
  • @Cacheable,放着方法和类上,缓存方法或类下所有方法的返回值
  • @CachePut,每次先执行方法,再将结果放入缓存
  • @CacheEvict,删除缓存
  • @Caching,可以配置@Cacheable、@CachePut、@CacheEvict

# 6.定时器注解

  • @EnableScheduling,开启定时任务功能
  • @Scheduled,按指定执行周期执行方法
  • @Schedules,包含多个@Scheduled,可同时运行多个周期配置
  • @EnableAsync,开启方法异步执行的能力,通过@Async 或者自定义注解找到需要异步执行的方法。通过实现 AsyncConfigurer 接口的 getAsyncExecutor()和 getAsyncUncaughtExceptionHandler()方法自定义 Executor 和异常处理。
  • @Async,标记方法为异步线程中执行

# 7.注入配置文件

  • @EnableConfigurationProperties,启动@ConfigurationProperties 功能
  • @ConfigurationProperties,将 properties 文件里的内容,自动注入 bean 对应的属性中
  • @DeprecatedConfigurationProperty,用在配置文件的 getter()方法上,标记字段已经过期,并提示替换的字段。一般给 spring-boot-configuration-processor 使用。
  • @NestedConfigurationProperty,标记在配置文件的字段上,提示 spring-boot-configuration-processor,配置包含嵌套的配置。
  • spring-configuration-metadata.json 提供配置的元信息,在写 properties 配置时,会有语法提示。在项目中引入 spring-boot-configuration-processor 项目,会扫描@ConfigurationProperties 注解,自动生成 spring-configuration-metadata.json

# 8.@Autowired

为什么不推荐使用@Autowired 属性注入,@Autowired 本质是通过反射类进行的属性注入的,因此执行时刻是对象创建完成之后。

解决方式:

  • 构造器的方式
  • lombok 全参数构造器方式@RequiredArgsConstructor

优点:

  • 项目启动时性能的提升使用构造器替换以反射为基础的成员变量初始化,最直接的收益是项目启动时性能的提升
  • 基于构造函数注入的主要优点是可以将需要注入的字段声明为 final, 使得它们会在类实例化期间被初始化,这对于所需的依赖项很方便。

nacos源码

image-20230720165310307

# 9.@AliasFor

  • 指定别名,属性互换
  • 将一个注解上的属性值传递给另一个注解对另一个注解的属性进行别名覆盖
  • 组合多个注解,通过一个注解达到使用多个注解的效果

# 10.注解总结

@SpringBootApplication :包含了@ComponentScan、@Configuration 和@EnableAutoConfiguration 注解。其中@ComponentScan 让 spring Boot 扫描到 Configuration 类并把它加入到程序上下文。

@Configuration 等同于 spring 的 XML 配置文件;使用 Java 代码可以检查类型安全。

@EnableAutoConfiguration 自动配置。

@ComponentScan 组件扫描,可自动发现和装配一些 Bean。

@Component 可配合 CommandLineRunner 使用,在程序启动后执行一些基础任务。

@RestController 注解是@Controller 和@ResponseBody 的合集,表示这是个控制器 bean,并且是将函数的返回值直 接填入 HTTP 响应体中,是 REST 风格的控制器。

@Autowired 自动导入。

@PathVariable 获取参数。

@JsonBackReference 解决嵌套外链问题。

@RepositoryRestResourcepublic 配合 spring-boot-starter-data-rest 使用。

# 五.常见问题

# 1.加载整体流程?

image-20230818001117903

image-20230818001137574

# 2.starter 是如何被加载的?

  1. Springboot 在启动的时候, 从类路径下 META-INF/spring.factories 获取指定的值

  2. 将这些自动配置的类导入容器, 自动配置就会生效, 帮我们进行自动配置

  3. 以前是需要自己配置的东西, 现在 Springboot 帮忙做

  4. 整合 javaEE, 解决方案和自动配置的东西都在 spring-boot-autoconfigure-2.0.0.RELEASE.jar 包下

  5. 它会把所有需要导入的组件, 已类名的方式返回,这些组件就会被添加到容器.

run 方法

public ConfigurableApplicationContext run(String... args) {
  StopWatch stopWatch = new StopWatch();
  stopWatch.start();
  ConfigurableApplicationContext context = null;
  Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
  configureHeadlessProperty();
  //获取所有的org.springframework.boot.SpringApplicationRunListeners实现类,这里我们也可以使用spring.factories自定义实现类。
  SpringApplicationRunListeners listeners = getRunListeners(args);
  listeners.starting();
  try {
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(
      args);
    //自定义监听器加载配置信息和系统环境变量
    ConfigurableEnvironment environment = prepareEnvironment(listeners,
                                                             applicationArguments);
    configureIgnoreBeanInfo(environment);
    Banner printedBanner = printBanner(environment);
    context = createApplicationContext();
    //获取所有spring.factories自定义实现,根据type值找到对应的 class  的全限定名称列表
    exceptionReporters = getSpringFactoriesInstances(
      SpringBootExceptionReporter.class,
      new Class[] { ConfigurableApplicationContext.class }, context);
    prepareContext(context, environment, listeners, applicationArguments,
                   printedBanner);
    //执行自定义spring.fatories中的类(Bean初始化类,环境变量初始化类)的指定方法
    refreshContext(context);
    afterRefresh(context, applicationArguments);
    stopWatch.stop();
    if (this.logStartupInfo) {
      new StartupInfoLogger(this.mainApplicationClass)
        .logStarted(getApplicationLog(), stopWatch);
    }
    listeners.started(context);
    //一些程序启动就要执行的的任务,包括spring.fatories中定义的key为EnableAutoConfiguration,
    //接口CommandLineRunner的实现类。
    callRunners(context, applicationArguments);
  }
  catch (Throwable ex) {
    handleRunFailure(context, listeners, exceptionReporters, ex);
    throw new IllegalStateException(ex);
  }
  listeners.running(context);
  return context;
}
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

# 3.SpringApplication 作用

这个类主要做了以下四件事情:

  1. 推断应用的类型是普通的项目还是 Web 项目
  2. 查找并加载所有可用初始化器 , 设置到 initializers 属性中
  3. 找出所有的应用程序监听器,设置到 listeners 属性中
  4. 推断并设置 main 方法的定义类,找到运行的主类
public class SpringApplication {
  private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
  }
  // 获取Spring工厂
  private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
                                                        Class<?>[] parameterTypes, Object... args) {
    // 获取ClassLoader
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    // 定义class数组,即返回值 names 是所有 classpath 下面的 META-INF/spring.factories 中定义的父节点
    Set<String> names = new LinkedHashSet<>(
      SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 内部循环初始化 names的构造函数,获取实例实例对象
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                                                       classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
  }
  // 创建Spring工厂实例
  private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
                                                     ClassLoader classLoader, Object[] args, Set<String> names) {
    List<T> instances = new ArrayList<>(names.size());
    // 遍历实例对象的全限定名
    for (String name : names) {
      try {
        // 加载该类,通过指定的classloader加载对应的类获取对应的Class对象
        Class<?> instanceClass = ClassUtils.forName(name, classLoader);
        // 断言是否为该接口的实现类
        Assert.isAssignable(type, instanceClass);
        // 获取构造方法
        Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
        // 实例化该类
        T instance = (T) BeanUtils.instantiateClass(constructor, args);
        // 添加到结果集当中
        instances.add(instance);
      }
      catch (Throwable ex) {
        throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
      }
    }
    return instances;
  }
}
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

SpringFactoriesLoader

public abstract class SpringFactoriesLoader {
   public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
   private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
    // 加载工厂
   public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
      // 获取类名称(图4)
      String factoryClassName = factoryClass.getName();
      // 根据类名称获取需要加载的工厂类名称
      return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
   }
    // 通过加载所有 classpath 下面的 META-INF/spring.factories文件,扫描加载类
   private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        // 从cache获取实例的结果集 当是Null表示当前的cache是空的;cache 实现 new ConcurrentReferenceHashMap<>()
      MultiValueMap<String, String> result = cache.get(classLoader);
      if (result != null) {
         return result;
      }
      try {
          // 获取所有 classpath 下面的 META-INF/spring.factories 中的资源 urls
          // 当classLoader为非空的时候调用getResouurces方法获取
          // 当classLoader为空的时候调用ClassLoader.getSystemResouurces方法获取
         Enumeration<URL> urls = (classLoader != null ?
               classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
               ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
         result = new LinkedMultiValueMap<>();
          // 循环处理 urls中的元素,获取元素
         while (urls.hasMoreElements()) {
             // 获取元素 url地址
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
             // 解析文件 把文件变成配置属性
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
             // 循环解析并把结果放入result
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                // 类名列表
               List<String> factoryClassNames = Arrays.asList(
                     StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));

               //记录所有的key和对应的value集合
               result.addAll((String) entry.getKey(), factoryClassNames);
            }
         }
          // 缓存类加载器和文件解析器的结果集
         cache.put(classLoader, result);
         return result;
      }
      catch (IOException ex) {
         throw new IllegalArgumentException("Unable to load factories from location [" +
               FACTORIES_RESOURCE_LOCATION + "]", ex);
      }
   }
}
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

在获取到所有的接口之后,就会调用 getSpringFactoriesInstances()创建接口的实现类。

spring.factories就像是工厂一样配置了大量的接口对应的实现类,我们通过这些配置 + 反射处理就可以拿到相应的实现类。这种类似于插件式的设计方式,只要引入对应的 jar 包,那么对应的 spring.factories 就会被扫描到,对应的实现类也就会被实例化,如果不需要的时候,直接把 jar 包移除即可。

# 4.Starter 的实现原理?

Starters 是一组方便的依赖描述符,您可以在应用程序中包含这些描述符。您可以获得所需的所有 Spring 和相关技术的一站式服务,而无需搜索示例代码和复制粘贴依赖描述符的负载。例如,如果要开始使用 Spring 和 JPA 进行数据库访问,请在项目中包含spring-boot-starter-data-jpa依赖项。

启动器包含许多依赖项,这些依赖项是使项目快速启动和运行所需的依赖项,以及一组受支持的托管传递依赖项。

Spring Starter 是 Spring Boot 框架中的一个重要概念,它实际上是一种约定,用于简化项目的初始化和配置。Spring Starter 的实现原理可以总结如下:

  1. 自动配置(Auto-Configuration):Spring Boot 的核心之一是自动配置机制。通过在类路径下的 META-INF/spring.factories 文件中定义自动配置类,Spring Boot 可以根据项目的依赖和配置,自动地配置各种组件、功能和属性。Spring Boot Starter 的核心思想就是使用自动配置来预置一组常见的配置,从而使得用户可以快速启动和运行应用。

  2. 条件化配置(Conditional Configuration):Spring Boot 使用条件注解和条件类来控制自动配置的生效。例如,@ConditionalOnClass 注解可以在类路径中存在某个特定类时才生效,@ConditionalOnProperty 注解可以根据配置属性的值来判断是否生效。通过这种方式,Spring Boot 可以根据不同的条件选择性地应用自动配置。

  3. Starter 模块:Spring Boot Starter 实际上是一组依赖库的集合,它们打包了特定功能或领域的常见依赖和配置。每个 Starter 模块都包含了自动配置、依赖管理和其他必要的资源。当用户在项目中添加一个特定的 Starter 依赖时,Spring Boot 会自动引入所需的依赖和配置,从而实现相关功能的快速集成。

  4. 约定优于配置:Spring Boot 鼓励使用约定而不是大量的配置来简化应用程序的开发。Starter 模块遵循一定的约定,使得开发人员可以更容易地构建和部署应用程序,而不必手动配置许多细节。

  5. Spring Boot Maven 插件:Spring Boot 提供了一个 Maven 插件,可以将应用程序打包成可执行的 JAR 或 WAR 文件。该插件能够识别项目中的 Starter 依赖,自动创建可执行的运行环境,并处理类路径、资源和配置。

Spring Starter 的实现原理基于自动配置、条件化配置和约定优于配置的思想,通过提供预配置的依赖和配置,帮助开发人员快速启动和构建 Spring Boot 应用程序。

# 5.如何命名?

所有官方首发都遵循类似的命名模式; spring-boot-starter-*,其中*是一种特殊类型的应用程序。此命名结构旨在帮助您找到启动器。许多 IDE 中的 Maven 集成允许您按名称搜索依赖项。例如,安装了适当的 Eclipse 或 STS 插件后,可以在 POM 编辑器中按ctrl-space并输入“spring-boot-starter”以获取完整列表。

第三方启动者不应以spring-boot开头,因为它是为官方 Spring Boot 工件保留的。相反,第三方启动器通常以项目名称开头。例如,名为thirdpartyproject的第三方启动项目通常被命名为thirdpartyproject-spring-boot-starter

命名归约:

官方命名:

  • 前缀:spring-boot-starter-xxx
  • 比如:spring-boot-starter-web....

自定义命名:

  • xxx-spring-boot-starter
  • 比如:mybatis-spring-boot-starter

启动器:

  • 启动器: 说白了就是 springboot 的启动场景(可以理解特定场景的整套解决方案)
  • 比如 spring-boot-starter-web, 它就会帮我们自动导入 web 环境所有的依赖
  • springboot 会讲所有的功能场景, 都变成一个个的启动器
  • 我们要用什么功能, 就只需要找到对应的启动器就可以了.

比如:

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>3.5.1</version>
</dependency>
1
2
3
4
5

# 6.自定义 starter

我们的多项目权限是如何做的?只需要一个登录校验即可解决,也就是自定义的 starter

1.需要封装的代码

@Configuration
@ConditionalOnWebApplication //web应用生效
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
    @Autowired
    HelloProperties helloProperties;

    @Bean
    public HelloService helloService(){
        HelloService service = new HelloService();
        service.setHelloProperties(helloProperties);
        return service;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

2.spring.factories

META-INF\spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.kuang.HelloServiceAutoConfiguration
1
2
3

作用:EnableAutoConfiguration 它用来记录 jar 包中需要注册到容器中的 Bean 的 qualified name,这样可以通过反射获取到对应的 class

AutoConfigurationImportSelector它里边有一个方法:getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes),作用是加载这些定义在 spring.factories 中的 Bean。对应@ConditionalOnWebApplication

3.安装到 maven 仓库

image-20221221154030508

# 7.@Scheduled 不生效

如果 @Scheduled 注解在 Spring Boot 应用中不生效,可能有一些常见的原因和解决方法,以下是一些可能的问题和解决方案:

  1. 启用定时任务功能: 确保在 Spring Boot 应用的主类上添加了 @EnableScheduling 注解,以启用定时任务功能。如果忘记添加这个注解,定时任务可能不会被触发。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class YourApplication {
    public static void main(String[] args) {
        SpringApplication.run(YourApplication.class, args);
    }
}
1
2
3
4
5
6
7
8
9
10
11
  1. 检查方法访问修饰符: 确保使用 @Scheduled 注解的方法的访问修饰符是 public,否则 Spring 无法正确地访问和调用该方法。
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class MyScheduledTasks {

    @Scheduled(cron = "0 0 * * * *") // 示例的定时表达式
    public void myScheduledMethod() {
        // your scheduled task logic here
    }
}
1
2
3
4
5
6
7
8
9
10
11
  1. 检查 Cron 表达式: 确保 @Scheduled 注解中的 Cron 表达式正确,并且在预期的时间范围内触发任务。可以使用在线 Cron 表达式生成工具来验证表达式的准确性。

  2. 使用固定延迟或固定速率: 除了 Cron 表达式,你还可以使用 @Scheduled 注解的 fixedDelayfixedRate 属性来设置固定延迟或固定速率执行任务。确保这些属性设置正确。

@Scheduled(fixedDelay = 5000) // 每隔5秒执行一次
public void myScheduledMethod() {
    // your scheduled task logic here
}
1
2
3
4
  1. 检查类扫描路径: 确保被 @Scheduled 注解的方法所在的类被正确地扫描到。通常情况下,添加了 @Component 或其他组件扫描相关的注解的类会被自动扫描并注册为 Spring Bean。

  2. 日志输出: 在你的方法内部添加日志输出,以便在任务执行时能够查看到相关信息和调试信息。

上次更新: 11/26/2024, 10:00:49 PM