zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    spring框架学习(五)整合JDBCTemplate
    spring框架学习(四)AOP思想
    spring框架学习(三)spring与junit整合测试
    spring框架学习(二)使用注解代替xml配置
    Java SpringMVC(1)搭建项目
    spring框架学习(一)入门
    Java MyBatis3(2) Mapper代理的开发方式
    Java MyBatis3(1)入门
    .NET LINQ 查询
    vim之vimrc配置文件
  • 原文地址:https://www.cnblogs.com/ryjJava/p/14438824.html
Copyright © 2011-2022 走看看