IOC(Inversion of Control,控制倒轉),意思是對象之間的關系不再由傳統的程序來控制,而是由spring容器來統一控制這些對象創建、協調、銷毀,而對象只需要完成業務邏輯即可。IOC的一個重點是在系統運行中,動態的向某個對象提供它所需要的其他對象。這一點是通過DI(Dependency Injection,依賴注入)來實現的。那么DI是如何實現的呢? Java 1.3之后一個重要特征是反射(reflection),它允許程序在運行的時候動態的生成對象、執行對象的方法、改變對象的屬性,spring就是通過反射來實現注入的。
BeanFactory與ApplicationContext
BeanFactory是Spring里面最底層的接口,包含了各種Bean的定義,Spring 使用 BeanFactory 來實例化、配置和管理 Bean。BeanFactory 只能管理單例(Singleton)Bean 的生命周期。它不能管理原型(prototype,非單例)Bean 的生命周期。這是因為原型 Bean 實例被創建之后便被傳給了客戶端,容器失去了對它們的引用。
ApplicationContext接口作為BeanFactory的派生,除了提供BeanFactory所具有的功能外,還提供了更完整的框架功能:
- 繼承MessageSource,因此支持國際化。
- 統一的資源文件訪問方式。
- 提供在監聽器中注冊bean的事件。
- 同時加載多個配置文件。
- 載入多個(有繼承關系)上下文 ,使得每一個上下文都專注於一個特定的層次,比如應用的web層。
與 BeanFactory 懶加載的方式不同,它是預加載,所以,每一個 bean 都在 ApplicationContext 啟動之后實例化
容器初始化分析
在SpringBoot(一)原理剖析:SpringApplication啟動原理 中,分析SpringBoot啟動流程時,有3個步驟與容器的創建和初始化有關,分別是createApplicationContext()、prepareContext()和refreshContext()。因此着重從這3個方法看SpringBoot如何初始化IOC容器。
creatApplicationContext

protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = 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); }
- this.applicationContextClass由SpringApplicationBuilder類中的contextClass()方法賦值的,默認為空;
- webApplicationType變量是SpringApplication.run()方法調用構造方法時賦值的,本項目中是SERVLET,生成的容器是AnnotationConfigServletWebServerApplicationContext
- 通過BeanUtils工具類將獲取到的容器類轉換成ConfigurableApplicationContext類的實例,返回給應用使用
prepareContext

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); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); } protected void postProcessApplicationContext(ConfigurableApplicationContext context) { if (this.beanNameGenerator != null) { context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator); } if (this.resourceLoader != null) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader()); } } if (this.addConversionService) { context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance()); } } protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } 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); } loader.load(); }
- context.setEnvironemnt:為容器設置環境,和webApplicationType有關,在本項目中為StandardServletEnvironment;
- postProcessApplicationContext:在ApplicationContext中應用任何相關的后期處理,beanNameGenerator默認為空;
- applyInitializers:初始化context容器
- listeners.contextPrepared:將事件廣播器注冊到Spring容器中;
- logStartupInfo:記錄啟動信息:[Starting App on ... with PID 2536...] ;
- logStartupProfileInfo:記錄活動配置文件信息: [No active profile set, falling back to default profiles: default];
- beanFactory.registerSingleton:往容器中注冊指定接口實現類的單例;
- getAllSources:啟動類信息;
- load:從上面獲取的啟動類注冊到ApplicationContext的BeanFactory中;
- listeners.contextLoaded:廣播出ApplicationPreparedEvent事件給相應的監聽器執行
refreshContext
采用了模板方法模式,實際是調用如下AbstractApplicationContext的refresh方法:

public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
- prepareRefresh:設置容器的狀態 、初始化屬性設置(應用監聽器)、檢查必備屬性是否存在;
- obtainFreshBeanFactory:設置beanFactory序列化id、獲取beanFactory;
- prepareBeanFactory:主要是對beanFactory做一些配置包括各種類加載器和需要忽略的依賴;
- postProcessBeanFactory:允許上下文子類對bean工廠進行后置處理;
- invokeBeanFactoryPostProcessors:調用BeanDefinitionRegistryPostProcessor實現向容器內添加bean的定義;
- registerBeanPostProcessors:注冊后置處理器,用於攔截bean的創建,AOP原理的的核心方法AspectJAutoProxyRegistrar.registerBeanDefinitions在此執行;
- initMessageSource:初始化國際化相關屬性;
- initApplicationEventMulticaster:初始化事件廣播器;
- onRefresh:該方法是一個空實現,是留給子類實現的,主要內容是創建web容器;
- registerListeners:添加容器內事件監聽器至事件廣播器中;
- finishBeanFactoryInitialization:初始化所有剩下的單例bean;
- finishRefresh:清空緩存、初始化生命周期處理器、調用化生命周期處理器onRfresh方法、發布ContextRefreshedEvent事件、JXM相關處理
ApplicationContextAware實例
Spring容器初始化過程中會檢測容器中的所有Bean,如果發現某個Bean實現了ApplicationContextAware接口,Spring容器會在創建該Bean之后,自動調用該Bean的setApplicationContextAware()方法。調用該方法時,會將容器本身作為參數傳給該方法——該方法中的實現部分將Spring傳入的參數(容器本身)賦給該類對象的applicationContext實例變量,因此接下來可以通過該applicationContext實例變量來訪問容器本身。

@Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext = null; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtil.applicationContext = applicationContext; } public static ApplicationContext getApplicationContext() { return applicationContext; } public static Object getBean(String name) throws BeansException { return applicationContext.getBean(name); } public static boolean containsBean(String name) { return applicationContext.containsBean(name); } public static boolean isSingleton(String name) { return applicationContext.isSingleton(name); } }
@SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); System.out.println(SpringContextUtil.containsBean("orangeService")); System.out.println(SpringContextUtil.containsBean("appleService")); } }
運行結果如下:
... 2021-02-24 23:27:04.814 INFO 8588 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2021-02-24 23:27:04.840 INFO 8588 --- [ main] com.ryj.test2021.App : Started App in 7.867 seconds (JVM running for 8.778) false true