SpringBoot 项目中都有主启动类用来启动项目
@SpringBootApplication
注解@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
主类作用
initializers
属性listeners
属性main()
方法定义类,找到运行主类Spring Boot 自动装配流程
package li_maven;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 主类,启动项目;包括 Tomcat 服务器
* @SpringBootApplication 注解中包含多个注解
* 可以将本类当作配置文件使用。 使用 @Bean 注解声明对象并放入容器
* 可以自动配置对象并放到容器:例如 MyBatis 需要的对象
* 扫描注解创建对象:默认扫描范围 此注解所在类所在的包及子包
*/
@SpringBootApplication
public class SpringBootDemoApplication {
/**
* 执行程序启动:执行 SpringApplication.run() 方法
* 方法内传参:本类 Class 对象,args
* 固定执行写法
*/
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
@Configuration
注解
@Bean
声明对象注入到容器@Component
注解
启用自动配置
复合注解,包含以下注解:实现自动装配
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
加载自动配置类的时候,并非将 spring.factories
的配置全部加载进来
@Conditional
等注解的判断进行动态加载包含 @AutoConfigurationPackage
:自动导入配置包
包含 @Import({AutoConfigurationPackages.Registrar.class})
@Import
为 spring 的注解:导入一个配置文件
AutoConfigurationPackages.class
的内部类 Registrar.class
执行逻辑来决定如何导入Registrar
实现了 ImportBeanDefinitionRegistrar
类
@Import
导入到 spring 容器@Import({AutoConfigurationImportSelector.class})
@Import
支持导入的三种方式
@Configuration
注解的配置类ImportSelector
的实现ImportBeanDefinitionRegistrar
的实现自动装配在 AutoConfigurationImportSelector
类中进行以下流程
selectImports(AnnotationMetadata annotationMetadata)
:选择需要导入的组件
getAutoConfigurationEntry()
方法getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)
AnnotationMetadata
返回 AutoConfigurationImportSelector.AutoConfigurationEntry
getCandidateConfigurations()
方法找到所有候选的配置类getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)
找到所有候选的配置类
有自动配置,不需要手写配置的值;配置类有默认值
调用 SpringFactoriesLoader.loadFactoryNames()
方法找到需要配置的组件
断言判断调用方法返回的结果
loadFactoryNames()
方法找到自动的配置类返回才不会报错loadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader)
传入参数:this.getSpringFactoriesLoaderFactoryClass()
返回值:EnableAutoConfiguration.class
@SpringBootApplication
注解下标识的同一个注解即获取一个能加载自动配置类的类
EnableAutoConfiguration
this.getBeanClassLoader()
this.beanClassLoader
String factoryTypeName = factoryType.getName();
factoryType
是 EnableAutoConfiguration.class
ClassLoader classLoaderToUse = classLoader
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader()
返回值:调用 loadSpringFactories()
转为 List 集合返回
getOrDefault(factoryTypeName, Collections.emptyList())
factoryTypeName
值是 EnableAutoConfiguration
META-INF/spring.factories
文件下key
为 org.....EnableAutoConfiguration
得值对应的 valueloadSpringFactories(ClassLoader classLoader)
:返回 Map 集合
Enumeration urls = classLoader.getResources("META-INF/spring.factories")
META-INF/spring.factories
META-INF/spring.factories
文件META-INF/spring.factories
文件SpringFactoriesLoader
工厂加载机制
META-INF/spring.factories
文件
,
分隔的实现类SpringFactoriesLoader
实现相应的实现类注入 Spirng 容器classpath
路径下的 META-INF/spring.factories
文件
SpringBoot 启动的时候加载主配置类
@EnableAutoConfiguration
注解开启自动配置功能@EnableAutoConfiguration
作用
EnableAutoConfigurationImportSelector
给容器中导入一些组件selectImports()
方法的内容
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
SpringFactoriesLoader.loadFactoryNames()
META-INF/spring.factories
properties
对象properties
中获取 EnableAutoConfiguration.class
类(类名)对应的值META-INF/spring.factories
里面配置的所有 EnableAutoConfiguration
的值加入到了容器xxxAutoConfiguration
类都是容器中的一个组件,都加入到容器中,用他们来做自动配置对每一个自动配置类进行自动配置功能
HttpEncodingAutoConfiguration
为例解释自动配置原理
xxxxProperties
类中封装
@Configuration // 表示这是一个配置类,和配置文件一样,也可以给容器中添加组件
/*
启动指定类的 ConfigurationProperties 功能;
将配置文件中对应的值和 HttpEncodingProperties 绑定起来;
并把 HttpEncodingProperties 加入到ioc容器中
*/
@EnableConfigurationProperties(HttpEncodingProperties.class)
/*
Spring 底层 @Conditional 注解(Spring注解版),
如果满足指定的条件,整个配置类里面的配置就会生效;
判断当前应用是否是 web 应用,如果是,当前配置类生效
*/
@ConditionalOnWebApplication
// 判断当前项目有没有 CharacterEncodingFilter:SpringMVC中进行乱码解决的过滤器;
@ConditionalOnClass(CharacterEncodingFilter.class)
/*
判断配置文件中是否存在配置:spring.http.encoding.enabled,如果不存在,判断也成立
即使配置文件中不配置 pring.http.encoding.enabled=true,也默认生效的
*/
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
// 和 SpringBoot 的配置文件映射
private final HttpEncodingProperties properties;
// 只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}
@Bean // 给容器中添加一个组件,组件的某些值需要从 properties 中获取
@ConditionalOnMissingBean(CharacterEncodingFilter.class) // 判断容器是否有这个组件
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
}
@Conditional
等注解的判断进行动态加载
basePackageClasses
或basePackages
来定义要扫描的特定包initializers
属性listeners
属性手动获取容器对象
SpringApplication.run()
方法返回 Spring 容器对象
ConfigurableApplicationContext
ApplicationContext
接口再获取业务 bean 进行调用
//持久层类,注解创建对象放到容器中
@Repository
class Demo{
//类定义略
}
//主类,启动时扫描同级、下级目录注解对象放到容器
@SpringBootApplication
public class DemoApplication{
public static void main(String[] args){
//获取容器对象
ApplicationContext context = SpringApplication.run( DemoApplication.class, args);
//从容器中的得到 bean 对象
Demo demo = (Demo)context.getBean("demo");
System.out.println(demo);
}
}
@PostConstruct
CommandLineRunner
接口ApplicationRunner
接口ApplicationListener
接口@PostConstruct
始终最先执行ApplicationStartedEvent
事件ApplicationRuner
默认优先执行CommandLineRunner
默认后执行
@Order
则按数字大小执行ApplicationReadyEvent
事件@Autowired
@PostConstruct
@PostConstruce
方法,之后再初始化下一个 Bean@PostConstruct
方法中逻辑处理时间过长时会增加 SpringBoot 初始化 Bean 时间,增加应用启动时间
SpringBoot 提供两个接口实现这种需求,容器启动成功后的最后一步回调
CommandLineRunner
ApplicationRunner
@Order
注解指定优先级
Ordered
接口控制执行顺序示例
package com.example.demo2;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 实现 CommandLineRunner 接口,Order 设置为 1 使优先级最高
*/
@SpringBootApplication
@Order(value = 1)
public class Demo2Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Demo2Application.class, args);
}
// 覆写接口的 run 方法,在容器创建后自动执行
@Override
public void run(String... args) throws Exception {
}
}
package li_maven.springboot;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 实现 ApplicationRunner 接口,Order 设置为 2 优先级次高
*/
@Order(value = 2)
@SpringBootApplication
public class Application implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("启动后自动执行");
}
}
和另两个接口实现一样都不影响服务,可以正常提供服务
监听事件通常是 ApplicationStartedEvent
或 ApplicationReadyEnvent
使用
package li_maven.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
@SpringBootApplication
public class Application implements ApplicationListener {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
}
}