springboot一般通過以下main方法來啟動項目
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
查看源碼發現加載的主要邏輯寫在了
ConfigurableApplicationContext
org.
springframework.
boot.
SpringApplication.run(
String... args)這個方法里面
具體邏輯如下:
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment);
//1.創建容器 context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context);
//2.把bean裝載進容器context中去 prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } listeners.started(context); 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; }
下面重點講"把bean裝載進容器context中去"的這個方法prepareContext(context, environment, listeners, applicationArguments, printedBanner)的邏輯
方法的代碼實現邏輯如下:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // Load the sources 也就要加載的聲明配置的類,這里是聲明了@SpringBootApplication的DemoApplication Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty");
//把聲明了@SpringBootApplication的類加載進容器的具體實現 load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); }
load方法的具體實現:
/** * Load beans into the application context. * @param context the context to load beans into * @param sources the sources to load */ protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug( "Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); }
//這個BeanDefinationLoader的實現類為org.springframework.boot.BeanDefinitionLoader,為springboot自定義的一個beanDefination的加載器,專門用來加載配置聲明的類的
BeanDefinitionLoader loader = createBeanDefinitionLoader( getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); }
//org.springframework.boot.BeanDefinitionLoader把beanDefination加載進spring的BeanDefinationRegistry中的方法
loader.load();
}
以下為org.springframework.boot.BeanDefinitionLoader的loan方法實現邏輯:
private int load(Class<?> source) { if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) { // Any GroovyLoaders added in beans{} DSL can contribute beans here GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class); load(loader); } if (isComponent(source)) {
//通過org.springframework.context.annotation.AnnotatedBeanDefinitionReader.AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry)來注冊注解掃描的類定義
this.annotatedReader.register(source); return 1; } return 0; }
org.springframework.context.annotation.AnnotatedBeanDefinitionReader的作用是啥呢?來看看
AnnotatedBeanDefinitionReader類的注釋:
/** * Convenient adapter for programmatic registration of annotated bean classes. * This is an alternative to {@link ClassPathBeanDefinitionScanner}, applying * the same resolution of annotations but for explicitly registered classes only. * * @author Juergen Hoeller * @author Chris Beams * @author Sam Brannen * @author Phillip Webb * @since 3.0 * @see AnnotationConfigApplicationContext#register */ public class AnnotatedBeanDefinitionReader
意思就是:
用於聲明bean類的編程注冊的方便的適配器,
這是ClassPathBeanDefinitionsCanner的替代方法,應用
聲明類解析,但僅限於顯式注冊的類。