最棒 Spring Boot 干貨總結(jié) !3萬字長文 ,收藏!
來源:33h.co/eLt2
說明:前面有4個小節(jié)關(guān)于Spring的基礎(chǔ)知識
分別是:IOC容器、JavaConfig、事件監(jiān)聽、SpringFactoriesLoader詳解
它們占據(jù)了本文的大部分內(nèi)容:
雖然它們之間可能沒有太多的聯(lián)系,但這些知識對于理解Spring Boot的核心原理至關(guān)重要,如果你對Spring框架爛熟于心,完全可以跳過這4個小節(jié)。正是因為這個系列的文章是由這些看似不相關(guān)的知識點組成,因此取名知識清單。
在過去兩三年的Spring生態(tài)圈,最讓人興奮的莫過于Spring Boot框架。或許從命名上就能看出這個框架的設(shè)計初衷:快速的啟動Spring應用。因而Spring Boot應用本質(zhì)上就是一個基于Spring框架的應用,它是Spring對“約定優(yōu)先于配置”理念的最佳實踐產(chǎn)物,它能夠幫助開發(fā)者更快速高效地構(gòu)建基于Spring生態(tài)圈的應用。
那Spring Boot有何魔法?
自動配置、起步依賴、Actuator、命令行界面(CLI) 是Spring Boot最重要的4大核心特性,其中CLI是Spring Boot的可選特性,雖然它功能強大,但也引入了一套不太常規(guī)的開發(fā)模型,因而這個系列的文章僅關(guān)注其它3種特性。如文章標題,本文是這個系列的第一部分,將為你打開Spring Boot的大門,重點為你剖析其啟動流程以及自動配置實現(xiàn)原理。要掌握這部分核心內(nèi)容,理解一些Spring框架的基礎(chǔ)知識,將會讓你事半功倍。
一、拋磚引玉:探索Spring IoC容器
如果有看過SpringApplication.run()方法的源碼,Spring Boot冗長無比的啟動流程一定
會讓你抓狂,透過現(xiàn)象看本質(zhì).
SpringApplication只是將一個典型的Spring應用的啟動流程進行了擴展,因此,透徹理解,?Spring容器是打開Spring Boot大門的一把鑰匙。
1.1、Spring IoC容器
可以把Spring IoC容器比作一間餐館,當你來到餐館,通常會直接招呼服務(wù)員:點菜!至于菜的原料是什么?如何用原料把菜做出來?可能你根本就不關(guān)心。
IoC容器也是一樣,你只需要告訴它需要某個bean,它就把對應的實例(instance)扔給你,至于這個bean是否依賴其他組件,怎樣完成它的初始化,根本就不需要你關(guān)心。
作為餐館,想要做出菜肴,得知道菜的原料和菜譜,同樣地,IoC容器想要管理各個業(yè)務(wù)對象以及它們之間的依賴關(guān)系,需要通過某種途徑來記錄和管理這些信息。
BeanDefinition對象就承擔了這個責任:容器中的每一個bean都會有一個對應的BeanDefinition實例,該實例負責保存bean對象的所有必要信息,包括bean對象的class類型、是否是抽象類、構(gòu)造方法和參數(shù)、其它屬性等等。
當客戶端向容器請求相應對象時,容器就會通過這些信息為客戶端返回一個完整可用的bean實例。
原材料已經(jīng)準備好(把BeanDefinition看著原料),開始做菜吧,等等,你還需要一份菜譜,BeanDefinitionRegistry和BeanFactory就是這份菜譜,BeanDefinitionRegistry抽象出bean的注冊邏輯,而BeanFactory則抽象出了bean的管理邏輯,而各個BeanFactory的實現(xiàn)類就具體承擔了bean的注冊以及管理工作。
它們之間的關(guān)系就如下圖:

BeanFactory、BeanDefinitionRegistry關(guān)系圖(來自:Spring揭秘)
DefaultListableBeanFactory作為一個比較通用的BeanFactory實現(xiàn),它同時也實現(xiàn)了BeanDefinitionRegistry接口,因此它就承擔了Bean的注冊管理工作。從圖中也可以看出,BeanFactory接口中主要包含getBean、containBean、getType、getAliases等管理bean的方法,而BeanDefinitionRegistry接口則包含registerBeanDefinition、removeBeanDefinition、getBeanDefinition等注冊管理BeanDefinition的方法。
下面通過一段簡單的代碼來模擬BeanFactory底層是如何工作的:
// 默認容器實現(xiàn)
?DefaultListableBeanFactory beanRegistry = new?DefaultListableBeanFactory();
?// 根據(jù)業(yè)務(wù)對象構(gòu)造相應的BeanDefinition
?AbstractBeanDefinition definition = new?RootBeanDefinition(Business.class,true);
?// 將bean定義注冊到容器中
?beanRegistry.registerBeanDefinition("beanName",definition);
?// 如果有多個bean,還可以指定各個bean之間的依賴關(guān)系
?// ........
??// 然后可以從容器中獲取這個bean的實例
?// 注意:這里的beanRegistry其實實現(xiàn)了BeanFactory接口,所以可以強轉(zhuǎn),
?// 單純的BeanDefinitionRegistry是無法強制轉(zhuǎn)換到BeanFactory類型的
?BeanFactory container = (BeanFactory)beanRegistry;
?Business business = (Business)container.getBean("beanName");// 通常為BeanDefinitionRegistry的實現(xiàn)類,這里以DeFaultListabeBeanFactory為例
?BeanDefinitionRegistry beanRegistry = new?DefaultListableBeanFactory();
??// XmlBeanDefinitionReader實現(xiàn)了BeanDefinitionReader接口,用于解析XML文件
?XmlBeanDefinitionReader beanDefinitionReader = new?XmlBeanDefinitionReaderImpl(beanRegistry);
?// 加載配置文件 beanDefinitionReader.loadBeanDefinitions("classpath:spring-bean.xml");
??// 從容器中獲取bean實例
?BeanFactory container = (BeanFactory)beanRegistry;
?Business business = (Business)container.getBean("beanName");// 通常為BeanDefinitionRegistry的實現(xiàn)類,這里以DeFaultListabeBeanFactory為例
?BeanDefinitionRegistry beanRegistry = new?DefaultListableBeanFactory();
??// XmlBeanDefinitionReader實現(xiàn)了BeanDefinitionReader接口,用于解析XML文件
?XmlBeanDefinitionReader beanDefinitionReader = new?XmlBeanDefinitionReaderImpl(beanRegistry);
?// 加載配置文件 beanDefinitionReader.loadBeanDefinitions("classpath:spring-bean.xml");
??// 從容器中獲取bean實例
?BeanFactory container = (BeanFactory)beanRegistry;
?Business business = (Business)container.getBean("beanName");
// 代碼來自:org.springframework.context.support.ApplicationContextAwareProcessor
?// 其postProcessBeforeInitialization方法調(diào)用了invokeAwareInterfaces方法
?private?void?invokeAwareInterfaces(Object?bean){
?????if?(bean instanceof?EnvironmentAware){
?????????((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
?????}
?????if?(bean instanceof?ApplicationContextAware){
?????????((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
?????}
?????// ......
}"bookService" ?class="cn.moondev.service.BookServiceImpl">bean>@Configuration
?public?class
?MoonBookConfiguration
?{
??????// 任何標志了@Bean的方法,其返回值將作為一個bean注冊到Spring的IOC容器中
?????// 方法名默認成為該bean定義的id
?????@Bean
?????public?BookService bookService()?{
?????????return?new?BookServiceImpl();
?????}
?}"bookService" ?class="cn.moondev.service.BookServiceImpl">
?????<property?name="dependencyService"?ref="dependencyService"/>
?bean>
??"otherService"?class="cn.moondev.service.OtherServiceImpl">
?????<property?name="dependencyService"?ref="dependencyService"/>
?bean>
??"dependencyService"?class="DependencyServiceImpl"/> @Configuration
?public?class?MoonBookConfiguration?{
??????// 如果一個bean依賴另一個bean,則直接調(diào)用對應JavaConfig類中依賴bean的創(chuàng)建方法即可
?????// 這里直接調(diào)用dependencyService()
?????@Bean
?????public?BookService bookService()?{
?????????return?new?BookServiceImpl(dependencyService());
?????}
??????@Bean
?????public?OtherService otherService()?{
?????????return?new?OtherServiceImpl(dependencyService());
?????}
??????@Bean
?????public?DependencyService dependencyService()?{
?????????return?new?DependencyServiceImpl();
?????}
?}@Configuration
?public?class?MoonBookConfiguration{
?????@Bean
?????public?BookService bookService()?{
?????????return?new?BookServiceImpl();
?????}
?}@Configuration
?// 可以同時導入多個配置類,比如:@Import({A.class,B.class})
?@Import(MoonBookConfiguration.class)
?public class MoonUserConfiguration
?{
?????@Bean
?????public UserService userService(BookService bookService) {
?????????return?new?BookServiceImpl(bookService);
?????}
?}
public?class?JdbcTemplateCondition?implements?Condition?{
??????@Override
?????public?boolean?matches(ConditionContext
?conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata)?{
?????????try?{
?????????conditionContext.getClassLoader().loadClass("org.springframework.jdbc.core.JdbcTemplate");
?????????????return?true;
?????????}
?catch?(ClassNotFoundException e) {
?????????????e.printStackTrace();
?????????}
?????????return?false;
?????}
?}@Conditional(JdbcTemplateCondition.class) @Service?public MyService service() { ...... }
// jdbc config
?jdbc.mysql.url=jdbc:mysql://localhost:3306/sampledb
?jdbc.mysql.username=root
?jdbc.mysql.password=123456
?......
??// 配置數(shù)據(jù)源
?@Configuration
?public?class?HikariDataSourceConfiguration {
??????@Value("jdbc.mysql.url")
?????public?String?url;
?????@Value("jdbc.mysql.username")
?????public?String?user;
?????@Value("jdbc.mysql.password")
?????public?String?password;
??????????@Bean
?????public?HikariDataSource dataSource() {
?????????HikariConfig hikariConfig = new?HikariConfig();
?????????hikariConfig.setJdbcUrl(url);
?????????hikariConfig.setUsername(user);
?????????hikariConfig.setPassword(password);
?????????// 省略部分代碼
?????????return?new?HikariDataSource(hikariConfig);
?????}
?}@Component
?// 還可以通過@PropertySource("classpath:jdbc.properties")來指定配置文件
?@ConfigurationProperties("jdbc.mysql")
?// 前綴=jdbc.mysql,會在配置文件中尋找jdbc.mysql.*的配置項
?pulic class?JdbcConfig {
?????public?String?url;
?????public?String?username;
?????public?String?password;
?}
??@Configuration
?public?class?HikariDataSourceConfiguration {
??????@AutoWired
?????public?JdbcConfig config;
??????????@Bean
?????public?HikariDataSource dataSource() {
?????????HikariConfig hikariConfig = new?HikariConfig();
?????????hikariConfig.setJdbcUrl(config.url);
?????????hikariConfig.setUsername(config.username);
?????????hikariConfig.setPassword(config.password);
?????????// 省略部分代碼
?????????return?new?HikariDataSource(hikariConfig);
???}
?}#App
?app.menus[0].title=Home
?app.menus[0].name=Home
?app.menus[0].path=/
?app.menus[1].title=Login
?app.menus[1].name=Login
?app.menus[1].path=/login
??app.compiler.timeout=5
?app.compiler.output-folder=/temp/
??app.error=/error/@Component
?@ConfigurationProperties("app")
?public?class?AppProperties {
??????public?String?error;
?????public?Listprotected?Class> loadClass(String?name, boolean?resolve) {
?????synchronized (getClassLoadingLock(name)) {
?????// 首先,檢查該類是否已經(jīng)被加載,如果從JVM緩存中找到該類,則直接返回
?????Class> c = findLoadedClass(name);
?????if?(c == null) {
?????????try?{
?????????????// 遵循雙親委派的模型,首先會通過遞歸從父加載器開始找,
?????????????// 直到父類加載器是BootstrapClassLoader為止
?????????????if?(parent != null) {
?????????????????c = parent.loadClass(name, false);
?????????????} else?{
?????????????????c = findBootstrapClassOrNull(name);
?????????????}
????????} catch?(ClassNotFoundException e) {}
?????????if?(c == null) {
?????????????// 如果還找不到,嘗試通過findClass方法去尋找
?????????????// findClass是留給開發(fā)者自己實現(xiàn)的,也就是說
?????????????// 自定義類加載器時,重寫此方法即可
????????????c = findClass(name);
?????????}
?????}
?????if?(resolve) {
????????resolveClass(c);
?????}
?????return?c;
?????}
?}
for?(String?aDriver : driversList) {
?????try?{
?????????// 直接使用AppClassLoader
?????????Class.forName(aDriver, true, ClassLoader.getSystemClassLoader());
?????} catch?(Exception ex) {
?????????println("DriverManager.Initialize: load failed: "?+ ex);
??????}
???}Thread.currentThread().getClassLoader()Thread.currentThread().getContextClassLoader()public Enumeration<URL>?getResources(String name) throws IOException {
?????Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration>[2];
?????if?(parent?!= null) {
?????????tmp[0] = parent.getResources(name);
?????} else?{
?????????tmp[0] = getBootstrapResources(name);
?????}
?????tmp[1] = findResources(name);
?????return?new?CompoundEnumeration<>(tmp);
?}// 尋找Array.class文件
?public?static?void?main(String[] args) throws Exception{
?????// Array.class的完整路徑
?????String name = "java/sql/Array.class";
?????Enumeration urls = Thread.currentThread().getContextClassLoader().getResources(name);
?????while?(urls.hasMoreElements()) {
?????????URL url = urls.nextElement();
?????????System.out.println(url.toString());
?????}
?} $JAVA_HOME/jre/lib/rt.jar!/java/sql/Array.classpublic?static?final String?FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
?// spring.factories文件的格式為:key=value1,value2,value3
?// 從所有的jar包中找到META-INF/spring.factories文件
?// 然后從文件中解析出key=factoryClass類名稱的所有value值
?public?static?List<String> loadFactoryNames(Class> factoryClass, ClassLoader classLoader) {
?????String?factoryClassName = factoryClass.getName();
?????// 取得資源文件的URL
?????Enumeration urls = (classLoader != null?? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
?????List<String> result = new?ArrayList<String>();
?????// 遍歷所有的URL
?????while?(urls.hasMoreElements()) {
?????????URL url = urls.nextElement();
?????????// 根據(jù)資源文件URL解析properties文件
?????????Properties properties = PropertiesLoaderUtils.loadProperties(new?UrlResource(url));
?????????String?factoryClassNames = properties.getProperty(factoryClassName);
?????????// 組裝數(shù)據(jù),并返回
?????????result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
?????}
?????return?result;
?} // 來自 org.springframework.boot.autoconfigure下的META-INF/spring.factories
?// EnableAutoConfiguration后文會講到,它用于開啟Spring Boot自動配置功能
?org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
?org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
?org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
?org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration\public?class?MethodMonitorEvent?extends?EventObject?{
?????// 時間戳,用于記錄方法開始執(zhí)行的時間
?????public?long?timestamp;
??????public?MethodMonitorEvent(Object source)?{
?????????super(source);
?????}
?}// 1、定義事件監(jiān)聽接口
?public?interface?MethodMonitorEventListener?extends?EventListener?{
?????// 處理方法執(zhí)行之前發(fā)布的事件
?????public?void?onMethodBegin(MethodMonitorEvent event);
?????// 處理方法結(jié)束時發(fā)布的事件
?????public?void?onMethodEnd(MethodMonitorEvent event);
?}
?// 2、事件監(jiān)聽接口的實現(xiàn):如何處理
?public?class?AbstractMethodMonitorEventListener?implements?MethodMonitorEventListener?{
??????@Override
?????public?void?onMethodBegin(MethodMonitorEvent event) {
?????????// 記錄方法開始執(zhí)行時的時間
?????????event.timestamp = System.currentTimeMillis();
?????}
??????@Override
?????public?void?onMethodEnd(MethodMonitorEvent event) {
?????????// 計算方法耗時
?????????long?duration = System.currentTimeMillis() - event.timestamp;
?????????System.out.println("耗時:"?+ duration);
?????}
?}public?class?MethodMonitorEventPublisher?{
??????private?List listeners = new?ArrayList();
??????public?void?methodMonitor() {
?????????MethodMonitorEvent eventObject = new?MethodMonitorEvent(this);
?????????publishEvent("begin",eventObject);
?????????// 模擬方法執(zhí)行:休眠5秒鐘
?????????TimeUnit.SECONDS.sleep(5);
?????????publishEvent("end",eventObject);
??????}
??????private?void?publishEvent(String status,MethodMonitorEvent event) {
?????????// 避免在事件處理期間,監(jiān)聽器被移除,這里為了安全做一個復制操作
?????????List copyListeners = ? new?ArrayList(listeners);
?????????for?(MethodMonitorEventListener listener : copyListeners) {
?????????????if?("begin".equals(status)) {
?????????????????listener.onMethodBegin(event);
?????????????} else?{
?????????????????listener.onMethodEnd(event);
?????????????}
?????????}
?????}
?????????public?static?void?main(String[] args) {
?????????MethodMonitorEventPublisher publisher = new?MethodMonitorEventPublisher();
?????????publisher.addEventListener(new?AbstractMethodMonitorEventListener());
?????????publisher.methodMonitor();
?????}
?????// 省略實現(xiàn)
?????public?void?addEventListener(MethodMonitorEventListener listener) {}
?????public?void?removeEventListener(MethodMonitorEventListener listener) {}
?????public?void?removeAllListeners() {} 2. 事件監(jiān)聽器的管理。publisher類中提供了事件監(jiān)聽器的注冊與移除方法,這樣客戶端可以根據(jù)實際情況決定是否需要注冊新的監(jiān)聽器或者移除某個監(jiān)聽器。
@SpringBootApplication
?public?class?MoonApplication?{
??????public?static?void?main(String[] args)?{
?????????SpringApplication.run(MoonApplication.class, args);
?????}
?}@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 {
?????// ......
?}@Target(ElementType.TYPE)
?@Retention(RetentionPolicy.RUNTIME)
?@Documented
?@Inherited
?@AutoConfigurationPackage
?@Import(EnableAutoConfigurationImportSelector.class)
?public @interface?EnableAutoConfiguration {
?????// ......
?}public?String[] selectImports(AnnotationMetadata annotationMetadata) {
?????// 省略了大部分代碼,保留一句核心代碼
?????// 注意:SpringBoot最近版本中,這句代碼被封裝在一個單獨的方法中
?????// SpringFactoriesLoader相關(guān)知識請參考前文
?????List<String> factories = new?ArrayList<String>(new?LinkedHashSet<String>(
???????????SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader)));
?}// 來自 org.springframework.boot.autoconfigure下的META-INF/spring.factories
?// 配置的key = EnableAutoConfiguration,與代碼中一致
?org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
?org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
?org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
?org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration\
?.....@Configuration
?@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
?@EnableConfigurationProperties(DataSourceProperties.class)
?@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })
?public class DataSourceAutoConfiguration {
?}// 提供對datasource配置信息的支持,所有的配置前綴為:spring.datasource
?@ConfigurationProperties(prefix = "spring.datasource")
?public?class?DataSourceProperties {
?????private?ClassLoader classLoader;
?????private?Environment environment;
?????private?String?name = "testdb";
?????......
?}@Configuration
?public class DataSourcePoolMetadataProvidersConfiguration {
??????@Configuration
?????@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
?????static class TomcatDataSourcePoolMetadataProviderConfiguration {
?????????@Bean
?????????public DataSourcePoolMetadataProvider tomcatPoolDataSourceMetadataProvider() {
?????????????.....
?????????}
?????}
???......
?}
private?void?initialize(Object[] sources) {
??????if?(sources != null?&& sources.length > 0) {
??????????this.sources.addAll(Arrays.asList(sources));
??????}
??????// 判斷是否是Web項目
??????this.webEnvironment = deduceWebEnvironment();
??????setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
??????setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
??????// 找到入口類
??????this.mainApplicationClass = deduceMainApplicationClass();
?}org.springframework.context.ApplicationListener=\ cn.moondev.listeners.xxxxListener\關(guān)于SpringApplication的初始化,我們就說這么多。public?ConfigurableApplicationContext run(String... args) {
?????????StopWatch stopWatch = new?StopWatch();
?????????stopWatch.start();
?????????ConfigurableApplicationContext context = null;
?????????FailureAnalyzers analyzers = null;
?????????configureHeadlessProperty();
?????????// ①
?????????SpringApplicationRunListeners listeners = getRunListeners(args);
?????????listeners.starting();
?????????try?{
?????????????// ②
ApplicationArguments applicationArguments = new?DefaultApplicationArguments(args);
?????????????ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
?????????????// ③
?????????????Banner printedBanner = printBanner(environment);
?????????????// ④
?????????????context = createApplicationContext();
?????????????// ⑤
?????????????analyzers = new?FailureAnalyzers(context);
?????????????// ⑥
?????????????prepareContext(context, environment, listeners, applicationArguments,printedBanner);
?????????????// ⑦
??????????????refreshContext(context);
?????????????// ⑧
?????????????afterRefresh(context, applicationArguments);
?????????????// ⑨
?????????????listeners.finished(context, null);
?????????????stopWatch.stop();
?????????????return?context;
????????}
?????????catch?(Throwable ex) {
?????????????handleRunFailure(context, listeners, analyzers, ex);
?????????????throw?new?IllegalStateException(ex);
?????????}
?????}public?interface?SpringApplicationRunListener?{
??????// 運行run方法時立即調(diào)用此方法,可以用戶非常早期的初始化工作
?????void?starting();
??????????// Environment準備好后,并且ApplicationContext創(chuàng)建之前調(diào)用
?????void?environmentPrepared(ConfigurableEnvironment environment);
??????// ApplicationContext創(chuàng)建好后立即調(diào)用
?????void?contextPrepared(ConfigurableApplicationContext context);
??????// ApplicationContext加載完成,在refresh之前調(diào)用
?????void?contextLoaded(ConfigurableApplicationContext context);
??????// 當run方法結(jié)束之前調(diào)用
?????void?finished(ConfigurableApplicationContext
?context, Throwable exception);
??}public?void?starting()?{
?????// 發(fā)布一個ApplicationStartedEvent
?????this.initialMulticaster.multicastEvent(new?ApplicationStartedEvent(this.application, this.args));
?}listeners.environmentPrepared(environment);. ____??????????_????????????__?_?_
??/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
?( ( )\___ | '_?| '_|?| '_ \/ _` |?\ \ \ \
??\\/ ___)| |_)| |?| |?| || (_|?| ) ) ) )
???' |____| .__|_| |_|_|?|_\__, |?/ / /?/
??=========|_|==============|___/=/_/_/_/
?:: Spring Boot :: (v1.5.6.RELEASE)// 摘自refresh()方法中一句代碼
?invokeBeanFactoryPostProcessors(beanFactory);protected?void?invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)?{
?????PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
?????......
?}ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor
?SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
?ConfigFileApplicationListener$PropertySourceOrderingPostProcessorpublic?void?initialize(ConfigurableApplicationContext context)?{
?????context.addBeanFactoryPostProcessor(new?ConfigurationWarningsPostProcessor(getChecks()));
?}如有文章對你有幫助,“在看”和轉(zhuǎn)發(fā)是對我最大的支持!
精通 Spring Boot 系列文(1) 精通 Spring Boot 系列文(2) 精通 Spring Boot 系列文(2) 精通 Spring Boot 系列文(4) 精通 Spring Boot 系列文(5) 精通 Spring Boot 系列文(6) 精通 Spring Boot 系列文(7) 精通 Spring Boot 系列文(8) 精通 Spring Boot 系列文(9) 精通 Spring Boot 系列文(10) 精通 Spring Boot 系列文(11) 精通 Spring Boot 系列文(12) Java后端編程
更多Java推文,關(guān)注公眾號

