乐在其中

以JEE为主攻,以Flex为点缀,以Eclipse RCP为乐趣
请访问http://www.inframesh.org

首页 新随笔 联系 管理
  43 Posts :: 0 Stories :: 8 Comments :: 0 Trackbacks

Spring 和 Struts在web.xml中增加的配置:

   1: <!-- spring的配置 -->
   2: <context-param>
   3:     <param-name>contextConfigLocation</param-name>
   4:     <param-value>classpath:/SpringContext/applicationContext-web.xml</param-value>
   5: </context-param>    
   7:  
   8: <listener>
   9:     <listener-class>org.springframework.web.context.ContextLoaderListener
  10:     </listener-class>
  11: </listener>
  12:  
  13: <filter>
  14:     <filter-name>struts2</filter-name>
  15:     <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
  17:     <init-param>
  18:         <param-name>config</param-name>
  19:         <param-value>struts-default.xml,struts-plugin.xml,struts/struts.xml</param-value>
  20:     </init-param>
  21: </filter>
  22:  
  23: <filter-mapping>
  24:     <filter-name>struts2</filter-name>
  25:     <url-pattern>*.do</url-pattern>
  26: </filter-mapping>

 

第一个tag定义的是spring的配置文件地址到环境参数(context parameter)

第二个tag定义一个listener为org.springframework.web.context.ContextLoaderListener,这里相当于j2ee容器给我们提供的main函数的切入点,可以让我们做一些系统初始化的工作,需要实现的类是:javax.servlet.ServletContextListener

第三个tag则定义了struts2的一个filter。Filter则是对每次请求(可以通过filter-mapping指定)做过滤处理,请求首先请过filter链的处理,然后再到HttpServlet的init方法。对应的类是:javax.servlet.Filter。上面先配置了一个filter,对应的类是org.apache.struts2.dispatcher.FilterDispatcher,参数则是struts的配置文件位置

第四个参数定义了filter怎样行为,显然它对.do为后缀的请求应用struts2这个名称的filter

这里需要首先搞清楚servlet规范中什么是listener?
 
详细请参见 Servlet Listener和Filter

 

查看ContextLoaderListener可知,它正好继承了javax.servlet.ServletContextListener,用于监听javax.servlet.ServletContextEvent事件

/*
 * Copyright 2002-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package org.springframework.web.context;
 
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
 
/**
 * Bootstrap listener to start up Spring's root {@link WebApplicationContext}.
 * Simply delegates to {@link ContextLoader}.
 *
 * <p>This listener should be registered after
 * {@link org.springframework.web.util.Log4jConfigListener}
 * in <code>web.xml</code>, if the latter is used.
 *
 * @author Juergen Hoeller
 * @since 17.02.2003
 * @see ContextLoaderServlet
 * @see org.springframework.web.util.Log4jConfigListener
 */
public class ContextLoaderListener implements ServletContextListener {
 
    private ContextLoader contextLoader;
 
 
    /**
     * Initialize the root web application context.
     */
    public void contextInitialized(ServletContextEvent event) {
        this.contextLoader = createContextLoader();
        this.contextLoader.initWebApplicationContext(event.getServletContext());
    }
 
    /**
     * Create the ContextLoader to use. Can be overridden in subclasses.
     * @return the new ContextLoader
     */
    protected ContextLoader createContextLoader() {
        return new ContextLoader();
    }
 
    /**
     * Return the ContextLoader used by this listener.
     * @return the current ContextLoader
     */
    public ContextLoader getContextLoader() {
        return this.contextLoader;
    }
 
 
    /**
     * Close the root web application context.
     */
    public void contextDestroyed(ServletContextEvent event) {
        if (this.contextLoader != null) {
            this.contextLoader.closeWebApplicationContext(event.getServletContext());
        }
    }
 
}

此类implement了ServletContextListener的两个方法:

public void contextInitialized(ServletContextEvent event);
public void contextDestroyed(ServletContextEvent event);

分别做context的初始化和销毁

另外提供了一个protected方法:protected ContextLoader createContextLoader()  用于创建真正做事情的代理类CotextLoader

和一个public方法:public ContextLoader getContextLoader();

 

可见,这个Listener类直接将工作代理给了ContextLoader类了

 

____________________________________________________________________________________________________________________________

我们按图索骥,下面再分析org.springframework.web.context.ContextLoader

ContextLoader里面有个私有成员:private WebApplicationContext context

此变量提供了create方法:

createWebApplicationContext(ServletContext servletContext, ApplicationContext parent)

因为Spring提供了多种WebApplicationContext类,所以需要一个方法来决定使用哪个WebApplicationContextContext类

protected Class determineContextClass(ServletContext servletContext) throws ApplicationContextException {
        String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
        if (contextClassName != null) {
            try {
                return ClassUtils.forName(contextClassName);
            }
            catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load custom context class [" + contextClassName + "]", ex);
            }
        }
        else {
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
            try {
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            }
            catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load default context class [" + contextClassName + "]", ex);
            }
        }
    }

 

public static final String CONTEXT_CLASS_PARAM = "contextClass";

所以如果在web.xml中的<context-param> </context-param>中定义了参数contextClass,那么直接就决定了用此Context类

否则,就应用缺省策略来决定使用哪个Context类。

缺省策略如下:

private static final Properties defaultStrategies;
 
static {
    // Load default strategy implementations from properties file.
    // This is currently strictly internal and not meant to be customized
    // by application developers.
    try {
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
        throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
    }
}

实际上,缺省策略从org\springframework\web\context\ContextLoader.properties 文件中取得属性org.springframework.web.context.WebApplicationContext

我们看看ContextLoader.properties文件的内容:

# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.
 
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

显然,缺省的Context类就是 XmlWebApplicationContext类

!!!真累啊,Spring...

 

OK,总算知道用哪个Context类了,那么现在到了create这个Context实例的时候了

protected WebApplicationContext createWebApplicationContext(
            ServletContext servletContext, ApplicationContext parent) throws BeansException {
 
        Class contextClass = determineContextClass(servletContext);
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                    "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        }
 
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
        wac.setParent(parent);
        wac.setServletContext(servletContext);
        wac.setConfigLocation(servletContext.getInitParameter(CONFIG_LOCATION_PARAM));
        customizeContext(servletContext, wac);
        wac.refresh();
 
        return wac;
    }

这里很容易理解,只不过它的创建不是直接new,而是封装了一层,调用BeanUtils.instantiateClass()工具方法

接下来设定WebApplicationCcontext实例的parent, servletContext,

其中配置文件位置(web.xml中)的contextConfigLocation参数指定的

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:/SpringContext/applicationContext-web.xml
    </param-value>
</context-param>

倒数第二行调用的customizeContext()方法目前Spring实现代码是空的,估计留作以后(看来万事要留余地啊^_^)

最后一件事就是调用WebApplicationContext的refresh()方法。(这个方法是个stratup方法,很重要。他干的事情后面会着重涉及)

 

最后就是真正做事的initWebApplicationContext()方法:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
            throws IllegalStateException, BeansException {
 
        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!");
        }
 
        servletContext.log("Initializing Spring root WebApplicationContext");
        if (logger.isInfoEnabled()) {
            logger.info("Root WebApplicationContext: initialization started");
        }
        long startTime = System.currentTimeMillis();
 
        try {
            // Determine parent for root web application context, if any.
            ApplicationContext parent = loadParentContext(servletContext);
 
            // Store context in local instance variable, to guarantee that
            // it is available on ServletContext shutdown.
            this.context = createWebApplicationContext(servletContext, parent);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
            currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);
 
            if (logger.isDebugEnabled()) {
                logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
                        WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
            }
            if (logger.isInfoEnabled()) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
            }
 
            return this.context;
        }
        catch (RuntimeException ex) {
            logger.error("Context initialization failed", ex);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
            throw ex;
        }
        catch (Error err) {
            logger.error("Context initialization failed", err);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
            throw err;
        }
    }

它其实做得事情很简单,调用loadParentContext()方法获取父context,调用createWebApplicationContext()创建WebApplicationContext实例

 

一切都似乎完成了,至少我们明白了配置文件是如何加载进去的,至于IoC容器如何帮你注入配置文件中的bean,下次再探索。线索是什么呢?你还记得在createWebApplication()的最后一步做什么了吗?refresh(), 对了,这个就是你作为职业"嘿客"要探索的下一个线索

posted on 2008-12-26 16:53 suprasoft Inc,. 阅读(2630) 评论(1)  编辑  收藏 所属分类: J2EE

Feedback

# re: 读Spring源代码之按图索骥(一)Context创建与配置文件加载 2013-10-30 21:08 马小龙
真的很不错  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航:
博客园   IT新闻   Chat2DB   C++博客   博问  
 
©2005-2008 Suprasoft Inc., All right reserved.