论坛首页 Java企业应用论坛

Spring源代码解析(二):IoC容器在Web容器中的启动

浏览 29146 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-06-04  
以下引用自博客:http://jiwenke-spring.blogspot.com/
上面我们分析了IOC容器本身的实现,下面我们看看在典型的web环境中,Spring IOC容器是怎样被载入和起作用的。
简单的说,在web容器中,通过ServletContext为Spring的IOC容器提供宿主环境,对应的建立起一个IOC容器的体系。其中,首先需要建立的是根上下文,这个上下文持有的对象可以有业务对象,数据存取对象,资源,事物管理器等各种中间层对象。在这个上下文的基础上,和web MVC相关还会有一个上下文来保存控制器之类的MVC对象,这样就构成了一个层次化的上下文结构。在web容器中启动Spring应用程序就是一个建立这个上下文体系的过程。Spring为web应用提供了上下文的扩展接口
WebApplicationContext:
public interface WebApplicationContext extends ApplicationContext {
    //这里定义的常量用于在ServletContext中存取根上下文
    String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
    ......
    //对WebApplicationContext来说,需要得到Web容器的ServletContext
    ServletContext getServletContext();
}

而一般的启动过程,Spring会使用一个默认的实现,XmlWebApplicationContext - 这个上下文实现作为在web容器中的根上下文容器被建立起来,具体的建立过程在下面我们会详细分析。
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {

    /** 这是和web部署相关的位置信息,用来作为默认的根上下文bean定义信息的存放位置*/
    public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
    public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
    public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
   
    //我们又看到了熟悉的loadBeanDefinition,就像我们前面对IOC容器的分析中一样,这个加载工程在容器的refresh()的时候启动。
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
        //对于XmlWebApplicationContext,当然使用的是XmlBeanDefinitionReader来对bean定义信息来进行解析
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

    protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
    }
    //使用XmlBeanDefinitionReader来读入bean定义信息
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            for (int i = 0; i < configLocations.length; i++) {
                reader.loadBeanDefinitions(configLocations[i]);
            }
        }
    }
    //这里取得bean定义信息位置,默认的地方是/WEB-INF/applicationContext.xml
    protected String[] getDefaultConfigLocations() {
        if (getNamespace() != null) {
            return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
        }
        else {
            return new String[] {DEFAULT_CONFIG_LOCATION};
        }
    }
}

对于一个Spring激活的web应用程序,可以通过使用Spring代码声明式的指定在web应用程序启动时载入应用程序上下文(WebApplicationContext),Spring的ContextLoader是提供这样性能的类,我们可以使用 ContextLoaderServlet或者ContextLoaderListener的启动时载入的Servlet来实例化Spring IOC容器 - 为什么会有两个不同的类来装载它呢,这是因为它们的使用需要区别不同的Servlet容器支持的Serlvet版本。但不管是 ContextLoaderSevlet还是 ContextLoaderListener都使用ContextLoader来完成实际的WebApplicationContext的初始化工作。这个ContextLoder就像是Spring Web应用程序在Web容器中的加载器booter。当然这些Servlet的具体使用我们都要借助web容器中的部署描述符来进行相关的定义。
下面我们使用ContextLoaderListener作为载入器作一个详细的分析,这个Servlet的监听器是根上下文被载入的地方,也是整个 Spring web应用加载上下文的第一个地方;从加载过程我们可以看到,首先从Servlet事件中得到ServletContext,然后可以读到配置好的在web.xml的中的各个属性值,然后ContextLoder实例化WebApplicationContext并完成其载入和初始化作为根上下文。当这个根上下文被载入后,它被绑定到web应用程序的ServletContext上。任何需要访问该ApplicationContext的应用程序代码都可以从WebApplicationContextUtils类的静态方法来得到:
    WebApplicationContext getWebApplicationContext(ServletContext sc)

以Tomcat作为Servlet容器为例,下面是具体的步骤:
1.Tomcat 启动时需要从web.xml中读取启动参数,在web.xml中我们需要对ContextLoaderListener进行配置,对于在web应用启动入口是在ContextLoaderListener中的初始化部分;从Spring MVC上看,实际上在web容器中维护了一系列的IOC容器,其中在ContextLoader中载入的IOC容器作为根上下文而存在于 ServletContext中。
    //这里对根上下文进行初始化。
    public void contextInitialized(ServletContextEvent event) {
        //这里创建需要的ContextLoader
        this.contextLoader = createContextLoader();
        //这里使用ContextLoader对根上下文进行载入和初始化
        this.contextLoader.initWebApplicationContext(event.getServletContext());
    }

通过ContextLoader建立起根上下文的过程,我们可以在ContextLoader中看到:
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
            throws IllegalStateException, BeansException {
        //这里先看看是不是已经在ServletContext中存在上下文,如果有说明前面已经被载入过,或者是配置文件有错误。
        if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
        //直接抛出异常
        .........
        }
      
        ...............
        try {
            // 这里载入根上下文的父上下文
            ApplicationContext parent = loadParentContext(servletContext);

            //这里创建根上下文作为整个应用的上下文同时把它存到ServletContext中去,注意这里使用的ServletContext的属性值是
            //ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,以后的应用都是根据这个属性值来取得根上下文的 - 往往作为自己上下文的父上下文
            this.context = createWebApplicationContext(servletContext, parent);
            servletContext.setAttribute(
                    WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
            ..........

            return this.context;
        }
           ............
    }

建立根上下文的父上下文使用的是下面的代码,取决于在web.xml中定义的参数:locatorFactorySelector,这是一个可选参数:
    protected ApplicationContext loadParentContext(ServletContext servletContext)
            throws BeansException {

        ApplicationContext parentContext = null;

        String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
        String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);

        if (locatorFactorySelector != null) {
            BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
            ........
            //得到根上下文的父上下文的引用
            this.parentContextRef = locator.useBeanFactory(parentContextKey);
            //这里建立得到根上下文的父上下文
            parentContext = (ApplicationContext) this.parentContextRef.getFactory();
        }

        return parentContext;
    }

得到根上下文的父上下文以后,就是根上下文的创建过程:
    protected WebApplicationContext createWebApplicationContext(
            ServletContext servletContext, ApplicationContext parent) throws BeansException {
        //这里需要确定我们载入的根WebApplication的类型,由在web.xml中配置的contextClass中配置的参数可以决定我们需要载入什么样的ApplicationContext,
        //如果没有使用默认的。
        Class contextClass = determineContextClass(servletContext);
        .........
        //这里就是上下文的创建过程
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
        //这里保持对父上下文和ServletContext的引用到根上下文中
        wac.setParent(parent);
        wac.setServletContext(servletContext);

        //这里从web.xml中取得相关的初始化参数
        String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
        if (configLocation != null) {
            wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,
                    ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));
        }
       //这里对WebApplicationContext进行初始化,我们又看到了熟悉的refresh调用。
        wac.refresh();
        return wac;
    }

初始化根ApplicationContext后将其存储到SevletContext中去以后,这样就建立了一个全局的关于整个应用的上下文。这个根上下文会被以后的DispatcherServlet初始化自己的时候作为自己ApplicationContext的父上下文。这个在对 DispatcherServlet做分析的时候我们可以看看到。

3.完成对ContextLoaderListener的初始化以后, Tomcat开始初始化DispatchServlet,- 还记得我们在web.xml中队载入次序进行了定义。DispatcherServlet会建立自己的ApplicationContext,同时建立这个自己的上下文的时候会从ServletContext中得到根上下文作为父上下文,然后再对自己的上下文进行初始化,并最后存到 ServletContext中去供以后检索和使用。
可以从DispatchServlet的父类FrameworkServlet的代码中看到大致的初始化过程,整个ApplicationContext的创建过程和ContextLoder创建的过程相类似:
    protected final void initServletBean() throws ServletException, BeansException {
        .........
        try {
            //这里是对上下文的初始化过程。
            this.webApplicationContext = initWebApplicationContext();
            //在完成对上下文的初始化过程结束后,根据bean配置信息建立MVC框架的各个主要元素
            initFrameworkServlet();
        }
       ........
    }

对initWebApplicationContext()调用的代码如下:
    protected WebApplicationContext initWebApplicationContext() throws BeansException {
        //这里调用WebApplicationContextUtils静态类来得到根上下文
        WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
       
        //创建当前DispatcherServlet的上下文,其上下文种类使用默认的在FrameworkServlet定义好的:DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
        WebApplicationContext wac = createWebApplicationContext(parent);
        ........
        if (isPublishContext()) {
            //把当前建立的上下文存到ServletContext中去,注意使用的属性名是和当前Servlet名相关的。
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
        }
        return wac;
    }

其中我们看到调用了WebApplicationContextUtils的静态方法得到根ApplicationContext:
    public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
        //很简单,直接从ServletContext中通过属性名得到根上下文
        Object attr = sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
        .......
        return (WebApplicationContext) attr;
    }
然后创建DispatcherServlet自己的WebApplicationContext:
    protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)
            throws BeansException {
        .......
        //这里使用了BeanUtils直接得到WebApplicationContext,ContextClass是前面定义好的DEFAULT_CONTEXT_CLASS =                           
        //XmlWebApplicationContext.class;
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());

        //这里配置父上下文,就是在ContextLoader中建立的根上下文
        wac.setParent(parent);

        //保留ServletContext的引用和相关的配置信息。
        wac.setServletContext(getServletContext());
        wac.setServletConfig(getServletConfig());
        wac.setNamespace(getNamespace());

        //这里得到ApplicationContext配置文件的位置
        if (getContextConfigLocation() != null) {
            wac.setConfigLocations(
                StringUtils.tokenizeToStringArray(
                            getContextConfigLocation(), ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));
        }
       
        //这里调用ApplicationContext的初始化过程,同样需要使用refresh()
        wac.refresh();
        return wac;
    }

4. 然后就是DispatchServlet中对Spring MVC的配置过程,首先对配置文件中的定义元素进行配置 - 请注意这个时候我们的WebApplicationContext已经建立起来了,也意味着DispatcherServlet有自己的定义资源,可以需要从web.xml中读取bean的配置信息,通常我们会使用单独的xml文件来配置MVC中各个要素定义,这里和web容器相关的加载过程实际上已经完成了,下面的处理和普通的Spring应用程序的编写没有什么太大的差别,我们先看看MVC的初始化过程:
    protected void initFrameworkServlet() throws ServletException, BeansException {
        initMultipartResolver();
        initLocaleResolver();
        initThemeResolver();
        initHandlerMappings();
        initHandlerAdapters();
        initHandlerExceptionResolvers();
        initRequestToViewNameTranslator();
        initViewResolvers();
    }

5. 这样MVC的框架就建立起来了,DispatchServlet对接受到的HTTP Request进行分发处理由doService()完成,具体的MVC处理过程我们在doDispatch()中完成,其中包括使用Command模式建立执行链,显示模型数据等,这些处理我们都可以在DispatcherServlet的代码中看到:
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ......
        try {
            doDispatch(request, response);
        }
       .......
    }

实际的请求分发由doDispatch(request,response)来完成:
  protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {
       .......
       // 这是Spring定义的执行链,里面放了映射关系对应的handler和定义的相关拦截器。
       HandlerExecutionChain mappedHandler = null;
     
        ......
        try {
            //我们熟悉的ModelAndView在这里出现了。
            ModelAndView mv = null;
            try {
                processedRequest = checkMultipart(request);

                //这里更具request中的参数和映射关系定义决定使用的handler
                mappedHandler = getHandler(processedRequest, false);

                ......
                //这里是handler的调用过程,类似于Command模式中的execute.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                .......
            //这里将模型数据通过视图进行展现
            if (mv != null && !mv.wasCleared()) {
                render(mv, processedRequest, response);
            }
              ........
    }

这样具体的MVC模型的实现就由bean配置文件里定义好的view resolver,handler这些类来实现用户代码的功能。
总结上面的过程,我们看到在web容器中,ServletContext可以持有一系列的web上下文,而在整个web上下文中存在一个根上下文来作为其它 Servlet上下文的父上下文。这个根上下文是由ContextLoader载入并进行初始化的,对于我们的web应用, DispatcherSerlvet载入并初始化自己的上下文,这个上下文的父上下文是根上下文,并且我们也能从ServletContext中根据 Servlet的名字来检索到我们需要的对应于这个Servlet的上下文,但是根上下文的名字是由Spring唯一确定的。这个 DispactcherServlet建立的上下文就是我们开发Spring MVC应用的IOC容器。
具体的web请求处理在上下文体系建立完成以后由DispactcherServlet来完成,上面对MVC的运作做了一个大致的描述,下面我们会具体就SpringMVC的框架实现作一个详细的分析。
   发表时间:2007-06-08  
好文章,其实讲IOC容器,其实说的是Spring管理Bean的功能,并不一定是像WEB容器,EJB容器这样的东西;Spring初始化BeanFactory,是通过web容器来加载的,这个倒是非常正常。
0 请登录后投票
   发表时间:2007-06-09  
hiwzg 写道
好文章,其实讲IOC容器,其实说的是Spring管理Bean的功能,并不一定是像WEB容器,EJB容器这样的东西;Spring初始化BeanFactory,是通过web容器来加载的,这个倒是非常正常。

对的,实际上就是怎样在不同环境中加载Spring应用程序的过程。就像我们看到的boot过程一样。Spring IOC容器和web容器,EJB容器没有直接的关系,而且它本身就可以独立的被作为JVM的应用程序来使用。这样Spring作为胶水的优势就很明显了,什么资源都可以黏上。然后业务应用和IOC容器打交道就行了 - 在这个程度上说自己简化了J2EE开发。

0 请登录后投票
   发表时间:2007-06-21  
重新更新了一下,和IOC容器分析的知识做了一下结合,在Spring中的WebApplicationContext是为了在web容器中更好使用而设计的IOC容器 - 里面我们看到很多和web容器相关的特定的东西。
然后就是这些特定的IOC容器以什么样的方式和过程在web容器中建立起来的问题。
0 请登录后投票
   发表时间:2007-07-04  
又看了遍代码,虽然还没有完全看明白,尤其是那个ContextLoader中的loadParentContext方法.当然,大致思想已经明白了.

在web.xml中配置1个ContextLoaderListener,而这个定义的ContextLoaderListener以下简称context其实就被作为根上下文来处理了,因为所谓"根",其实有且只有1个.

从ContextLoader的initWebApplicationContext方法中可以看到

if (servletContext
				.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - "
							+ "check whether you have multiple ContextLoader* definitions in your web.xml!");
		}


这里的getAttribute,当然会有对应的setAttribute,这个setAttribute的操作在该方法的返回前完成.

在createWebApplicationContext中,类似进行了加载CONFIG_LOCATION_PARAM的动作,初步推断为,这个根上下文来管理多个子上下文.当然这里的加载也仅仅对资源定义的加载.

	/** Default config location for the root context */
	public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";


从以上代码里也可以看出,WebApplicationContext也可以通过配置来完成,spring给的默认实现是XmlWebApplicationContext.

在createWebApplicationContext,将parent context设置到根上下文中.但是这个parent是如何而来,就是我未能理解的地方.可能作者的意图是,将所有的context,包括父contex以及从configLocation中可能获取到的定义进行集中管理.
0 请登录后投票
   发表时间:2007-07-05  
bennyparlo 写道
又看了遍代码,虽然还没有完全看明白,尤其是那个ContextLoader中的loadParentContext方法.当然,大致思想已经明白了.

总的来说,在web容器中启动Spring MVC应用程序的过程主要就是建立一系列上下文的过程,在这里有一个上下文体系被建立起来,具体的说一个web应用对应一个web.xml,这里面定义的 ContextLoader载入这个应用的根上下文 - 这个根上上文也是可以设置自己的父上下文的,只是一般不设而已,这里就是下面要讨论的地方。
然后是对每个DispatchServlet建立自己上下文的过程,每个Servlet的都建立起自己对应的上下文并且把web应用ContextLoader建立的根上下文作为自己上下文的父上下文,业就是说不同的Servlet之间是可以通过共享根上下文来共享bean的使用的 - 只要把这些需要共享的bean用根上下文中定义就好了。值得注意的是这个根上下文对应的是这个web应用,也就是说这些共享bean的共享范围是在这个web应用中的。
0 请登录后投票
   发表时间:2007-07-05  
下面就是我们要讨论的问题:
在web应用根上下文建立的时候,是可以对它设置父上下文的,在ContextLoader中:
    protected ApplicationContext loadParentContext(ServletContext servletContext)
            throws BeansException {

        ApplicationContext parentContext = null;
        //这里读取在web.xml中配置的参数
        String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
        String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);
       
        //这里得到一个Locator实际上是一个管理IOC容器的容器
        if (locatorFactorySelector != null) {
            BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);

            if (logger.isInfoEnabled()) {
                logger.info("Getting parent context definition: using parent context key of '" +
                        parentContextKey + "' with BeanFactoryLocator");
            }
            //这里从Locator容器中得到需要的IOC容器作为根上下文的父上下文,这个IOC容器的取得通过配置LOCATOR_FACTORY_KEY_PARAM属性
            this.parentContextRef = locator.useBeanFactory(parentContextKey);
            parentContext = (ApplicationContext) this.parentContextRef.getFactory();
        }

        return parentContext;
    }

问题在这个BeanFactory的作用上,看到在reference guide和Java Doc中的说明:
在reference3.9中提到这个Selector的使用:
As another example, in a complex J2EE apps with multiple layers (various JAR files, EJBs, and WAR files packaged as an EAR), with each layer having its own Spring IoC container definition (effectively forming a hierarchy), the preferred approach when there is only one web-app (WAR) in the top hierarchy is to simply create one composite Spring IoC container from the multiple XML definition files from each layer. All of the various Spring IoC container implementations may be constructed from multiple definition files in this fashion. However, if there are multiple sibling web-applications at the root of the hierarchy, it is problematic to create a Spring IoC container for each web-application which consists of mostly identical bean definitions from lower layers, as there may be issues due to increased memory usage, issues with creating multiple copies of beans which take a long time to initialize (e.g. a Hibernate SessionFactory), and possible issues due to side-effects. As an alternative, classes such as ContextSingletonBeanFactoryLocator or SingletonBeanFactoryLocator may be used to demand-load multiple hierarchical (that is one container is the parent of another) Spring IoC container instances in a singleton fashion, which may then be used as the parents of the web-application Spring IoC container instances. The result is that bean definitions for lower layers are loaded only as needed, and loaded only once.

这里的大致意思是说如果在一个复杂的J2EE应用中怎样简化IOC配置的问题,可以通过这个Locator为多个web应用或者是多层IOC定义一个顶层的IOC容器,这样的话可以只用对多个配置文件读取一次,同时这个顶层IOC容器中管理的bean都是可以共享的 - 而且这个顶层的IOC容器是以单例形式被使用的。在JAVA Doc中给出了使用的例子:
首先配置在顶层IOC需要的定义文件,实际上是配置了一个IOC容器作为这个Locator的bean:
<?xml version="1.0" encoding="UTF-8"?>

 <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans_2_0.dtd">
 
 <beans>
 
   <bean id="com.mycompany.myapp"
         class="org.springframework.context.support.ClassPathXmlApplicationContext">
     <constructor-arg>
       <list>
         <value>com/mycompany/myapp/util/applicationContext.xml</value>
         <value>com/mycompany/myapp/dataaccess/applicationContext.xml</value>
         <value>com/mycompany/myapp/dataaccess/services.xml</value>
       </list>
     </constructor-arg>
   </bean>
 
 </beans>

然后就是对这个Locator的使用,如果去看一下ContextLoader的代码也是这样使用的:
 BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();

 BeanFactoryReference bf = bfl.useBeanFactory("com.mycompany.myapp");
 // now use some bean from factory 
 MyClass zed = bf.getFactory().getBean("mybean");

在ContextLoader中的使用是这样的:
        if (locatorFactorySelector != null) {

            BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);



            if (logger.isInfoEnabled()) {

                logger.info("Getting parent context definition: using parent context key of '" +

                        parentContextKey + "' with BeanFactoryLocator");

            }

              //这里从Locator容器中得到需要的IOC容器作为根上下文的父上下文,这个IOC容器的取得通过配置LOCATOR_FACTORY_KEY_PARAM属性

            this.parentContextRef = locator.useBeanFactory(parentContextKey);

            parentContext = (ApplicationContext) this.parentContextRef.getFactory();

        }

从这点看,这个管理IOC容器的容器还是很方便的使用的 - 而且是单例的形式,这样为我们管理那些公用的bean提供了很多便利,
换句话说,如果灵活使用的话,我们可以通过这个管理容器的容器根据需要选择我们的需要到的容器,也就是选择需要的bean - 不一定在顶层去作为根上下文的父上下文。
但经过讨论,我还是不太明白这个Locator怎样能够在跨web应用做到一个单例的使用?因为一个web应用启动时就会维护一个自己的ContextLoader,这里就会有一个自己的Locator,也就是以后在这个应用的范围内使用这个Locator,所以难道这个Locator只能在一个Web应用中使用来管理IOC容器?但是reference guide中的这段话又怎么理解呢?

However, if there are multiple sibling web-applications at the root of the hierarchy, it is problematic to create a Spring IoC container for each web-application which consists of mostly identical bean definitions from lower layers,as there may be issues due to increased memory usage, issues with creating multiple copies of beans which take a long time to initialize (e.g. a Hibernate SessionFactory), and possible issues due to side-effects.

难道这个Locator的作用范围的确只能在一个web应用中为多层次的IOC定义提供服务?








0 请登录后投票
   发表时间:2007-07-05  
大致的意思说,很难从每个兄弟的web应用中取得各自的bean定义,为他们创建个公用的ioc容器.因为那样做需要花费大量的时间和空间.

那这样看来,这个Locator就是ContextLoaderListener的父上下文集合.同时ContextLoaderListener也作为所有servlet的父上下文.

但现在问题又来了,ContextLoaderListener可以创建自己的上下文以及上下文的bean定义.但这里又搞个根上下文的父上下文,这个又为什么意图呢?
0 请登录后投票
   发表时间:2007-07-05  
bennyparlo 写道
大致的意思说,很难从每个兄弟的web应用中取得各自的bean定义,为他们创建个公用的ioc容器.因为那样做需要花费大量的时间和空间.

这里创建的共用IOC容器来共用这个容器里面的bean,来避免重复的创建过程。比如像Hibernate的SessionFactoryBean
引用

那这样看来,这个Locator就是ContextLoaderListener的父上下文集合.同时ContextLoaderListener也作为所有servlet的父上下文.

这个Locator提供的是一个上下文的管理环境,也就是上下文容器,应用程序可以根据需要选自己需要的上下文- 作不作父上下文是ContextLoader来决定的,也就是作什么用Locator不管,而由客户程序决定。在ContextLoader是做为web应用的根上下文来使用 - 根据属性配置选出想要的上下文。
引用

但现在问题又来了,ContextLoaderListener可以创建自己的上下文以及上下文的bean定义.但这里又搞个根上下文的父上下文,这个又为什么意图呢?

我想这里是不是想在不同的应用之间共享IOC工厂?但搞不清楚怎样能做到这个单例的Locator共享。因为似乎不同的ContextLoader都会维护一个。或者问题可以转化成,在不同的web应用之间在一个web服务器中怎样可以共享JAVA对象呢?
看了一下TOMCAT的类装载器的介绍,如果加载的时候,使用的是web应用的公共类装载起来载入这个Locator就能在不同的web应用之间共享这个单例对象,也就是说在部署spring.jar的时候,需要把这个包放在common/share下面而不是当前自己的web应用里面。这个应该算是在使用locator的时候的一个小小注意事项吧。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics