spring4.1.8初始化源码学习三部曲之三:abstractapplicationcontext.refresh方法

程序员欣宸      2022-06-10     494

关键词:

欢迎访问我的GitHub

refresh方法简介

  • 本章来学习refresh方法,具体的源码在AbstractApplicationContext类中,该方法的简介请看下面源码中的注释:

    @Override
    public void refresh() throws BeansException, IllegalStateException 
    //startupShutdownMonitor对象在spring环境刷新和销毁的时候都会用到,确保刷新和销毁不会同时执行
    synchronized (this.startupShutdownMonitor) 
        // 准备工作,例如记录事件,设置标志,检查环境变量等,并有留给子类扩展的位置,用来将属性加入到applicationContext中
        prepareRefresh();
    
        // 创建beanFactory,这个对象作为applicationContext的成员变量,可以被applicationContext拿来用,
        // 并且解析资源(例如xml文件),取得bean的定义,放在beanFactory中
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
        // 对beanFactory做一些设置,例如类加载器、spel解析器、指定bean的某些类型的成员变量对应某些对象等
        prepareBeanFactory(beanFactory);
    
        try 
            // 子类扩展用,可以设置bean的后置处理器(bean在实例化之后这些后置处理器会执行)
            postProcessBeanFactory(beanFactory);
    
            // 执行beanFactory后置处理器(有别于bean后置处理器处理bean实例,beanFactory后置处理器处理bean定义)
            invokeBeanFactoryPostProcessors(beanFactory);
    
            // 将所有的bean的后置处理器排好序,但不会马上用,bean实例化之后会用到
            registerBeanPostProcessors(beanFactory);
    
            // 初始化国际化服务
            initMessageSource();
    
            // 创建事件广播器
            initApplicationEventMulticaster();
    
            // 空方法,留给子类自己实现的,在实例化bean之前做一些ApplicationContext相关的操作
            onRefresh();
    
            // 注册一部分特殊的事件监听器,剩下的只是准备好名字,留待bean实例化完成后再注册
            registerListeners();
    
            // 单例模式的bean的实例化、成员变量注入、初始化等工作都在此完成
            finishBeanFactoryInitialization(beanFactory);
    
            // applicationContext刷新完成后的处理,例如生命周期监听器的回调,广播通知等
            finishRefresh();
        
    
        catch (BeansException ex) 
            logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
    
            // 刷新失败后的处理,主要是将一些保存环境信息的集合做清理
            destroyBeans();
    
            // applicationContext是否已经激活的标志,设置为false
            cancelRefresh(ex);
    
            // Propagate exception to caller.
            throw ex;
        
    
    
  • 接下来逐个分析吧:

prepareRefresh方法

  • prepareRefresh方法的源码如下:

    protected void prepareRefresh() 
    //记录初始化开始时间
    this.startupDate = System.currentTimeMillis();
    //context是否关闭的标志,设置为false
    this.closed.set(false);
    //context是否激活的标志,设置为true
    this.active.set(true);
    
    if (logger.isInfoEnabled()) 
        logger.info("Refreshing " + this);
    
    
    //留给子类实现的空方法
    initPropertySources();
    
    /**
    AbstractPropertyResolver类的requiredProperties是个集合,
    在下面的validateRequiredProperties方法中,都要拿requiredProperties中的元素作为key去检查是否存在对应的环境变量,
    如果不存在就抛出异常
    */
    getEnvironment().validateRequiredProperties();
    
  • 上述代码中,注意以下两处:
    1. initPropertySources是个空方法,是留给子类实现的,以AnnotationConfigWebApplicationContext类为例,就overwrite了initPropertySources方法:
      @Override
      protected void initPropertySources() 
      ConfigurableEnvironment env = getEnvironment();
      if (env instanceof ConfigurableWebEnvironment) 
      ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, this.servletConfig);
      
      
  • 跟踪上面的initPropertySources方法,最终找到了WebApplicationContextUtils.initServletPropertySources:

    public static void initServletPropertySources(
            MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) 
    
        Assert.notNull(propertySources, "propertySources must not be null");
        if (servletContext != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) &&
                propertySources.get(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) 
            propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME,
                    new ServletContextPropertySource(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
        
        if (servletConfig != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) &&
                propertySources.get(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) 
            propertySources.replace(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
                    new ServletConfigPropertySource(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
        
    
  • 上面的代码所做的事情,就是给context增加环境变量数据(数据来自servlet相关的配置信息),这样spring环境就能从context中随时key取得对应的变量了;
  1. getEnvironment().validateRequiredProperties()的作用是用来校验context中是否存在"某些"变量,何谓"某些"?来看validateRequiredProperties方法,追踪到多层调用,最终在AbstractPropertyResolver类的validateRequiredProperties方法中实现:
    @Override
    public void validateRequiredProperties() 
    MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
    for (String key : this.requiredProperties) 
        if (this.getProperty(key) == null) 
            ex.addMissingRequiredProperty(key);
        
    
    if (!ex.getMissingRequiredProperties().isEmpty()) 
        throw ex;
    
    
    • 上述代码显示,如果集合requiredProperties中的name在context中找不到对应的变量,就会抛出异常;
  • 那么问题来了,requiredProperties集合是何时设置的呢?spring-framework中并没有调用,但是官方的单元测试源码给我们了启发,如下图:

  • 如上图红框,如果业务需要确保某些变量在spring环境中必须存在,就可以调用setRequiredProperties方法将变量的name传递进去,这样validateRequiredProperties方法就会做检查了,我们可以基于现有的各种ApplicationContext实现自己定制一个Context类,确保在validateRequiredProperties方法调用之前调用setRequiredProperties方法将变量的name传递进去(例如重写initPropertySources),就能让spring帮我们完成检查了;

obtainFreshBeanFactory()

  • 接下来看ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();得到临时变量beanFactory,先看看ConfigurableListableBeanFactory和BeanFactory的关系:

  • 再看看obtainFreshBeanFactory方法:
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() 
    //由子类创建beanFactory
    refreshBeanFactory();
    //取得子类创建好的beanFactory,作为obtainFreshBeanFactory方法的返回值返回
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) 
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    
    return beanFactory;
    
  • 上述代码中有的refreshBeanFactory需要细看;

refreshBeanFactory方法

//这个方法设置的是这个方法设置的是configResources
public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, ApplicationContext parent)
throws BeansException

super(parent);
Assert.notNull(paths, "Path array must not be null");
Assert.notNull(clazz, "Class argument must not be null");
this.configResources = new Resource[paths.length];
for (int i = 0; i < paths.length; i++) 
    this.configResources[i] = new ClassPathResource(paths[i], clazz);

refresh();

- 因此,到底是configLocations 还是configResources ,和我们使用哪个构造方法来实例化applicationContext对象有关;
- 如果我们实例化applicationContext对象的方式是**new ClassPathXmlApplicationContext("applicationContext.xml")**,那么setConfigLocations方法就会被调用,因此loadBeanDefinitions方法内部,实际执行的代码如下:
```java
String[] configLocations = getConfigLocations();
    if (configLocations != null) 
        reader.loadBeanDefinitions(configLocations);
    
  • 现在可以来看AbstractBeanDefinitionReader类的loadBeanDefinitions(String... locations)方法了:
    public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException 
        Assert.notNull(locations, "Location array must not be null");
        int counter = 0;
        for (String location : locations) 
            counter += loadBeanDefinitions(location);
        
        return counter;
    
  • 展开上面for循环中调用的方法:

    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException 
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) 
            throw new BeanDefinitionStoreException(
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        
    
        if (resourceLoader instanceof ResourcePatternResolver) 
            // Resource pattern matching available.
            try 
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                int loadCount = loadBeanDefinitions(resources);
                if (actualResources != null) 
                    for (Resource resource : resources) 
                        actualResources.add(resource);
                    
                
                if (logger.isDebugEnabled()) 
                    logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                
                return loadCount;
            
            catch (IOException ex) 
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            
        
        else 
            // Can only load single resources by absolute URL.
            Resource resource = resourceLoader.getResource(location);
            int loadCount = loadBeanDefinitions(resource);
            if (actualResources != null) 
                actualResources.add(resource);
            
            if (logger.isDebugEnabled()) 
                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            
            return loadCount;
        
    
  • 以上方法中,首先要记得resourceLoader是ClassPathXmlApplicationContext(beanDefinitionReader.setResourceLoader(this)这行代码),所有resourceLoader.getResource(location)这行代码最终会调用PathMatchingResourcePatternResolver类的getResources(String locationPattern)方法得到bean有关的Resource对象;
    得到Resource对象后,接着会调用loadBeanDefinitions(Resource... resources)方法来加载bean的定义了,最终是调用XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource)方法:

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException 
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) 
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        
    
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) 
            currentResources = new HashSet<EncodedResource>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        
        if (!currentResources.add(encodedResource)) 
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        
        try 
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try 
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) 
                    inputSource.setEncoding(encodedResource.getEncoding());
                
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            
            finally 
                inputStream.close();
            
        
        catch (IOException ex) 
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        
        finally 
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) 
                this.resourcesCurrentlyBeingLoaded.remove();
            
        
    
  • 上述代码可见,重要的是通过Resource对象得到InputStream,再调用doLoadBeanDefinitions方法:
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException 
        try 
            Document doc = doLoadDocument(inputSource, resource);
            return registerBeanDefinitions(doc, resource);
        
        ...
  • 上面是加载bean定义的关键代码:先制作Document对象,再调用registerBeanDefinitions方法,最终会将每个bean的定义放入DefaultListableBeanFactory的beanDefinitionMap中,详细的堆栈如下图:

  • 完成了bean定义的注册,可以回到AbstractRefreshableApplicationContext.refreshBeanFactory方法了,看看loadBeanDefinitions(beanFactory)之后的代码:
    synchronized (this.beanFactoryMonitor) 
            this.beanFactory = beanFactory;
        
  • 至此,refreshBeanFactory方法分析完毕,该方法所做的事情:把xml文件中的bean定义被解析后,存放在DefaultListableBeanFactory的beanDefinitionMap中;

  • 现在回到主线的AbstractApplicationContext.refresh()方法内,obtainFreshBeanFactory()我们已经分析完毕,所有bean定义都被存放在beanFactory这个临时变量对应的实例中;

prepareBeanFactory

  • 接下来是prepareBeanFactory(beanFactory),看一下此方法的源码:

    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) 
    //设置类加载器
    beanFactory.setBeanClassLoader(getClassLoader());
    //设置解析器,用于解析bean的定义中出现的Spel表达式表达式
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    //设置一个注册接口,该接口只有一个方法registerCustomEditors,用来设置自定义的转换器
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    
    // 部署一个bean的后置处理器ApplicationContextAwareProcessor,用于将spring的环境信息注入到实例化的bean之中
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    //bean在初始化的时候,如果有属性的类型为ResourceLoaderAware,则该属性不会被依赖注入
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    
    // BeanFactory interface not registered as resolvable type in a plain factory.
    // MessageSource registered (and found for autowiring) as a bean.
    //bean如果有个属性的类型为BeanFactory.class,那么该属性会被设置为beanFactory
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    
    // Detect a LoadTimeWeaver and prepare for weaving, if found.
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) 
        // 部署一个bean的后置处理器ApplicationContextAwareProcessor,用于AOP静态代理相关的处理
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        // Set a temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    
    
    // Register default environment beans.
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) 
        //注册一个bean
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) 
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) 
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    
    
  • 上述代码中有以下几点需要注意:

    1. beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())),此方法要配合AbstractBeanFactory.registerCustomEditors方法一起看更好理解:addPropertyEditorRegistrar方法向propertyEditorRegistrars属性中放入了一个registrar,之后调用registerCustomEditors方法的时候,会用到propertyEditorRegistrars中的registrar,调用这些registrar的registerCustomEditors方法,完成自定义的转换器的设置;
    2. beanFactory.addBeanPostProcessor方法用来注入后置处理器,在bean实例被创建后,初始化方法被执行的前后,后置处理器的postProcessBeforeInitialization、postProcessAfterInitialization这两个方法会分别被调用;
    3. beanFactory.ignoreDependencyInterface设置了依赖注入时要忽略的接口,例如bean有个属性类型是ResourceLoaderAware,那么该属性不会被注入ResourceLoaderAware类型的实例;
    4. beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory)是特殊设置,如果一个bean有个属性的类型是BeanFactory,那么该属性会被设置为beanFactory这个实例;
  • 总的来说prepareBeanFactory方法就是为beanFactory做一些设置工作,传入一些后面会用到的参数和工具类,再在spring容器中创建一些bean;

postProcessBeanFactory

  • postProcessBeanFactory方法是留给子类扩展的,可以在bean实例初始化之前注册后置处理器(类似prepareBeanFactory方法中的beanFactory.addBeanPostProcessor),以子类AbstractRefreshableWebApplicationContext为例,其postProcessBeanFactory方法如下:

    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 
        beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
        beanFactory.ignoreDependencyInterface(ServletContextAware.class);
        beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
    
        WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
        WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
    
  • 可见除了WebApplicationContextUtils类的工作之外,其余的都是和prepareBeanFactory方法中类似的处理;

invokeBeanFactoryPostProcessors

  • invokeBeanFactoryPostProcessors方法用来执行BeanFactory实例的后置处理器BeanFactoryPostProcessor的postProcessBeanFactory方法,这个后置处理器除了原生的,我们也可以自己扩展,用来对Bean的定义做一些修改,由于此时bean还没有实例化,所以不要在自己扩展的BeanFactoryPostProcessor中调用那些会触发bean实例化的方法(例如BeanFactory的getBeanNamesForType方法),源码的文档中有相关说明,如下图红框所示,不要触发bean的实例化,如果要处理bean实例请在BeanPostProcessor中进行:

registerBeanPostProcessors

  • registerBeanPostProcessors方法的代码略多,就不在此贴出来了,简单的说,就是找出所有的bean的后置处理器(注意,是bean的后置处理器,不是beanFactory的后置处理器,bean后置处理器处理的是bean实例,beanfactory后置处理器处理的是bean的定义),然后将这些bean的后置处理器分为三类:

    1. 实现了顺序接口Ordered.class的,先放入orderedPostProcessors集合,排序后顺序加入beanFactory的bean后处理集合中;
    2. 既没有实现Ordered.class,也没有实现PriorityOrdered.class的后置处理器,也加入到beanFactory的bean后处理集合中;
    3. 最后是实现了优先级接口PriorityOrdered.class的,排序后顺序加入beanFactory的bean后处理集合中;
  • registerBeanPostProcessors方法执行完毕后,beanFactory中已经保存了有序的bean后置处理器,在bean实例化之后,会依次使用这些后置处理器对bean实例来做对应的处理;

initMessageSource

  • initMessageSource方法用来准备国际化资源相关的,将实现了MessageSource接口的bean存放在ApplicationContext的成员变量中,先看是否有配置,如果有就实例化,否则就创建一个DelegatingMessageSource实例的bean;

initApplicationEventMulticaster

  • spring中有事件、事件广播器、事件监听器等组成事件体系,在initApplicationEventMulticaster方法中对事件广播器做初始化,如果找不到此bean的配置,就创建一个SimpleApplicationEventMulticaster实例作为事件广播器的bean,并且保存- 为applicationContext的成员变量applicationEventMulticaster;

onRefresh

  • onRefresh是个空方法,留给子类自己实现的,在实例化bean之前做一些ApplicationContext相关的操作,以子类AbstractRefreshableWebApplicationContext为例,看看它的onRefresh方法:
    @Override
    protected void onRefresh() 
    this.themeSource = UiApplicationContextUtils.initThemeSource(this);
    
  • 可见是做了主题相关的初始化,并保存在ApplicationContext的成员变量中;

registerListeners

  • 方法名为registerListeners,看名字像是将监听器注册在事件广播器中,但实际情况并非如此,只有一些特殊的监听器被注册了,那些在bean配置文件中实现了ApplicationListener接口的类还没有实例化,所以此处只是将其name保存在广播器中,将这些监听器注册在广播器的操作是在bean的后置处理器中完成的,那时候bean已经实例化完成了,我们看代码:

    protected void registerListeners() 
    // 注册的都是特殊的事件监听器,而并非配置中的bean
    for (ApplicationListener<?> listener : getApplicationListeners()) 
        getApplicationEventMulticaster().addApplicationListener(listener);
    
    
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let post-processors apply to them!
    // 根据接口类型找出所有监听器的名称
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) 
        // 这里只是把监听器的名称保存在广播器中,并没有将这些监听器实例化!!!
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    
    

finishBeanFactoryInitialization

  • finishBeanFactoryInitialization方法做了两件事:
  • beanFactory对象的初始化;
  • 我们在bean配置文件中配置的那些单例的bean,都是在finishBeanFactoryInitialization方法中实例化的;

  • 看代码:

    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) 
    // Initialize conversion service for this context.
    // 实例化类型转换的bean,并保存在ApplicationContext中
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
            beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) 
        beanFactory.setConversionService(
        beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    
    
    // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
    // 实例化LoadTimeWeaverAware接口的bean,用于ApsectJ的类加载期织入的处理
    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    for (String weaverAwareName : weaverAwareNames) 
        getBean(weaverAwareName);
    
    
    // Stop using the temporary ClassLoader for type matching.
    // 确保临时的classLoader为空,临时classLoader一般被用来做类型匹配的
    beanFactory.setTempClassLoader(null);
    
    // Allow for caching all bean definition metadata, not expecting further changes.
    // 将一个标志设置为true,表示applicationContext已经缓存了所有bean的定义,这些bean的name都被保存在applicationContext的frozenBeanDefinitionNames成员变量中,相当于一个快照,记录了当前那些bean的定义已经拿到了
    beanFactory.freezeConfiguration();
    
    // 实例化所有还未实例化的单例bean
    beanFactory.preInstantiateSingletons();
    
  • 上述代码中,beanFactory.preInstantiateSingletons()需要展开仔细看:

    public void preInstantiateSingletons() throws BeansException 
        if (this.logger.isDebugEnabled()) 
            this.logger.debug("Pre-instantiating singletons in " + this);
        
    
        // Iterate over a copy to allow for init methods which in turn register new bean definitions.
        // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
        List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
    
        // Trigger initialization of all non-lazy singleton beans...
        for (String beanName : beanNames) 
            // 获取bean的定义,该定义已经和父类定义做了合并
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            // 非抽象类、是单例、非懒加载
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) 
                //FactoryBean的处理
                if (isFactoryBean(beanName)) 
                    final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) 
                        isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() 
                            @Override
                            public Boolean run() 
                                return ((SmartFactoryBean<?>) factory).isEagerInit();
                            
                        , getAccessControlContext());
                    
                    else 
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    
                    if (isEagerInit) 
                        getBean(beanName);
                    
                
                else 
                    //非FactoryBean的实例化、初始化
                    getBean(beanName);
                
            
        
    
        // Trigger post-initialization callback for all applicable beans...
        // 单例实例化完成后,如果实现了SmartInitializingSingleton接口,afterSingletonsInstantiated就会被调用,此处用到了特权控制逻辑AccessController.doPrivileged
        for (String beanName : beanNames) 
            Object singletonInstance = getSingleton(beanName);
            if (singletonInstance instanceof SmartInitializingSingleton) 
                final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
                if (System.getSecurityManager() != null) 
                    AccessController.doPrivileged(new PrivilegedAction<Object>() 
                        @Override
                        public Object run() 
                            smartSingleton.afterSingletonsInstantiated();
                            return null;
                        
                    , getAccessControlContext());
                
                else 
                    smartSingleton.afterSingletonsInstantiated();
            
        
    
    
  • 上述代码中,要重点关注getBean(beanName),这里面会实例化bean,由于内容太多不适合在本章细说,这里先将实例化bean的调用路径整理出来:
    
    AbstractBeanFactory.getBean(String name)

->

AbstractBeanFactory.doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly)

->

DefaultSingletonBeanRegistry.getSingleton(String beanName, ObjectFactory<?> singletonFactory)

->

AbstractBeanFactory.doGetBean中的匿名类的getObject方法

->

AbstractAutowireCapableBeanFactory.createBean(String beanName, RootBeanDefinition mbd, Object[] args)

->
AbstractAutowireCapableBeanFactory.doCreateBean(final String beanName, final RootBeanDefinition mbd, Object[] args)

->

AbstractAutowireCapableBeanFactory.createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args)

->

instantiateBean(final String beanName, final RootBeanDefinition mbd)

->

SimpleInstantiationStrategy.instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner)

->

BeanUtils.instantiateClass(Constructor<T> ctor, Object... args)

->

Constructor.newInstance(Object ... initargs)

->

bean的构造方法

- 以上调用路径可以看出,bean对象的创建是BeanUtils.instantiateClass方法通过反射来创建的;

- 再来看看bean的成员变量是什么时候被注入值的,如下图,AbstractAutowireCapableBeanFactory.doCreateBean方法中,先调用createBeanInstance创建bean的对象(绿框所示),再调用populateBean方法给成员变量注入内容(红框所示):

![image.png](https://s4.51cto.com/images/blog/202206/09071102_62a12c86093be78398.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)

- 将注入值的调用堆栈整理如下,可见是也是通过反射完成注入的:
```java
AbstractAutowireCapableBeanFactory.populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw)

->

AbstractAutowireCapableBeanFactory.applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs)

->

AbstractPropertyAccessor.setPropertyValues(PropertyValues pvs)

->

BeanWrapperImpl.setPropertyValue(PropertyValue pv)

->

Method.invoke(Object obj, Object... args)
  • 看过了成员变量注入的逻辑后,还有个重要的逻辑也请关注,就是bean的初始化(bean的配置文件中的init-method属性),AbstractAutowireCapableBeanFactory.doCreateBean方法中,在调用populateBean方法给成员变量注入值之后,马上调用initializeBean方法进行初始化操作,调用堆栈整理如下:
    
    AbstractAutowireCapableBeanFactory.initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd)

->

AbstractAutowireCapableBeanFactory.invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)

->

AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)

->

Method.invoke(Object obj, Object... args)

- 可见依旧是通过反射来执行初始化方法;

### finishRefresh
- 最后一个方法是finishRefresh,这是在bean的实例化、初始化等完成后的一些操作,例如生命周期变更的回调,发送applicationContext刷新完成的广播等,展开看看:
```java
protected void finishRefresh() 
    // 检查是否已经配置了生命周期处理器,如果没有就new一个DefaultLifecycleProcessor
    initLifecycleProcessor();

    // 找到所有实现了Lifecycle接口的bean,按照每个bean设置的生命周期阶段进行分组,再依次调用每个分组中每个bean的start方法,完成生命周期监听的通知
    getLifecycleProcessor().onRefresh();

    // 创建一条代表applicationContext刷新完成的事件,交给广播器去广播
    publishEvent(new ContextRefreshedEvent(this));

    // 如果配置了MBeanServer,就完成在MBeanServer上的注册
    LiveBeansView.registerApplicationContext(this);
  • 至此,整个初始化流程咱们已经过了一遍了,但是篇幅有限,很多细节都没有展开,另外很多子类也有自己独特的扩展,这些都需要花时间去细看,希望本文能帮您整理思路,从总体上了解初始化的各个关键步骤,以免过早陷入细节;

欢迎关注51CTO博客:程序员欣宸

自定义springbootstarter三部曲之三:源码分析spring.factories加载过程(代码片段)

欢迎访问我的GitHub本文是《自定义springbootstarter三部曲》系列的终篇,前文中我们开发了一个starter并做了验证,发现关键点在于spring.factories的自动加载能力,让应用只要依赖starter的jar包即可,今天我们来分析Spring和Springboot源码... 查看详情

spring4.1.8扩展实战之一:自定义环境变量验证

欢迎访问我的GitHub关于扩展在之前学习spring环境初始化源码的过程中,见到有些地方能通过子类来实现自定义扩展,从本章开始,我们来逐个实践这些扩展,除了加深对spring的理解,有的扩展也能解决一些通用的问题;文中涉及... 查看详情

javacv人脸识别三部曲之三:识别和预览

...套源码):https://github.com/zq2599/blog_demos《JavaCV人脸识别三部曲》链接《视频中的人脸保存为图片》《训练》《识别和预览》本篇概览作为《JavaCV人脸识别三部曲》的终篇,今天咱们要开发一个实用的功能:有人出现在... 查看详情

leetcode952三部曲之三:再次优化(122ms->96ms,超51%->超91%)(代码片段)

...f1a;https://github.com/zq2599/blog_demos本篇概览本文是《LeetCode952三部曲之三》的终篇,先回顾一下前文的成果,看看我们之前已经优化到什么程度:前文的优化思路是减小并查集数组的规模,带来的结果是节省内存、减... 查看详情

leetcode952三部曲之三:再次优化(122ms->96ms,超51%->超91%)(代码片段)

...f1a;https://github.com/zq2599/blog_demos本篇概览本文是《LeetCode952三部曲之三》的终篇,先回顾一下前文的成果,看看我们之前已经优化到什么程度:前文的优化思路是减小并查集数组的规模,带来的结果是节省内存、减... 查看详情

go语言基准测试(benchmark)三部曲之三:提高篇(代码片段)

...github.com/zq2599/blog_demos本篇概览-《Go语言基准测试(benchmark)三部曲》已近尾声,经历了《基础篇》和《内存篇》的实战演练,相信您已熟练掌握了基准测试的常规操作以及各种参数的用法,现在可以学习一些进阶版的技... 查看详情

flinkonyarn三部曲之三:提交flink任务(代码片段)

欢迎访问我的GitHub本文是《FlinkonYarn三部曲》系列的终篇,先简单回顾前面的内容:《FlinkonYarn三部曲之一:准备工作》:准备好机器、脚本、安装包;《FlinkonYarn三部曲之二:部署和设置》:完成CDH和Flink部署,并在管理页面做... 查看详情

elasticsearch实战三部曲之三:搜索操作(代码片段)

欢迎访问我的GitHub本文是《elasticsearch实战三部曲》的终篇,作为elasticsearch的核心功能,搜索的重要性不言而喻,今天的实战都会围绕搜索展开;环境信息本次实战用到的elasticsearch版本是6.5.4,安装在Ubuntu16.04.5LTS,客户端工具是... 查看详情

leetcode第三题(longestsubstringwithoutrepeatingcharacters)三部曲之三:两次优化(代码片段)

...ub本文是《LeetCode第三题(LongestSubstringWithoutRepeatingCharacters)三部曲》的第三篇,之前的两篇文章列出了思路并写出了Java代码,虽然在LeetCode网站提交通过,但是成绩并不理想,40多毫秒的速度,与诸多优秀的方案有不小差距,今天... 查看详情

k8s自定义controller三部曲之三:编写controller代码(代码片段)

欢迎访问我的GitHub本篇概览本文是《k8s自定义controller三部曲》的终篇,前面的章节中,我们创建了CRD,再通过自动生成代码的工具将controller所需的informer、client等依赖全部准备好,到了本章,就该编写controller的代码了,也就是... 查看详情

javacv人脸识别三部曲之三:识别和预览(代码片段)

...套源码):https://github.com/zq2599/blog_demos《JavaCV人脸识别三部曲》链接《视频中的人脸保存为图片》《训练》《识别和预览》本篇概览作为《JavaCV人脸识别三部曲》的终篇,今天咱们要开发一个实用的功能:有人出现在... 查看详情

javacv人脸识别三部曲之三:识别和预览(代码片段)

...套源码):https://github.com/zq2599/blog_demos《JavaCV人脸识别三部曲》链接《视频中的人脸保存为图片》《训练》《识别和预览》本篇概览作为《JavaCV人脸识别三部曲》的终篇,今天咱们要开发一个实用的功能:有人出现在... 查看详情

docker下prometheus和grafana三部曲之三:自定义监控项开发和配置(代码片段)

...迎访问我的GitHub本篇概览本文是《Docker下Prometheus和Grafana三部曲》的终篇,前面的文章中,我们体验了快速搭建监控环境,也揭示了如何编排Docker容器来简化环境搭建过程,在监控系统中有个业务web服务,它上报的数据也能在监... 查看详情

docker下rabbitmq四部曲之三:细说java开发

欢迎访问我的GitHub本文是《Docker下RabbitMQ四部曲》系列的第三篇,实战两个基于SpringBoot的工程,分别用来生产和消费RabbitMQ消息;前文链接前两章的内容是体验RabbitMQ服务,以及制作RabbitMQ镜像:《Docker下RabbitMQ四部曲之一:极速... 查看详情

spring4.1.8扩展实战之五:改变bean的定义(beanfactorypostprocessor接口)(代码片段)

...ean实例做一些控制;BeanFactoryPostProcessor接口简介spring容器初始化时,从资源中读取到bean的相关定义后,保存在beanFactory的成员变量中(参考DefaultListableBeanFactory类的成员变量 查看详情

spring4.1.8扩展实战之八:import注解(代码片段)

欢迎访问我的GitHub在spring框架下做开发时,@Import是常见的注解,可以用来动态创建bean,今天我们先从源码分析原理,再用实战来验证Import的作用;文章概览本章由以下几部分组成:从Enable前缀的注解谈起,揭示常见的Enable注解... 查看详情

spring4.1.8扩展实战之四:感知spring容器变化(smartlifecycle接口)(代码片段)

欢迎访问我的GitHub本篇概览本章是《spring4.1.8扩展实战》的第四篇,如果业务上需要在spring容器启动和关闭的时候做一些操作,可以自定义SmartLifecycle接口的实现类来扩展,本章我们通过先分析再实战的方法,来掌握这种扩展方... 查看详情

tikv源码阅读三部曲写流程(代码片段)

背景TiKV是一个支持事务的分布式Key-Value数据库,目前已经是CNCF基金会的顶级项目。作为一个新同学,需要一定的前期准备才能够有能力参与TiKV社区的代码开发,包括但不限于学习Rust语言,理解TiKV的原理和在前... 查看详情