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

 

posted @ 2021-02-24 23:32  鄙人取个名字好难  阅读(2469)  评论(0编辑  收藏  举报