一、SpringBoot介绍
- Spring Boot 设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。
- 嵌入的 Tomcat,无需部署 WAR 文件
- Spring Boot 并不是对 Spring 功能上的增强,而是提供了一种快速使用 Spring 的方式
springboot启动流程

二、构建springboot项目
2.1 引入依赖
maven方式
<!-- 继承SpringBoot官方指定的父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
</parent>
<!-- springBoot的启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>gradle方式
buildscript {
ext {
//统一配置springcloud的版本号
set('springCloudVersion', 'Greenwich.SR2')
}
dependencies {
//引用boot的gradle插件
classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.3.RELEASE"
}
}
//控制子项目依赖的jar包的版本号(多模块项目需要)
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}2.2 编写启动类
/*启动器存放的位置。启动器可以和 controller 位于同一个包下,或
* 者位于 controller 的上一级 包中,但是不能放到 controller
* 的平级以及子包下。
*/
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}三、基础知识
3.1 依赖说明
parent
开发SpringBoot程序要继承spring-boot-starter-parent。使用parent可以帮助开发者进行版本的统一管理,parent包含了使用技术的依赖和版本号,但不负责帮助导入坐标,只依赖版本。
starter
starter定义了使用某种技术时对于依赖的固定搭配格式,使用starter可以帮助开发者减少依赖配置,快速配置依赖关系。
引导类
SpringBoot程序启动还是创建了一个Spring容器对象。这个类在SpringBoot程序中是所有功能的入口,称这个类为。
作为一个引导类最典型的特征就是当前类上方声明了一个注解@SpringBootApplication
内嵌tomcat
内嵌tomcat是在
spring-boot-starter-web依赖中引入的,依赖项为:spring-boot-starter-tomcattomcat服务器运行其实是以对象的形式在Spring容器中运行的
更换内置的服务器
SpringBoot提供了3款内置的服务器
tomcat(默认):apache出品,粉丝多,应用面广,负载了若干较重的组件
jetty:更轻量级,负载性能远不及tomcat
undertow:负载性能勉强跑赢tomcat
xml<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> </dependencies>
3.2 程序打包及运行
程序打包
SpringBoot程序不仅将项目的内容进行了打包,还把工程运行需要的jar包也打包,保证可以独立运行。
mvn项目需要添加sptingboot项目打包配置
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>jar包执行
jar包执行根据jar包中的MANIFEST.MF进行执行
Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Implementation-Title: springboot_08_ssmp
Implementation-Version: 0.0.1-SNAPSHOT
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Start-Class: com.itheima.SSMPApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.5.4
Created-By: Maven Jar Plugin 3.2.0
Main-Class: org.springframework.boot.loader.JarLauncher使用java -jar执行此程序包,将执行Main-Class属性配置的类
Main-Class: org.springframework.boot.loader.JarLauncher。org.springframework.boot.loader.JarLauncher类是内部要查找Start-Class属性中配置的类。并执行对应的类
3.3 属性加载优先级
java –jar springboot.jar –-server.port=80 --logging.level.root=debug–-server.port=80 和 --logging.level.root=debug 为临时属性,可以快速修改某些配置。加载优先级要高于配置文件。官方属性加载的优先级:https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
有14种配置方式。第3条Config data说的就是使用配置文件,第11条Command line arguments说的就是使用命令行临时参数。而这14种配置的顺序就是SpringBoot加载配置的顺序。上面的优先级低,下面的优先级高。
临时属性就是在调用main方法时向main方法中传递的args值。
3.4 配置文件种类及加载优先级
springboot支持三种格式的配置文件:properties格式、yml格式、yaml格式
加载优先级(谁生效)
application.properties > application.yml > application.yamlSpringboot程序启动时,会从以下位置加载配置文件:
\1. file:./config/:当前项目下的/config目录下
\2. file:./ :当前项目的根目录
\3. classpath:/config/:classpath的/config目录
\4. classpath:/ :classpath的根目录
加载顺序为上文的排列顺序,高优先级配置的属性会生效
file :config/application.yml > file :application.yml > classpath:config/application.yml > classpath:application.yml3.5 多环境配置
多环境配置文件
yaml方式
yaml支持单文件分组环境和多文件方式
ymlspring: profiles: active: pro # 启动pro 老版 spring: config: activate: on-profile: pro # 最新的格式 --- spring: profiles: pro server: port: 80 --- spring: profiles: dev server: port: 81 --- spring: profiles: test server: port: 82还可定义多个文件,如:application-pro.yaml、application-dev.yaml。文件的命名规则为:application-环境名.yml。
application是主配置文件,其中根据
spring.profiles.active=环境后缀选择引入的配置文件。properties方式
properties支支持多文件方式,同yaml多文件方式一致。
多环境配置分隔
配置还可根据配置信息的类型分隔为不同类型的配置文件,如数据库,redis等,命名为application-类型.yml
导入配置则使用
spring:
profiles:
active: dev # 对应环境
include: devDB,devRedis,devMVC # 引入分离的配置当有其他环境是,还可进行分组,从sptingboot2.4版本开始
spring:
profiles:
active: dev
group:
"dev": devDB,devRedis,devMVC
"pro": proDB,proRedis,proMVC
"test": testDB,testRedis,testMVC多环境开发控制
maven方式
在项目构建时,可根据环境来管理整个工程,选择保留的配置环境。
xml<profiles> <profile> <id>env_dev</id> <properties> <profile.active>dev</profile.active> </properties> <activation> <activeByDefault>true</activeByDefault> <!--默认启动环境--> </activation> </profile> <profile> <id>env_pro</id> <properties> <profile.active>pro</profile.active> </properties> </profile> </profiles>sptingboot读取maven中属性值
yamlspring: profiles: active: @profile.active@启动时选择环境
项目启动时加载的application配置文件,可以修改名称。自定义加载的配置文件的文件名
通过临时属性修改。
sh--spring.config.name=文件名(不包含后缀) --spring.config.location=classpath:全路径+文件名+后缀,多个用,号隔开。3.6 bean属性赋值
@ConfigurationProperties注解是用来为bean绑定属性的。添加到类上是为spring容器管理的当前类的对象绑定属性,添加到方法上是为spring容器管理的当前方法的返回值对象绑定属性,其实本质上都一样。
开发者可以在yml配置文件中以对象的格式添加若干属性,在实体类上添加该注解,即可将配置中的属性值注入到对象中,该实体类需要交由spring管理
为bean属性赋值
java@Component @Data @ConfigurationProperties(prefix = "servers") public class ServerConfig { private String ipAddress; private int port; private long timeout; }为第三方bean加载属性。
java@Bean @ConfigurationProperties(prefix = "datasource") public DruidDataSource datasource(){ DruidDataSource ds = new DruidDataSource(); return ds; }@EnableConfigurationProperties
专门标注使用@ConfigurationProperties注解绑定属性的bean是哪些
java@SpringBootApplication @EnableConfigurationProperties(ServerConfig.class) public class Springboot13ConfigurationApplication { }使用该注解标注后,在bean上不用再声明@Component注解了。
解决使用@ConfigurationProperties注解时,有提示信息的问题
添加依赖坐标
xml<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency>属性注入宽松绑定
在进行匹配时,配置中的名称要去掉中划线和下划线后,忽略大小写的情况下去与java代码中的属性名进行忽略大小写的等值匹配,相同则匹配成功。但如果设置的prefix的为驼峰命名,取配置时会报错。
常用剂量单位绑定
配置中的值,没有单位的概念,因此,查看配置时,可能会存在误解
springboot充分利用了JDK8中提供的全新的用来表示计量单位的新数据类型
java@Component @Data @ConfigurationProperties(prefix = "servers") public class ServerConfig { // 表示时间间隔,可以通过@DurationUnit注解描述时间单位,小时 @DurationUnit(ChronoUnit.HOURS) // 表示存储空间,可以通过@DataSizeUnit注解描述存储空间单位,MB private Duration serverTimeOut; @DataSizeUnit(DataUnit.MEGABYTES) private DataSize dataSize; }
3.6 Condition条件判断
Condition(条件):Condition是在Spring4.0增加的条件判断功能,通 过这个可以功能可以实现选择性的创建Bean操作。
自定义注解实现某个类存在时才加载
自定义注解
import org.springframework.context.annotation.Conditional; import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(ClassCondition.class) public @interface MyConditionalOnClass { String[] value(); }自定义控制加载类
javaimport org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; import java.util.Map; public class ClassCondition implements Condition { /** * * @param conditionContext 上下文对象,获取Bean工厂,获取ClassLoader * @param annotatedTypeMetadata 注解元对象,用于获取注解信息 * @return */ @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { //返回是false时,Bean不加载的 //返回是true时,Bean加载的 Map<String, Object> map = annotatedTypeMetadata.getAnnotationAttributes(MyConditionalOnClass.class.getName()); System.out.println(map); boolean flag = true; try { String[] strings = (String[]) map.get("value"); for (String className : strings) { Class.forName(className); } } catch (ClassNotFoundException e) { flag = false; } return flag; } }使用
java//将User实体类放入Bean //1.如果没有条件,User对象放入Bean里 //2.加入Condition条件 // @Conditional注解里,需要导入Condition实现类 // 加载一个User,创建一个Condition实现类 //3.不应该写死"redis.clients.jedis.Jedis",在Condition注解里导入 //@Conditional(ClassCondition.class) @Bean //@MyConditionalOnClass({"redis.clients.jedis.Jedis"}) @ConditionalOnProperty(name = "itcast", havingValue = "itheima") // 值对应时才加载。 public User user(){ return new User(); }
总结
自定义条件:
自定义条件类:自定义类实现Condition接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回boolean值 。
matches 方法两个参数:
- context:上下文对象,可以获取属性值,获取类加载器,获取FactoryBean等。
- metadata:元数据对象,用于获取注解属性。
判断条件:在初始化Bean时,使用@Conditional(条件类.class)注解。
SpringBoot常用条件注解:
- ConditionalOnProperty:判断配置文件中是否有对应的属性和值才初始化Bean。
- ConditionalOnClass:判断环境中是否有对应的字节码文件才初始化Bean。
- ConditionalOnMissingBean:判断环境中是否有对应的Bean才初始化Bean。
3.7 @Enable*注解
SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某 些功能的。而其底层原理是使用@Import注解导入一些配置类,实现Bean的动 态加载
当@ComponentScan扫描不到加载的类时(扫描范围:当前引导类所在包以其子包 ),可以标注增加扫描包的范围@ComponentScan("com.itheima.config")或使用@Import注解导入配置类。(配置类中加载@Bean)
对import进行封装
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfig.class)
public @interface EnableUser {}
// 在启动类上添加@EnableUser即可加载配置3.8 @Import注解
@Enable*底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供4中用法:
- 导入Bean
- 导入配置类
- 导入ImportSelector实现类,一般用于加载配置文件中的类
- 导入ImportBeanDefifinitionRegistrar实现类
导入Bean
@SpringBootApplication
@Import(User.class)
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
/*
Object user = context.getBean("user");
System.out.println(user);
*/
User user = context.getBean(User.class);
System.out.println(user);
// 如果想要获取Bean的名称,那么可以使用context.getBeansOfType
Map<String, User> map = context.getBeansOfType(User.class);
System.out.println(map);
}
}导入配置类
// UserConfig
@Bean
public Role role(){ return new Role(); }
// 启动类上添加 @Import(UserConfig.class)实现ImportSelector接口
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.itheima.domain.Role", "com.itheima.domain.User"};
}
}
// 修改启动类
@SpringBootApplication
@Import(MyImportSelector.class)
public class SpringbootEnableApplication {}实现ImportBeanDefinitionRegistrar接 口
// 创建一个MyImportBeanDefinitionRegistrar类,实现ImportBeanDefinitionRegistrar接口,重写registerBeanDefinitions方法
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//注册User类
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).get BeanDefinition();
registry.registerBeanDefinition("user", beanDefinition);
//注册Role类
beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Role.class).get BeanDefinition();
registry.registerBeanDefinition("role", beanDefinition);
}
// 修改启动类
@SpringBootApplication
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringbootEnableApplication {}3.9 @EnableAutoConfiguration
@SpringBootApplication中的@EnableAutoConfiguration注解,也是通过@Import({AutoConfigurationImportSelector.class})来实现类的加载,
配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot 应用启动时,会自动加载这些配置类,初始化 Bean。
并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean。
3.10 ImportSelector
ImportSelector接口是Spring导入外部配置的核心接口,在SpringBoot的自动化配置和@EnableXXX(功能性注解)中起到了决定性的作用。当在@Configuration标注的Class上使用@Import引入了一个ImportSelector实现类后,会把实现类中返回的Class名称都定义为bean。
DeferredImportSelector接口继承ImportSelector,他和ImportSelector的区别在于装载bean的时机上,DeferredImportSelector需要等所有的@Configuration都执行完毕后才会进行装载
ImportSelector导入的Bean是用编程的方式动态的载入bean,在ConfifigurationClassParser这个类的processImports方法中处理
springBoot自动装载很大程度上归功于ImportSelector。
SpringBootApplication中声明了一个 @EnableAutoConfiguration。其中通过Import引入了SpringBoot定义的AutoConfigurationImportSelector,最终获取bean的渠道在SpringFactoriesLoader.loadFactoryNames,方法中会获取工程中所有META-INF/spring.factories文件,将其中的键值组合成Map并加载类。
每个jar都可以定义自己的META-INF/spring.factories ,jar被加载的同时 spring.factories里面定义的bean就可以自动被加载
3.11 监听
监听机制
SpringBoot 的监听机制,其实是对Java提供的事件监听机制的封装。
Java中的事件监听机制定义了以下几个角色:
①事件:Event,继承 java.util.EventObject 类的对象
②事件源:Source ,任意对象Object
③监听器:Listener,实现 java.util.EventListener 接口的对象
Springboot监听器
SpringBoot 在项目启动时,会对几个监听器进行回调,我们可以实现这些监听器接口,在项目启动时完成一些操作。
一共有四种实现方法:
- ApplicationContextInitializer
- SpringApplicationRunListener
- CommandLineRunner
- ApplicationRunner
创建Listener模块
ApplicationContextInitializer
@Component
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("ApplicationContextInitializer...ini tialize");
}
}META-INF/spring.factories 中添加配置
org.springframework.context.ApplicationContextInitialize r=com.itheima.listener.listener.MyApplicationContextInit ializerSpringApplicationRunListener
//@Component
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
// 需要有参构造
public MySpringApplicationRunListener(SpringApplication application, String[] args) {}
@Override
public void starting() {
System.out.println("SpringApplicationRunListener...正在 启动");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("SpringApplicationRunListener...环境 准备中");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...上下 文准备");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...上下 文加载");
}
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...已经 启动");
}
@Override
public void running(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...正在 启动中");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("SpringApplicationRunListener...启动 失败");
}
}修改 META-INF/spring.factories 配置文件
org.springframework.boot.SpringApplicationRunListener=co m.itheima.listener.listener.MySpringApplicationRunListenerCommandLineRunner
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("CommandLineRunner...run");
}
}ApplicationRunner
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("ApplicationRunner...run");
}
}ApplicationStartedEvent继承自SpringApplicationEvent
SpringApplicationEvent继承自ApplicationEvent
ApplicationEvent继承自EventObject
证明:Springboot里的监听事件是对java监听事件的一个封装
四、拓展
4.1 自定义Starter
Starter命名规范
| starter所属 | 命名规则 | 示例 |
|---|---|---|
| 官方提供 | spring-boot-starter-技术名称 | spring-boot-starter-web spring-boot-starter-test |
| 第三方提供 | 第三方技术名称-spring-boot-starter | druid-spring-boot-starter |
| 第三方提供 | 第三方技术名称-boot-starter(第三方技术名称过长,简化命名) | mybatis-plus-boot-starter |
定义步骤 以redis为例:
在Starter项目中添加依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>创建RedisAutoConfigure配置类
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;
@Configuration
@ConditionalOnClass(Jedis.class)//加载的时候判断Jedis类存在的时候才加载Bean
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfigure {
@Bean
@ConditionalOnMissingBean(name = "jedis")// 当jedis没有被创建时加载这个类,并写入Bean
public Jedis jedis(RedisProperties redisProperties){
System.out.println("RedisAutoConfigure...jedis");
return new Jedis(redisProperties.getHost(), redisProperties.getPort());
}
}创建redis配置类
import org.springframework.boot.context.properties.ConfigurationProperties;
//spring.redis.host=127.0.0.1
//spring.redis.port=6379
@ConfigurationProperties(prefix = "redis")
public class RedisProperties {
private String host = "localhost";
private int port = 6379;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}在resources下新建META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfigu ration=com.itheima.redis.RedisAutoConfigure