springMVC源码阅读

SpringMVC:
在典型的web环境中,Spring IOC容器是怎样被载入和起作用的了?简单的说,在web容器中,
通过ServletContext为Spring的IOC容器提供宿主环境,对应的建立起一个IOC容器的体系。

一般要在web.xml文件中配置一个listener:ContextLoaderListener.
这个listener实现了ServletContextListener,并重写了contextInitialized和
contextDestroyed方法,当该listenter侦听到web应用启动和关闭时会调用这两个方法。

代码如下: 

public void contextInitialized(ServletContextEvent event) {
    this.contextLoader = createContextLoader();
    this.contextLoader.initWebApplicationContext(event.getServletContext());
}

public void contextDestroyed(ServletContextEvent event) {
    if (this.contextLoader != null) {
        this.contextLoader.closeWebApplicationContext(event.getServletContext());
    }
}

 

方法很简单,初始化时实例化一个contextLoader对象,并调用initWebApplicationContext方法。
销毁时调用closeWebApplicationContext方法。

看来重点在ContextLoader这个类中!

一个重要的类:ContextLoader,加载上下文的类。

类中声明一个静态块

static {
        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());
        }
}

 

解析上述静态块:
1、此块在ContextLoader初始化时执行;
2、这句的作用是要设置defaultStrategies为org.springframework.web.context.support.XmlWebApplicationContext
3、这里涉及到读取properties文件,其实很简单:直接调用properties的load(InputStrem)方法,即可把文件中的键值对
装载到properties对象中。

spring中默认使用的日志记录器为commons-logging,所以使用spring时一定要先导入该jar.
竟然也有的代码用的log4j,乱了!

下面来看看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!");
}

 


首先在servletContext中获取属性org.springframework.web.context.WebApplicationContext.ROOT,如果存在,则抛出异常!
这个属性是做什么用的,不知道???

ApplicationContext parent = loadParentContext(servletContext);
加载父上下文,经调试,我的项目中该值为null。

 

接下来就是createWebApplicationContext:

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;
}

 

determineContextClass用来设置上下文类,一般就是默认的org.springframework.web.context.support.XmlWebApplicationContext,
然后设置ConfigurableWebApplicationContext(就是XmlWebApplicationContext)的父上下文、servletContext及配置路径,
此处的配置路径就是我们在web.xml中配置的路径:

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

 


倒数第二行代码调用了一个refresh方法,这个方法实际上是在AbstractApplicationContext这个抽象类中定义的,
XmlWebApplicationContext是该类的子孙类(这个方法找了好久,spring中的层层继承实在是太复杂了)。


具体代码如下:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();//设置开发时间startupDate,并将标志位active设置为true
        //ps:这里用到了多线程,抽时间研究一下
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);

        try {
            postProcessBeanFactory(beanFactory);
            invokeBeanFactoryPostProcessors(beanFactory);
            registerBeanPostProcessors(beanFactory);
            initMessageSource();
            initApplicationEventMulticaster();
            onRefresh();
            registerListeners();
            finishBeanFactoryInitialization(beanFactory);
            finishRefresh();
        }
        catch (BeansException ex) {
            beanFactory.destroySingletons();
            cancelRefresh(ex);
            throw ex;
        }
    }
}

 

//refresh此处水很深啊,其实是IOC中的内容,先不去管;接着往下看

回到initWebApplicationContext方法中来:
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
将刚才创建的XmlWebApplicationContext保存在servletContext局部变量中;
currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);
Thread.currentThread().getContextClassLoader()这一句是获取当前线程的上下文ClassLoader,以前总觉得web中
很少用到线程,现在看来线程无处不在啊。
接下来就是记录日志:
if (logger.isDebugEnabled()) ......
if (logger.isInfoEnabled()) ......
这里做的比较好,spring自动判断是否开始日志的debug或info模式,如果开启了才会输出日志

以上就是initWebApplicationContext的全过程了......

接下来分析closeWebApplicationContext:
源码如下:

public void closeWebApplicationContext(ServletContext servletContext) {
    servletContext.log("Closing Spring root WebApplicationContext");
    try {
        if (this.context instanceof ConfigurableWebApplicationContext) {
            ((ConfigurableWebApplicationContext) this.context).close();
        }
    }
    finally {
        currentContextPerThread.remove(Thread.currentThread().getContextClassLoader());
        servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
        if (this.parentContextRef != null) {
            this.parentContextRef.release();
        }
    }
}

 


先看这一句((ConfigurableWebApplicationContext) this.context).close();
这里close方法的具体实现如下(和refresh方法一样,仍然在AbstractApplicationContext类中):

public void close() {
    synchronized (this.startupShutdownMonitor) {//这个锁和refresh方法中的是同一个
        doClose();
        if (this.shutdownHook != null) {
            Runtime.getRuntime().removeShutdownHook(this.shutdownHook);//貌似很深奥啊,一会在深入研究
        }
    }
}

 

doClose方法的具体实现:

protected void doClose() {
    if (isActive()) {//此时为true,初始值为fasle,执行refresh()时设置true.
        if (logger.isInfoEnabled()) {
            logger.info("Closing " + this);
        }
        try {
            // Publish shutdown event.
            publishEvent(new ContextClosedEvent(this));
        }
        catch (Throwable ex) {
            logger.error("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
        }
        // Stop all Lifecycle beans, to avoid delays during individual destruction.
        Map lifecycleBeans = getLifecycleBeans();//获取所有单例bean
        for (Iterator it = new LinkedHashSet(lifecycleBeans.keySet()).iterator(); it.hasNext();) {
            String beanName = (String) it.next();
            doStop(lifecycleBeans, beanName);
        }
        // Destroy all cached singletons in the context's BeanFactory.
        destroyBeans();
        // Close the state of this context itself.
        closeBeanFactory();
        onClose();
        synchronized (this.activeMonitor) {
            this.active = false;
        }
    }
}

 

publishEvent(new ContextClosedEvent(this));这句代码调用了方法实现方式如下:

public void publishEvent(ApplicationEvent event) {
    Assert.notNull(event, "Event must not be null");
    if (logger.isTraceEnabled()) {
        logger.trace("Publishing event in context [" + getId() + "]: " + event);
    }
    getApplicationEventMulticaster().multicastEvent(event);
    if (this.parent != null) {
        this.parent.publishEvent(event);
    }
}

 未完待续...

 

 

 

 

posted @ 2013-07-04 13:12  lincolnPei  阅读(282)  评论(0编辑  收藏  举报