SpringBoot(三)原理剖析:IOC原理


  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);
    }
ConfigurableApplicationContext
  • 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();
    }
View Code
  1. context.setEnvironemnt:為容器設置環境,和webApplicationType有關,在本項目中為StandardServletEnvironment;
  2. postProcessApplicationContext:在ApplicationContext中應用任何相關的后期處理,beanNameGenerator默認為空;
  3. applyInitializers:初始化context容器
  4. listeners.contextPrepared:將事件廣播器注冊到Spring容器中;
  5. logStartupInfo:記錄啟動信息:[Starting App on ... with PID 2536...] ;
  6. logStartupProfileInfo:記錄活動配置文件信息: [No active profile set, falling back to default profiles: default];
  7. beanFactory.registerSingleton:往容器中注冊指定接口實現類的單例;
  8. getAllSources:啟動類信息;
  9. load:從上面獲取的啟動類注冊到ApplicationContext的BeanFactory中;
  10. 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();
            }
        }
    }
refresh
  1. prepareRefresh:設置容器的狀態 、初始化屬性設置(應用監聽器)、檢查必備屬性是否存在;
  2. obtainFreshBeanFactory:設置beanFactory序列化id、獲取beanFactory;
  3. prepareBeanFactory:主要是對beanFactory做一些配置包括各種類加載器和需要忽略的依賴;
  4. postProcessBeanFactory:允許上下文子類對bean工廠進行后置處理;
  5. invokeBeanFactoryPostProcessors:調用BeanDefinitionRegistryPostProcessor實現向容器內添加bean的定義;
  6. registerBeanPostProcessors:注冊后置處理器,用於攔截bean的創建,AOP原理的的核心方法AspectJAutoProxyRegistrar.registerBeanDefinitions在此執行
  7. initMessageSource:初始化國際化相關屬性;
  8. initApplicationEventMulticaster:初始化事件廣播器;
  9. onRefresh:該方法是一個空實現,是留給子類實現的,主要內容是創建web容器;
  10. registerListeners:添加容器內事件監聽器至事件廣播器中;
  11. finishBeanFactoryInitialization:初始化所有剩下的單例bean;
  12. 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);
    }

}
SpringContextUtil
@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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM