美文网首页
Spring Ioc: refresh、prepareRefre

Spring Ioc: refresh、prepareRefre

作者: 逗逗罗 | 来源:发表于2020-12-14 10:50 被阅读0次
前言

在文章 ApplicationContext 体系结构 中我们从一张 ApplicationContext 体系的类图开始,介绍了 ApplicationContext 体系中各个类的继承关系,接着分析了 ClassPathXmlApplicationContext 和 AnnotationConfigApplicationContext 类的构造函数中前两个函数的源码。从本篇文章开始,将重点介绍 refresh 函数。

  • AbstractApplicationContext::refresh
    ClassPathXmlApplicationContext 和 AnnotationConfigApplicationContext 类均继承了 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.
            // (1) 创建 IOC 容器(DefaultListableBeanFactory)
            // (2) 加载 xml 文件(最终生成 Document 对象)
            // (3) 读取 Document 对象,完成 BeanDefinition 的加载和注册
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
            // Prepare the bean factory for use in this context.
            // 对IOC容器进行预处理
            prepareBeanFactory(beanFactory);
    
            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);
    
                // Invoke factory processors registered as beans in the context.
                // 调用BeanFactoryPostProcessor后置处理器对 BeanDefinition 处理
                invokeBeanFactoryPostProcessors(beanFactory);
    
                // Register bean processors that intercept bean creation.
                // 注册BeanPostProcessor后置处理器
                registerBeanPostProcessors(beanFactory);
    
                // Initialize message source for this context.
                // STEP 7: 初始化一些消息源(比如处理国际化的i18n等消息源)
                initMessageSource();
    
                // Initialize event multicaster for this context.
                // STEP 8: 初始化应用事件广播器
                initApplicationEventMulticaster();
    
                // Initialize other special beans in specific context subclasses.
                // STEP 9: 初始化一些特殊的bean
                onRefresh();
    
                // Check for listener beans and register them.
                // STEP 10: 注册一些监听器
                registerListeners();
    
                // Instantiate all remaining (non-lazy-init) singletons.
                // STEP 11: 实例化剩余的单例bean(非懒加载方式)
                // 注意事项:Bean的IoC、DI和AOP都是发生在此步骤
                finishBeanFactoryInitialization(beanFactory);
    
                // Last step: publish corresponding event.
                // STEP 12: 完成刷新时,需要发布对应的事件
                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();
            }
        }
    }
    
  • AbstractApplicationContext::prepareRefresh

    protected void prepareRefresh() {
        // Switch to active.
        // 记录启动时间,容器状态
        this.startupDate = System.currentTimeMillis();
        this.closed.set(false);
        this.active.set(true);
    
        if (logger.isInfoEnabled()) {
            logger.info("Refreshing " + this);
        }
    
        // Initialize any placeholder property sources in the context environment.
        // 空函数什么也没做,初始化属性配置在其子类中有实现(例如:AbstractRefreshableWebApplicationContext、GenericWebApplicationContext等)
        initPropertySources();
    
        // Validate that all properties marked as required are resolvable:
        // see ConfigurablePropertyResolver#setRequiredProperties
        /**
        * 1. 获取 ConfigurableEnvironment
        * 2. 确保配置所有必须的属性
        **/
        getEnvironment().validateRequiredProperties();
    
        // Store pre-refresh ApplicationListeners...
        // 创建早期时间监听器容集合,保存早期监听器,也就是在之前已经初始化的监听器。
        if (this.earlyApplicationListeners == null) {
            this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
        }
        else {
            // Reset local application listeners to pre-refresh state.
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }
    
        // Allow for the collection of early ApplicationEvents,
        // to be published once the multicaster is available...
        // 创建早期事件集合
        this.earlyApplicationEvents = new LinkedHashSet<>();
    }
    
  • ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()

    创建 BeanFactory

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        // 1.
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }
    
    • refreshBeanFactory
      AbstractApplicationContext 中该方法为空,在子类 AbstractRefreshableApplicationContext(ClassPathXmlApplicationContext继承该类)、GenericApplicationContext(AnnotationConfigApplicationContext继承该类)中对该方法进行了扩展。

      1. AbstractRefreshableApplicationContext::refreshBeanFactory

        protected final void refreshBeanFactory() throws BeansException {
            // 若已存在 BeanFactory,则销毁容器中的 Bean 实例,将 BeanFactory 置为 null
            if (hasBeanFactory()) {
                destroyBeans();
                closeBeanFactory();
            }
            try {
                // 创建 BeanFactory 实例,类型为:DefaultListableBeanFactory
                DefaultListableBeanFactory beanFactory = createBeanFactory();
                beanFactory.setSerializationId(getId());
                // 定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖
                // 可在子类中去扩展定义属性:allowBeanDefinitionOverriding, allowCircularReferences
                customizeBeanFactory(beanFactory);
                // 加载 BeanDefinition,并注册到IoC容器中(重点)
                loadBeanDefinitions(beanFactory);
                synchronized (this.beanFactoryMonitor) {
                    this.beanFactory = beanFactory;
                }
            }
            catch (IOException ex) {
                throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
            }
        }
        
        • loadBeanDefinitions
          在 AbstractRefreshableApplicationContext::refreshBeanFactory 中 loadBeanDefinitions 为重点代码,接下来将详细介绍改部分的代码。
          |-- AbstractRefreshableApplicationContext::refreshBeanFactory 
          |-- AbstractXmlApplicationContext::loadBeanDefinitions:走了多个重载方法 
              |--AbstractBeanDefinitionReader::loadBeanDefinitions:走了多个重载方法 
              |--XmlBeanDefinitionReader::loadBeanDefinitions:走了多个重载方法 
              |--XmlBeanDefinitionReader::doLoadBeanDefinitions 
              |--XmlBeanDefinitionReader::registerBeanDefinitions 
                      |-- DefaultBeanDefinitionDocumentReader
                          ::registerBeanDefinitions   
                          ::doRegisterBeanDefinitions 
                          ::parseBeanDefinitions 
                          ::parseDefaultElement 
                          ::processBeanDefinition 
                              |--BeanDefinitionParserDelegate
                              ::parseBeanDefinitionElement 
                              ::parseBeanDefinitionElement
          1. AbstractRefreshableApplicationContext:主要用来对BeanFactory提供 refresh 功能。包括BeanFactory的创建和 BeanDefinition 的定义、解析、注册操作。
          2. AbstractXmlApplicationContext:提供对 xml 资源的解析功能,包括从Resource资源对象和资源路径中加载XML文件。
          3. AbstractBeanDefinitionReader:主要提供对于 BeanDefinition 对象的读取功能。具体读取工作交给子类实现。
          4. XmlBeanDefinitionReader:主要通过 DOM4J 对于 XML资源 的读取、解析功能,并提供对于 BeanDefinition 的注册功能。
          5. DefaultBeanDefinitionDocumentReader
          6. BeanDefinitionParserDelegate
          
          • AbstractXmlApplicationContext::loadBeanDefinitions
          protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
              // Create a new XmlBeanDefinitionReader for the given BeanFactory.
              // 创建 XmlBeanDefinitionReader, 此时会初始化 ResourceLoaderLoader
              XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
          
              // Configure the bean definition reader with this context's
              // resource loading environment.
              beanDefinitionReader.setEnvironment(this.getEnvironment());
              beanDefinitionReader.setResourceLoader(this);
              beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
          
              // Allow a subclass to provide custom initialization of the reader,
              // then proceed with actually loading the bean definitions.
              initBeanDefinitionReader(beanDefinitionReader);
              loadBeanDefinitions(beanDefinitionReader);
          }
          
          protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
              // debug 中 configResourses 为 null
              Resource[] configResources = getConfigResources();
              if (configResources != null) {
                  reader.loadBeanDefinitions(configResources);
              }
              String[] configLocations = getConfigLocations();
              if (configLocations != null) {
                  // 根据配置文件位置加载BeanDefinition
                  reader.loadBeanDefinitions(configLocations);
              }
          }
          
          // AbstractBeanDefinitionReader::loadBeanDefinitions
          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;
          }
          
          public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
              // 初始化 XmlBeanDefinitionReader 时已经初始化了 ResourceLoader
              // XmlBeanDefinitionReader 无法直接加载配置文件,因此需要委托 ResourceLoader 将配置文件加载为 Resource 对象
              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);
                      // 根据 Resource 加载 BeanDefinition
                      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;
              }
          }
          
          • XmlBeanDefinitionReader::loadBeanDefinitions
          public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
              return loadBeanDefinitions(new EncodedResource(resource));
          }
          
          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);
              }
          
              Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
              if (currentResources == null) {
                  currentResources = new HashSet<>(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();
                  }
              }
          }
          
          protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
                  throws BeanDefinitionStoreException {
              try {
                  // 将配置文件转换为 Document 对象
                  Document doc = doLoadDocument(inputSource, resource);
                  return registerBeanDefinitions(doc, resource);
              }
              // .. 此处异常捕获处理代码省去
          }
          
          public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
              // 创建 DefaultBeanDefinitionDocumentReader 对象
              BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
              int countBefore = getRegistry().getBeanDefinitionCount();
              // 1. 创建 Reader 上下文
              // 2. 根据 Document 对象加载 BeanDefinition
              documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
              return getRegistry().getBeanDefinitionCount() - countBefore;
          }
          
          • XmlBeanDefinitionReader::registerBeanDefinitions
          // 创建 XmlReaderContext, 需要创建 NameSpaceHandlerResolver 用来处理 xml 配置文件中的各命名空间
          // 读取 "META-INF/spring.handlers" 创建 DefaultNamespaceHandlerResolver
          public XmlReaderContext createReaderContext(Resource resource) {
              return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                      this.sourceExtractor, this, getNamespaceHandlerResolver());
          }
          
          public NamespaceHandlerResolver getNamespaceHandlerResolver() {
              if (this.namespaceHandlerResolver == null) {
                  this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
              }
              return this.namespaceHandlerResolver;
          }
          
          protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
              ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
              return new DefaultNamespaceHandlerResolver(cl);
          }
          
          public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
              //public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
              this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
          }
          
          • DefaultBeanDefinitionDocumentReader::registerBeanDefinitions
          public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
              this.readerContext = readerContext;
              logger.debug("Loading bean definitions");
              Element root = doc.getDocumentElement();
              doRegisterBeanDefinitions(root);
          }
          
          protected void doRegisterBeanDefinitions(Element root) {
              
              BeanDefinitionParserDelegate parent = this.delegate;
              // 根据 Reader 上下文创建 BeanDefinitionParserDelegate
              this.delegate = createDelegate(getReaderContext(), root, parent);
              /**
              * BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
              * delegate.initDefaults(root, parentDelegate);
              **/
          
              if (this.delegate.isDefaultNamespace(root)) {
                  String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
                  if (StringUtils.hasText(profileSpec)) {
                      String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                              profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                      if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                          if (logger.isInfoEnabled()) {
                              logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                      "] not matching: " + getReaderContext().getResource());
                          }
                          return;
                      }
                  }
              }
              // 空函数
              preProcessXml(root);
              // 解析标签
              parseBeanDefinitions(root, this.delegate);
              // 空函数
              postProcessXml(root);
          
              this.delegate = parent;
          }
          
          protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
              if (delegate.isDefaultNamespace(root)) {
                  NodeList nl = root.getChildNodes();
                  for (int i = 0; i < nl.getLength(); i++) {
                      Node node = nl.item(i);
                      if (node instanceof Element) {
                          Element ele = (Element) node;
                          if (delegate.isDefaultNamespace(ele)) {
                              // 处理默认的标签元素:bean/import/alias/beans
                              parseDefaultElement(ele, delegate);
                          }
                          else {
                              // 处理自定义的标签
                              delegate.parseCustomElement(ele);
                          }
                      }
                  }
              }
              else {
                  delegate.parseCustomElement(root);
              }
          }
          
          private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
              if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
                  // 引入新的文件
                  importBeanDefinitionResource(ele);
              }
              else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
                  processAliasRegistration(ele);
              }
              else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
                  // 处理 bean 标签
                  processBeanDefinition(ele, delegate);
              }
              else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
                  // recurse 递归调用
                  doRegisterBeanDefinitions(ele);
              }
          }
          
          protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
              // 解析 Bean 标签,创建 BeanDefinitionHolder 对象
              BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
              if (bdHolder != null) {
                  bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
                  try {
                      // Register the final decorated instance.
                      BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
                  }
                  catch (BeanDefinitionStoreException ex) {
                      getReaderContext().error("Failed to register bean definition with name '" +
                              bdHolder.getBeanName() + "'", ele, ex);
                  }
                  // Send registration event.
                  getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
              }
          }
          
          public static void registerBeanDefinition(
                  BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
                  throws BeanDefinitionStoreException {
          
              // Register bean definition under primary name.
              String beanName = definitionHolder.getBeanName();
              // 注册 BeanDefinition 至于 DefaultListableBeanFactory 的 beanDefinitionMap 中
              registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
          
              // Register aliases for bean name, if any.
              String[] aliases = definitionHolder.getAliases();
              if (aliases != null) {
                  for (String alias : aliases) {
                      registry.registerAlias(beanName, alias);
                  }
              }
          }
          
          // 自定义命名空间的解析,如: context,aop,tx
          public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
              String namespaceUri = getNamespaceURI(ele);
              if (namespaceUri == null) {
                  return null;
              }
              // 根据 META-INF/spring.handlers 中的配置,找到 Namespace 对应的 Handler
              NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
              if (handler == null) {
                  error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
                  return null;
              }
              // 调用 NamespaceHandler 实现的 parse 方法将 dom element 解析为 BeanDefinition
              return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
          }
          
          

        上述源码流程即为 obtainFreshBeanFactory 的过程,主要完成了

        1. 创建了 DefaultListableBeanFactory;
        2. 读取配置文件,完成了BeanDefinition的加载

        引申:
        在对自定义命名空间进行处理时,会有一些预置的 BeanDefinition 的加载,例如对 context:annotation-config 标签处理时,会加载 ConfigurationClassPostProcessor 类(BeanFactoryPostProcessor)的 BeanDefinition, 在后续 invokeBeanFactoryPostProcessors 方法中,会使用到该扩展点

        public class ContextNamespaceHandler extends NamespaceHandlerSupport {
        
            @Override
            public void init() {
                registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
                registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
                // 对 @autowired、@ Resource 、@ PostConstruct、@ PreDestroy、@Required、@PersistenceContext 注解解析
                registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
                // 除了annotation-config对应的注解解析外,扩展了 @Component 注解的支持,因此如果配制了 context:component-scan 就无须再配置 context:annotation-config
                registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
                registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
                registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
                registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
                registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
            }
        
        }
        
      2. GenericApplicationContext::refreshBeanFactory
        GenericApplicationContext 的 refresh 方法相比于 AbstractRefreshableApplicationContext 要简单许多,只有一个 refresh 标记的判断以及序列化 ID 的设置

        protected final void refreshBeanFactory() throws IllegalStateException {
            if (!this.refreshed.compareAndSet(false, true)) {
                throw new IllegalStateException(
                        "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
            }
            this.beanFactory.setSerializationId(getId());
        }
        
        
总结

本篇文章中首先对 refresh 函数源码进行了简单的分析,了解了各个函数大概的作用,然后针对 prepareRefresh 和 obtainFreshBeanFactory 函数做了详细的分析。

  1. prepareRefresh
    在该函数中进行了一些容器刷新的准备,如记录容器启动时间、定义早期事件的监听器等
  2. obtainFreshBeanFactory
    AbstractApplicationContext 中该方法为空,在其子类 AbstractRefreshableApplicationContext 和 GenericApplicationContext 中对该方法进行了扩展。
    • AbstractRefreshableApplicationContext
      方法中首先定义了 beanDefinitionReader,然后读取配置文件将配置文件中定义的 Bean 信息加载为 BeanDefinition,需要注意的是其中包含一些自定义命名空间的解析。
    • GenericApplicationContext
      仅做了refresh 标记的判断以及序列化 ID 的设置

相关文章

网友评论

      本文标题:Spring Ioc: refresh、prepareRefre

      本文链接:https://www.haomeiwen.com/subject/ycqvgktx.html