用过SpringBoot的都知道,如下代码就是一个最基本的SpringBoot主启动类,通过启动这个类,即可完成SpringBoot的启动装配等功能。
@SpringBootApplicationpublic class SpringBootMainApplication {public static void main(String[] args) {SpringApplication.run(SpringBootMainApplication.class, args);}}
所以,来看看,到底是如何实现的?
首先,可以看到,只有两处特殊的地方
- @SpringBootApplication
- SpringApplication.run(SpringBootMainApplication.class, args);
一、@SpringBootApplication
首先,可以看到
第一个注解 @ComponentScan,进行包扫描(都知道,放在主启动类所在包,以及子包下的组件,都会被扫描进入容器中)
第二个注解 @EnableAutoConfiguration, 看名字就知道,启动启动装配,通过AOP/事务的套路之后,都知道@EnableXXX,会导入一些组件,进而实现一些功能
@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 {}
所以,我们进入@EnableAutoConfiguration中看看,一看,果然如此,导入了一个ImportSelector,在ioc容器启动的时候,会调用其selectImports方法,往容器中添加组件
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {}
AutoConfigurationImportSelector#selectImports
@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}
在 getAutoConfigurationEntry 中,进行关键的操作,
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}AnnotationAttributes attributes = getAttributes(annotationMetadata);// 在这,找寻候选的配置,实际上,读取resources文件夹下的spring.factories文件,进行自动装配List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);configurations = removeDuplicates(configurations);Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = filter(configurations, autoConfigurationMetadata);fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);}// getCandidateConfigurations,寻找的是,spring.factories中的 EnableAutoConfiguration配置protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;}//protected Class<?> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;}
然后,执行完这个之后,自动装配就算完成了,会去读取所有导入的jar包中的spring.factories文件中的EnableAutoConfiguration
二、SpringApplication.run(SpringBootMainApplication.class, args);
run方法有多个重载,最终调用到的run方法如下,分为两步
- 创建SpringApplication对象
执行SpringApplication对象中的run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);}
new SpringApplication(primarySources)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {// 设置资源加载器,默认为null,不管this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");// 把主启动类保存到SpringApplication对象中this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));// 判断当前容器的环境状态,看是否是web环境/普通的Java环境this.webApplicationType = WebApplicationType.deduceFromClasspath();// 加载spring.factories中的 ApplicationContextInitializersetInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 加载spring.facotries中的 ApplicationListenersetListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 找到main方法所在类,没有的话,返回nullthis.mainApplicationClass = deduceMainApplicationClass();}
WebApplicationType.deduceFromClasspath();
static WebApplicationType deduceFromClasspath() {// 判断是否有webflux环境,如果是,返回reactive模式if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {return WebApplicationType.REACTIVE;}// SERVLET_INDICATOR_CLASSES中包含 Servlet/ConfigurableWebApplicationContext两个元素// 如果但凡导入了一个不存在,说明不是servlet环境,那就是一个普通Java环境for (String className : SERVLET_INDICATOR_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return WebApplicationType.NONE;}}// 否则,就是一个web环境,需要创建web版的spring容器return WebApplicationType.SERVLET;}
deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {try {StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();for (StackTraceElement stackTraceElement : stackTrace) {if ("main".equals(stackTraceElement.getMethodName())) {return Class.forName(stackTraceElement.getClassName());}}}catch (ClassNotFoundException ex) {// Swallow and continue}return null;}
run(args)
public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();// 获取spring.factories中配置的 SpringApplicationRunListenersSpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 预准备环境ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);configureIgnoreBeanInfo(environment);// 打印logoBanner printedBanner = printBanner(environment);// 创建ioc容器context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);// 准备上下文prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 刷新上下文refreshContext(context);// 刷新上下文之后的处理工作afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// 发布一个事件,ioc容器启动完成listeners.started(context);// 调用Runner的run方法callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {// 发布一个事件,容器已经正在运行listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;}
prepareEnvironment(listeners, applicationArguments); 预准备环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {// Create and configure the environmentConfigurableEnvironment environment = getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());ConfigurationPropertySources.attach(environment);// 发布容器准备完成的事件listeners.environmentPrepared(environment);// 把environment绑定到SpringApplication对象中bindToSpringApplication(environment);if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());}ConfigurationPropertySources.attach(environment);return environment;}
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {// 根据当前的环境,判断创建哪个ApplicationContextswitch (this.webApplicationType) {case SERVLET:// mvc AnnotationConfigServletWebServerApplicationContextcontextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;case REACTIVE:// webflux AnnotationConfigReactiveWebServerApplicationContextcontextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:// 默认普通Java AnnotationConfigApplicationContextcontextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);}
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {// 设置 environmentcontext.setEnvironment(environment);// 对容器进行后置处理,注册 beanNameGenerator/resourceLoader等postProcessApplicationContext(context);// 调用所有的ApplicationContextInitializer的initialize方法,进行初始化applyInitializers(context);// 发布容器准备完成事件listeners.contextPrepared(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beansConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// Load the sourcesSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");load(context, sources.toArray(new Object[0]));// 发布容器加载完成事件listeners.contextLoaded(context);}
refreshContext(context)
在这调用ioc的刷新方法,执行refresh的12步,其中有一步为onRefresh方法,在mvc环境中,重写了onRefresh方法
private void refreshContext(ConfigurableApplicationContext context) {// 调用ApplicationContext的refresh方法,ioc刷新方法refresh(context);if (this.registerShutdownHook) {try {// 注册hookcontext.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}}
在这,createWebServer,创建tomcat
@Overrideprotected void onRefresh() {super.onRefresh();try {createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}}// createWebServerprivate void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {ServletWebServerFactory factory = getWebServerFactory();//调用Servelet工厂获取WebServer服务this.webServer = factory.getWebServer(getSelfInitializer());}else if (servletContext != null) {try {getSelfInitializer().onStartup(servletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context", ex);}}initPropertySources();}// 以tomcat为例的话,就创建出一个tomcat对象,@Overridepublic 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());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);}
SpringBoot的大致启动流程至此结束
