1、main启动:
@SpringBootApplication + SpringApplication.run(App.class,args)
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}
spring boot分析@SpringBootApplication注解@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan
1.1、@SpringBootConfiguration定义当前为配置类
1.2、@ComponentScan指定扫描哪些范围
1.3、@EnableAutoConfiguration:@AutoConfigurationPackage + @Import(AutoConfigurationImportSelector.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
1.3.1、@AutoConfigurationPackage:@Import(AutoConfigurationPackages.Registrar.class)
new PackageImports(metadata).getPackageNames()获取主程序包名,所以spring boot默认扫描主程序所在包下所有配置注入到Bean
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
//导入组件
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
1.3.2、 @Import(AutoConfigurationImportSelector.class)
1)、selectImports获取导入组件
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
2)、getAutoConfigurationEntry获取所有需要组件
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
3)、getCandidateConfigurations获取所有要导入的候选组件
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
其中spring boot 2.7.3后已经不建议SpringFactoriesLoader.loadFactoryNames
4)、SPI机制META-INF/spring/%s.imports中的组件
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
Assert.notNull(annotation, "'annotation' must not be null");
ClassLoader classLoaderToUse = decideClassloader(classLoader);
String location = String.format(LOCATION, annotation.getName());
Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
List<String> importCandidates = new ArrayList<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
importCandidates.addAll(readCandidateConfigurations(url));
}
return new ImportCandidates(importCandidates);
}
5)、EnableAutoConfiguration从META-INF/spring.factories移到org.springframework.boot.autoconfigure.AutoConfiguration.imports
分析 SpringApplication类,提供静态方法调用构造方法实现方法调用
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
//实例化META-INF/spring.factories 相应初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//实例化监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
2.1、setInitializers,实例化所有初始器:
事件流程:
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
2.1.1、扩展:增加自定义初始化器
1)、新增自定义初始化类,实现ApplicationContextInitializer类,重写initialize方法
public class MyInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("自定义初始化执行器");
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map proMap = new HashMap<>();
proMap.put("key","myinitializer");
proMap.put("value","sk");
environment.getPropertySources().addLast(new MapPropertySource("MyInitailizer",proMap));
System.out.println("初始化结束,添加参数!");
}
}
2)、SPI配置resources/META-INF/spring.factories增加Initializers定义
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
com.sk.cloud.spring.init.initialner.MyInitializer
3)、调试如下
4)、调用链路:SpringApplication#run()->prepareContext(..)-->applyInitializers(context)-initialize(context)-->回调自定义初始化器
2.2、setListeners,实例化所有监听器
监听机制:
事件流程:
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener
2.2.1、扩展:增加自定义监视器(开始和结束监听器)
1)、创建自定义监听器,实现
public class AppStartingListener implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent event) {
System.out.println("容器启动前");
}
}
public class AppEndListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
System.out.println("容器启动后");
}
}
2)、SPI配置resources/META-INF/spring.factories增加Listeners定义
# Application Listeners
org.springframework.context.ApplicationListener=\
com.sk.cloud.spring.init.listenter.AppEndListener,\
com.sk.cloud.spring.init.listenter.AppStartingListener
3)、调试如下
4)、监听器回调
1)、ApplicationStartingEvent事件回调:SpringApplication#run() --> listeners.starting(..) -->回调
2)、ApplicationStartedEvent事件回调:SpringApplication#run() --> listeners.started(..) --> 回调
2.3、prepareEnvironment:准备Environment环境对象
url:Springboot配置文件加载原理及流程【源码分析】_springboot加载配置文件源码-CSDN博客
2.4、上下文刷新
1)、确定context类型
context = createApplicationContext();
public enum WebApplicationType {
/**
* The application should not run as a web application and should not start an
* embedded web server.
* 不启动内嵌的WebServer,不是运行web application
*/
NONE,
/**
* The application should run as a servlet-based web application and should start an
* embedded servlet web server.
* 启动内嵌的基于servlet的web server
*/
SERVLET,
/**
* The application should run as a reactive web application and should start an
* embedded reactive web server.
* 启动内嵌的reactive web server,这个application是一个reactive web application
*/
REACTIVE;
}
2)、prepareContext :预加载
Bean注册到单例池 --> SPI机制初始化ApplicationContextInitializer列表 --> 广播ApplicationContextInitializedEvent事件 --> 打印启动容器(Starting App ...)--> load(启动类转化为BeanDefinition注册到spring容器的BeanDefinitionMap中) --> listeners.contextLoaded(context)遍历了SpringApplication对象所有的监听器(创建SpringApplication的时候,从META-INF/spring.factories中加载到的ApplicationListener),判断实现ApplicationContextAware接口添加到spring容器的监听列表。最后广播ApplicationPreparedEvent事件添加到spring容器中
2.5、refreshContext
refreshContext(..)-->AbstractApplicationContext#refresh
public void refresh() throws BeansException, IllegalStateException {
//上下文刷新前的准备工作:设置启动日期、context当前状态、初始化属性和环境
prepareRefresh();
//获取bean工厂类
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//配置beanFactory一些特性,例如上下类加载器和后处理器
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 执行beanFactory后置处理器,可以自定义BeanFactoryPostProcessor修改Bean属性值
//ConfigurationClassParser#doProcessConfigurationClass 解析所有beanDefinition(配置类、bean类)
invokeBeanFactoryPostProcessors(beanFactory);
//注册bean的PostProcessor,用于后续bean的创建和拦截:自定义BeanPostProcessor实现postProcessBeforeInitialization和postProcessAfterInitialization拦击Bean处理。例如AOP
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// 初始化广播器,用于发布事件
initApplicationEventMulticaster();
// 初始话context上下文特殊bean,例如tomcat容器
onRefresh();
// Check for listener beans and register them.
registerListeners();
// 实例化bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
}
以ServletWebServerFactoryAutoConfiguration配置类为例,解释一下全局配置文件中的属性如何生效,比如:server.port=8081,是如何生效的(当然不配置也会有默认值,这个默认值来自于org.apache.catalina.startup.Tomcat)
在ServletWebServerFactoryAutoConfiguration类上,有一个@EnableConfigurationProperties注解:开启配置属性
server.port等,通过@ConfigurationProperties注解,绑定到对应的XxxxProperties配置实体类上封装为一个bean,然后再通过@EnableConfigurationProperties注解导入到Spring容器中。
Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。
2.1.2、启动内置tomcat
refreshContext(..)-->AbstractApplicationContext#refresh -->onRefresh() -->ServletWebServerApplicationContext#onRefresh-->createWebServer()-->getWebServerFactory()-->factory.getWebServer(..) -->getTomcatWebServer()-->initialize
1)、getWebServerFactory()
默认springboot使用tomacat容器
自动装配获取:spring-boot-autoconfigure的SPI文件中(这里注意spring boot2.7官方不推荐使用spring.factories了,EnableAutoConfiguration采用 META-INF/spring/%s.imports)
通过Import导入EmbeddedTomcat、EmbeddedJetty、EmbeddedUndertow
@AutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
分析EmbeddedTomcat,条件装配存在Tomcat类装配TomcatServletWebServerFactory
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
pom文件只要依赖spring-boot-start-tomacat会将Tomcat类引入到项目中,满足EmbeddedTomcat条件装配条件,向容器注入TomcatServletWebServerFactory,springboot容器启动getWebServerFactory会获取TomcatServletWebServerFactory创建Tomcat web容器
2)、Tomacat实现类:new Tocmat并完成了一些初始化配置 --> getTomcatWebServer(创建webserver实例)并启动tomcat容器(this.tomcat.start())
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
for (LifecycleListener listener : this.serverLifecycleListeners) {
tomcat.getServer().addLifecycleListener(listener);
}
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);
}
1)、默认SpringBootBanner
printBanner(env)->bannerPrinter.print->SpringBootBanner#printBanner
class SpringBootBanner implements Banner {
private static final String[] BANNER = { "", " . ____ _ __ _ _",
" /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
" \\\\/ ___)| |_)| | | | | || (_| | ) ) ) )", " ' |____| .__|_| |_|_| |_\\__, | / / / /",
" =========|_|==============|___/=/_/_/_/" };
private static final String SPRING_BOOT = " :: Spring Boot :: ";
private static final int STRAP_LINE_SIZE = 42;
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
for (String line : BANNER) {
printStream.println(line);
}
String version = SpringBootVersion.getVersion();
version = (version != null) ? " (v" + version + ")" : "";
StringBuilder padding = new StringBuilder();
while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
padding.append(" ");
}
printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),
AnsiStyle.FAINT, version));
printStream.println();
}
}
2)、自定义bannner打印
在Resources目录下新增banner.txt文件,自定义打印banner效果
2.8、Runners运行器
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
(runner).run(args);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
(runner).run(args.getSourceArgs());
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
调用ApplicationRunner和CommandLineRunner类执行run方法(order相同ApplicationRunner优先级更高),一般需要在初始化完毕的时候执行,例如加载数据库的配置信息
1)、自定义ApplicationRunner
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("my application runner: init dbserver application");
}
}