Spring代码分析一:加载与初始化

两盒软妹~` 提交于 2019-12-03 16:29:23
一般的Web项目都会在web.xml中加入Spring监听器,内容如下: 

Xml代码   收藏代码
  1. <listener>   
  2.         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>   
  3. </listener>  
  4.    
  5. <context-param>   
  6.         <param-name>contextConfigLocation</param-name>   
  7.         <param-value>classpath*:applicationContext-struts.xml,classpath*:spring/applicationContext.xml</param-value>   
  8. </context-param>  

我们的问题是,Spring是何时以及如何加载我们的配置文件来初始化Bean工厂的,带着这些问题,我们展开研究: 

我们先来看看web.xml中配置的监听器的类,来回答我们的问题,Spring是何时来加载我们的配置文件的: 

org.springframework.web.context.ContextLoaderListener 
 


它继承了javax.servlet.ServletContextListener接口。 

ServletContextListener是J2EE Servlet API中的一个标准接口, 

它能够监听ServletContext对象的生命周期,实际上就是监听Web应用的生命周期。 

当Servlet容器启动或终止Web应用时,会触发ServletContextEvent事件,该事件由ServletContextListener来处理。 

这里面有两个方法我们比较感兴趣: 

Java代码   收藏代码
  1. /**  
  2.  * Create the ContextLoader to use. Can be overridden in subclasses.  
  3.  * @return the new ContextLoader  
  4.  */  
  5. protected ContextLoader createContextLoader() {   
  6.       return new ContextLoader();   
  7. }  

这个方法构造一个默认的ContextLoader,ContextLoader可以理解为Spring上下文的加载器。之所以这样去定义这样一个类,是为了开发人员进行重写此方法来使用一个自定义的Spring上下文的加载器。 

Java代码   收藏代码
  1. /**  
  2.  * Initialize the root web application context.  
  3.  */  
  4. public void contextInitialized(ServletContextEvent event) {   
  5.      this.contextLoader = createContextLoader();   
  6.      this.contextLoader.initWebApplicationContext(event.getServletContext());   
  7. }  

这个方法很简单,仅仅只是调用了createContextLoader()构造了ContextLoader,并调用其初始化方法。 

由此,我们可以得出结论,Spring是在Web项目启动时,通过ServletContextListener机制,来加载以及初始化Spring上下文的。 

  

下面,我们好好研究一下Spring是如何加载其上下文的: 

我们先定位ContextLoader类。 



看看此类的initWebApplicationContext()方法(省略了不重要的语句) 

Java代码   收藏代码
  1. /**  
  2.  * Initialize Spring's web application context for the given servlet context,  
  3.  * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and  
  4.  * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.  
  5.  * @param servletContext current servlet context  
  6.  * @return the new WebApplicationContext  
  7.  * @throws IllegalStateException if there is already a root application context present  
  8.  * @throws BeansException if the context failed to initialize  
  9.  * @see #CONTEXT_CLASS_PARAM  
  10.  * @see #CONFIG_LOCATION_PARAM  
  11.  */  
  12. public WebApplicationContext initWebApplicationContext(ServletContext servletContext)   
  13.         throws IllegalStateException, BeansException {  
  14.     if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {   
  15.             throw new IllegalStateException(   
  16.                     "Cannot initialize context because there is already a root application context present - " +   
  17.                     "check whether you have multiple ContextLoader* definitions in your web.xml!");   
  18.     }  
  19.     try {   
  20.         // Determine parent for root web application context, if any.   
  21.         ApplicationContext parent = loadParentContext(servletContext);  
  22.    
  23.         // Store context in local instance variable, to guarantee that   
  24.         // it is available on ServletContext shutdown.   
  25.         this.context = createWebApplicationContext(servletContext, parent);   
  26.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);   
  27.         currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);  
  28.    
  29.         return this.context;   
  30.     } catch (RuntimeException ex) {   
  31.         logger.error("Context initialization failed", ex);   
  32.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);   
  33.         throw ex;   
  34.     } catch (Error err) {   
  35.         logger.error("Context initialization failed", err);   
  36.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);   
  37.         throw err;   
  38.     }   
  39. }  

其中的有两句比较重要,我们来看看: 

Java代码   收藏代码
  1. ApplicationContext parent = loadParentContext(servletContext);  


这个方法的用途主要是用来解决Spring共享环境的,即,如果我们有多个WAR包部署在同一个服务器上,而且这些WAR都共享某一套业务逻辑层。如何共享一套业务逻辑包配置而不要每个WAR都单独配置,这时我们就可能需要Spring的共享环境了。 

Java代码   收藏代码
  1. protected ApplicationContext loadParentContext(ServletContext servletContext) throws BeansException {  
  2.     ApplicationContext parentContext = null;  
  3.     // 从web.xml中读取父工厂的配置文件,默认为:"classpath*:beanRefContext.xml"   
  4.     String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);  
  5.    
  6.     // 从web.xml中读取父类工厂的名称   
  7.     String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);  
  8.    
  9.     if (parentContextKey != null) {   
  10.         // locatorFactorySelector may be null, indicating the default "classpath*:beanRefContext.xml"   
  11.         BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);   
  12.         this.parentContextRef = locator.useBeanFactory(parentContextKey);   
  13.         parentContext = (ApplicationContext) this.parentContextRef.getFactory();   
  14.     }  
  15.    
  16.     return parentContext;   
  17. }  

现在我们引入BeanFactoryLocator,它是Spring配置文件的一个定位器,Spring官方给它的定义是用来查找,使用和释放一个BeanFactory或其子类的接口。下面我们看看此图: 
 


Java代码   收藏代码
  1. ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);  


是根据参数locatorFactorySelector去一个单例工厂中去拿一个对应的BeanFactoryLocator,也即,如果工厂中没有对应于locatorFactorySelector的BeanFactoryLocator对象,那就返回一个新的BeanFactoryLocator实例(这里是ContextSingletonBeanFactoryLocator的实例),否则,就从工厂里取现有的BeanFactoryLocator对象。 

ContextSingletonBeanFactoryLocator里维护了一个静态的Map对象instances,每次需要新增BeanFactoryLocator实例时都会更新这个Map对象,这个Map对象是以配置文件名为KEY,BeanFactoryLocator对象为值。原因很简单,就是希望同一个配置文件只被初始化一次。 

如果没有在web.xml中定义locatorFactorySelector这个参数,父环境的配置文件默认使用:"classpath*:beanRefContext.xml" 
Java代码   收藏代码
  1. this.parentContextRef = locator.useBeanFactory(parentContextKey);  


此方法定义在SingletonBeanFactoryLocator类中,同样是一个单例工厂模式,判断传入的参数parentContextKey对应的BeanFactory是否有被初始化,经过上面的ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector)指定Spring父环境配置文件,这个方法判断指定的父环境是否被初始化,如果有则返回,没有就进行初始化。看看此方法的实现: 

Java代码   收藏代码
  1. public BeanFactoryReference useBeanFactory(String factoryKey) throws BeansException {   
  2.     synchronized (this.bfgInstancesByKey) {   
  3.         BeanFactoryGroup bfg = (BeanFactoryGroup) this.bfgInstancesByKey.get(this.resourceLocation);  
  4.    
  5.         if (bfg != null) {   
  6.             bfg.refCount++;   
  7.         } else {   
  8.             // Create the BeanFactory but don't initialize it.   
  9.             BeanFactory groupContext = createDefinition(this.resourceLocation, factoryKey);  
  10.    
  11.             // Record its existence now, before instantiating any singletons.   
  12.             bfg = new BeanFactoryGroup();   
  13.             bfg.definition = groupContext;   
  14.             bfg.refCount = 1;   
  15.             this.bfgInstancesByKey.put(this.resourceLocation, bfg);   
  16.             this.bfgInstancesByObj.put(groupContext, bfg);  
  17.    
  18.             // Now initialize the BeanFactory. This may cause a re-entrant invocation   
  19.             // of this method, but since we've already added the BeanFactory to our   
  20.             // mappings, the next time it will be found and simply have its   
  21.             // reference count incremented.   
  22.             try {   
  23.                 initializeDefinition(groupContext);   
  24.             } catch (BeansException ex) {   
  25.                 this.bfgInstancesByKey.remove(this.resourceLocation);   
  26.                 this.bfgInstancesByObj.remove(groupContext);   
  27.                 throw new BootstrapException("Unable to initialize group definition. " +   
  28.                     "Group resource name [" + this.resourceLocation + "], factory key [" + factoryKey + "]", ex);   
  29.             }   
  30.         }  
  31.    
  32.         try {   
  33.             BeanFactory beanFactory = null;   
  34.             if (factoryKey != null) {   
  35.                 beanFactory = (BeanFactory) bfg.definition.getBean(factoryKey, BeanFactory.class);   
  36.             } else if (bfg.definition instanceof ListableBeanFactory) {   
  37.                 beanFactory = (BeanFactory) BeanFactoryUtils.beanOfType((ListableBeanFactory) bfg.definition, BeanFactory.class);   
  38.             } else {   
  39.                 throw new IllegalStateException(   
  40.                     "Factory key is null, and underlying factory is not a ListableBeanFactory: " + bfg.definition);   
  41.             }   
  42.             return new CountingBeanFactoryReference(beanFactory, bfg.definition);   
  43.          } catch (BeansException ex) {   
  44.              throw new BootstrapException("Unable to return specified BeanFactory instance: factory key [" +   
  45.                     factoryKey + "], from group with resource name [" + this.resourceLocation + "]", ex);   
  46.          }  
  47.    
  48.     }   
  49. }  

此方法分为两作了两件事, 

第一,初始化上下文,主意这里初始化的是从web.xml配置参数里的Spring配置文件,也是上面讲loadParentContext方法里的 

Java代码   收藏代码
  1. BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);  


这句指定的参数。这里初始化的是这个配置文件所有Bean。我们指定的factoryKey对应的Bean也是其中之一。 

第二,从已经初始化的Spring上下文环境中获取Spring父环境。 

Xml代码   收藏代码
  1. <beans>    
  2.      <bean id="factoryBeanId" class="org.springframework.context.support.ClassPathXmlApplicationContext">    
  3.          <constructor-arg>    
  4.                 <list>    
  5.                      <value>sharebean.xml</value>    
  6.                 </list>    
  7.          </constructor-arg>    
  8.      </bean>   
  9.      <bean id="factoryBeanId2" class="org.springframework.context.support.ClassPathXmlApplicationContext">    
  10.          <constructor-arg>    
  11.                 <list>    
  12.                      <value>sharebean2.xml</value>    
  13.                 </list>    
  14.          </constructor-arg>    
  15.      </bean>   
  16. </beans>  
  17.   
  18. <!—========================= web.xml ========================= -->    
  19. <context-param>    
  20.         <param-name>locatorFactorySelector</param-name>    
  21.         <param-value>beanRefFactory.xml</param-value>    
  22. </context-param>    
  23.    
  24. <context-param>    
  25.         <param-name>parentContextKey</param-name>    
  26.         <param-value>factoryBeanId</param-value>    
  27. </context-param>  

这个一个典型的构造父环境的配置,web项目在启动的时候就会发现里面有Spring父环境的配置,那么Spring首先就会生成一个对应的配置文件为beanRefFactory.xml的BeanFactory(web.xml中的locatorFactorySelector参数指定),同时Spring在解析的时候,会发现factoryBeanId的配置同样为BeanFacotry(beanRefFactory.xml中factoryBeanId对应的Bean),所以Spring在拿父环境时就会写成: 

Java代码   收藏代码
  1. beanFactory = (BeanFactory) bfg.definition.getBean(factoryKey, BeanFactory.class);  


方法实现里引入了BeanFactoryGroup类。类的结构很简单 
 

refCount:用来记录实例被外部引用的记数,当调用locator.useBeanFactory(parentContextKey)方法时,引用数就会加1,当调用CountingBeanFactoryReference#release方法时,引用数就会减1,当它变成0时,Spring就会释放掉它占用的内存,同时也会销毁掉它definition变量引用的BeanFactory。下次再调用locator.useBeanFactory(parentContextKey)就会重新初始化BeanFactory。说到release,请同学们参考ContextLoader中如下的两条语句: 

// 在调用CountingBeanFactoryReference#release后,即使对象已经销毁,这个Map仍然可以返回locator对象。 

Java代码   收藏代码
  1. BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);  


// 如果对象已经销毁,再调用此方法会再一次初始化BeanFactory 
Java代码   收藏代码
  1. this.parentContextRef = locator.useBeanFactory(parentContextKey);  


bfgInstancesByKey:一个Map对象,以配置文件名为Key,配置文件解析后生成的BeanFactory构成的BeanFactoryGroup为值。 

bfgInstancesByObj:一个Map对象,以BeanFactoryGroup.definitiion为Key,以BeanFactoryGroup为值。这个对象主要还是在CountingBeanFactoryReference#release时使用。 

下面,我看再看看另一个地方: 

Java代码   收藏代码
  1. if (parentContextKey != null) {   
  2.       // locatorFactorySelector may be null, indicating the default "classpath*:beanRefContext.xml"   
  3.       BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);   
  4.       this.parentContextRef = locator.useBeanFactory(parentContextKey);   
  5.       parentContext = (ApplicationContext) this.parentContextRef.getFactory();   
  6. }  
  7. BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);  

上面这句仅仅是做了如下工作: 

Java代码   收藏代码
  1. BeanFactoryLocator bfl = (BeanFactoryLocator) instances.get(resourceLocation);   
  2. if (bfl == null) {  
  3.        // 仅仅只是设置了ContextSingletonBeanFactoryLocator里的resourceLocation属性的值,并没有初始化工厂。   
  4.         bfl = new ContextSingletonBeanFactoryLocator(resourceLocation);   
  5.        instances.put(resourceLocation, bfl);   
  6. }  

而我们使用工厂模式的时候,一般是把对象初始化好了,再给外部使用,为什么Spring这里要多此一举,在调用getInstance这后还要去调用useBeanFactory来初始化父环境?为什么Spring开发者不写成如下: 

Java代码   收藏代码
  1. BeanFactoryLocator bfl = (BeanFactoryLocator) instances.get(resourceLocation);   
  2. if (bfl == null) {   
  3.         bfl = new ContextSingletonBeanFactoryLocator(resourceLocation);  
  4.         // 下面这句可能换成 initBeanFactory 类似语句,这里只是打个比方  
  5.          bfl.useBeanFactory(parentContextKey);   
  6.         instances.put(resourceLocation, bfl);   
  7. }  

本来我认为这个写法是必须的,后来想想也不是,不过这里体现了Spring的灵活设计。如果按排上面的方法进行改造有几点不妥,1,每次都会初始化,开销比较大,可能有需求是需要延迟初始化的。2,每次都需要初始化都需要传入两个参数,分别为:配置文件名与父工厂名,3,类职责混乱,比如一个配置文件中可能定义了多个父环境的Bean,采用Spring这种方法是很清晰的: 

Java代码   收藏代码
  1. // 返回BeanFactoryLocator方便定位某个配置文件。  
  2. BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(“classpath*: parentBeanFactory.xml”);   
  3. parentContextRef1 = locator.useBeanFactory("parent1Key");  
  4. parentContextRef2 = locator.useBeanFactory("parent2Key");  

而使用我们改造的方法,则要写成如下: 

Java代码   收藏代码
  1. parentContextRef1 = ContextSingletonBeanFactoryLocator.getInstance("parentBeanFactory.xml""parent1Key");   
  2. parentContextRef2 = ContextSingletonBeanFactoryLocator.getInstance("parentBeanFactory.xml""parent2Key");  

相当麻烦且无语,引用了也只是这个配置文件中的某一个Bean的引用,没什么意义。 

这就是为什么BeanFactoryLocator接口存在的一个原因,用于查找某个配置文件中的一个BeanFactory。 

Java代码   收藏代码
  1. public interface BeanFactoryLocator {  
  2.         BeanFactoryReference useBeanFactory(String factoryKey) throws BeansException;  
  3. }  
  4. this.context = createWebApplicationContext(servletContext, parent);  


我们来看看这个函数做了些什么: 

Java代码   收藏代码
  1. protected WebApplicationContext createWebApplicationContext(   
  2.             ServletContext servletContext, ApplicationContext parent) throws BeansException {  
  3.        // 获得需要实例化的CONTEXT类名,确定ContextClass的类型。如果在web.xml中配置了contextClass这个parameter,  
  4.         // 使用这个指定的类作为ContextClass,会抛出ClassNotFound的异常。反之,使用默认的XmlWebApplicationContext  
  5.         Class contextClass = determineContextClass(servletContext);   
  6.    
  7.         // 所有的WebApplicationContext必须实现ConfigurableWebApplicationContext接口   
  8.         if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {   
  9.             throw new ApplicationContextException("Custom context class [" + contextClass.getName() +   
  10.                     "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");   
  11.         }  
  12.    
  13.         ConfigurableWebApplicationContext wac =   
  14.                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);   
  15.    
  16.        // 设置父环境   
  17.         wac.setParent(parent);   
  18.        // 设置Servlet上下文环境   
  19.         wac.setServletContext(servletContext);   
  20.        // 设置Spring配置文件路径   
  21.         wac.setConfigLocation(servletContext.getInitParameter(CONFIG_LOCATION_PARAM));   
  22.        customizeContext(servletContext, wac);   
  23.        wac.refresh();  
  24.    
  25.        return wac;   
  26. }  
  27.    
  28. protected Class determineContextClass(ServletContext servletContext) throws ApplicationContextException {  
  29.        // 获得需要实例化的CONTEXT类名,在web.xml中有设置,如果没有设置,那么为空   
  30.         String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);   
  31.        if (contextClassName != null) {   
  32.            try {   
  33.                 return ClassUtils.forName(contextClassName);   
  34.             } catch (ClassNotFoundException ex) {   
  35.                 throw new ApplicationContextException("Failed to load custom context class.", ex);   
  36.             }   
  37.        } else {  
  38.    
  39.            //如果在spring web.xml中没有设置context类位置,那么取得默认context  
  40.            //取得defaultStrategies配置文件中的WebApplicationContext属性    
  41.             contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());   
  42.            try {   
  43.                 return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());   
  44.            } catch (ClassNotFoundException ex) {   
  45.                 throw new ApplicationContextException("Failed to load default context class.", ex);   
  46.            }   
  47.        }   
  48. }  
  49.   
  50. private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";  
  51.    
  52. static {   
  53.      // Load default strategy implementations from properties file.   
  54.      // This is currently strictly internal and not meant to be customized   
  55.      // by application developers.   
  56.      try {   
  57.          ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);   
  58.          defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);   
  59.      } catch (IOException ex) {   
  60.          throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());   
  61.      }   
  62. }  
  63. // 在ContextLoader.properties里定义如下  
  64.   
  65. org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext  


再来看看Spring是如果进行初始化ApplicationContext的。就以XmlWebApplicationContext来说,它继承了ConfigurableWebApplicationContext这个接口,里面有个refresh()方法,我们可以看看它的实现(AbstractApplicationContext): 

Java代码   收藏代码
  1. public void refresh() throws BeansException, IllegalStateException {   
  2.         synchronized (this.startupShutdownMonitor) {   
  3.             // Prepare this context for refreshing.   
  4.             prepareRefresh();  
  5.    
  6.             // Tell the subclass to refresh the internal bean factory.   
  7.             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
  8.    
  9.             // Prepare the bean factory for use in this context.   
  10.             prepareBeanFactory(beanFactory);  
  11.    
  12.             try {   
  13.                 // Allows post-processing of the bean factory in context subclasses.   
  14.                 postProcessBeanFactory(beanFactory);  
  15.    
  16.                 // Invoke factory processors registered as beans in the context.   
  17.                 invokeBeanFactoryPostProcessors(beanFactory);  
  18.    
  19.                 // Register bean processors that intercept bean creation.   
  20.                 registerBeanPostProcessors(beanFactory);  
  21.    
  22.                 // Initialize message source for this context.   
  23.                 initMessageSource();  
  24.    
  25.                 // Initialize event multicaster for this context.   
  26.                 initApplicationEventMulticaster();  
  27.    
  28.                 // Initialize other special beans in specific context subclasses.   
  29.                 onRefresh();  
  30.    
  31.                 // Check for listener beans and register them.   
  32.                 registerListeners();  
  33.    
  34.                 // Instantiate all remaining (non-lazy-init) singletons.   
  35.                 finishBeanFactoryInitialization(beanFactory);  
  36.    
  37.                 // Last step: publish corresponding event.   
  38.                 finishRefresh();   
  39.             }  catch (BeansException ex) {   
  40.                 // Destroy already created singletons to avoid dangling resources.   
  41.                 beanFactory.destroySingletons();  
  42.    
  43.                 // Reset 'active' flag.   
  44.                 cancelRefresh(ex);  
  45.    
  46.                 // Propagate exception to caller.   
  47.                 throw ex;   
  48.             }   
  49.         }   
  50. }  

这个方法的实现由于涉及的东西比较多,比较国际化,事件等等,等我们理解了后续的源代码分析之后再重新过来进行研究。这样效率更高点。 

这样关于Spring在web项目中加载及初始化的方式我们大概也了解的比较清楚了,我们可以看到,Spring就第一步,加载都已经做了很多工作,不得不佩服Spring团队的智慧。 

最后,Spring加载完成之前,会将ApplicationContext放入ServletContext中,方便程序进行访问。 

Java代码   收藏代码
  1. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);   
  2. currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);  

其中WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE定义如下: 

Java代码   收藏代码
  1. String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";  


  

Spring环境 

加载组件:ContextLoaderListener 

配置路径:Servlet环境初始化参数contextConfigLocation指定的路径 

缺省路径: 没有缺省路径 

Spring环境的父环境 

加载组件:ContextLoaderListener和ContextSingletonBeanFactoryLocator 

配置路径:Servlet环境初始化参数locatorFactorySelector指定Bean工厂定位器使用的给BeanFactory,Servlet环境初始化参数parentContextKey指定Bean工厂定位器用于查找BeanFactory的关键字 

缺省路径: parentContextKey的缺省路径是classpath*:beanRefFactory.xml 

这里我们还有一个功能相近的类没有进行说明: 

ContextJndiBeanFactoryLocator 

有兴趣的同学可以自己看一下。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!