spring源码:web容器启动

  web项目中能够集成spring的ApplicationContext进行bean的管理,这样使用起来bean更加便捷,可以利用到不少spring的特性。咱们比较经常使用的web容器有jetty,tomcat,jboss等,以jetty为例,咱们看一下web容器是如何初始化和启动spring的context。web

1、Spring容器的加载spring

  在web工程中都有一个web.xml文件,jetty在启动的时候会加载这个配置文件,而且对文件中的各个listener进行加载。ContextLoaderListener继承了ServletContextListener,ServletContextListener做为ServletContext的监听者,会在ServletContext建立、销毁等过程当中监听ServletContextEvent事件,而后进行相应处理。关于这一块能够参考Spring的事件发布和监听机制:(Spring源码中的ApplicationContext的加强功能)中关于ApplicationContext做为事件发布者部分。全部的扩展点都在接受到ServletContextEvent事件时,具体的ContextLoaderListener处理ServletContextEvent代码以下:tomcat

/**
    * Initialize the root web application context.
*/
public void contextInitialized(ServletContextEvent event) {
    this.contextLoader = createContextLoader();
    this.contextLoader.initWebApplicationContext(event.getServletContext());
}

这里建立了一个contextLoader对象,ContextLoader顾名思义就是context的加载器,由它来完成context的加载:app

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

初始化web的context作了两件事情:1.查看是否指定了父容器,若是存在父容器则获取父容器;2.建立webContext,并指定父容器。laputa也使用了父子容器的指派特性,二方库plouto中负责bean加载的context做为父容器,laputa应用本身做为子容器,这样laputa就可以使用到了plouto中声明的bean(如在plouto中声明的widgetagContext,在laputa中的tagList能够顺利完成依赖注入)。以前作一个需求时,为了在tag中暴露cmsTemplateService给组件接入,把cmsTemplateService声明放到了plouto中,这样在laputa中可以更加方便引用。建立的webContext,默认给出的是XmlWebApplicationContext,关于这个类你们确定不会陌生,学习ApplicationContext的例子中会常常使用这个容器来加载xml形式的bean配置。到此,咱们得到了一个ApplicationContext,经过这个context咱们能够获取当前容器的bean以及父容器的bean。ide

2、如何在应用中使用contextpost

上述获取context后进行context存放的代码中有一段很是重要:学习

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);

  这两行代码告知了咱们如何去获取context:1.从servletContext中去拿;2.从当前的线程Map中去拿。ui

  A.servletContext中获取spring上下文。Spring对这一种获取方式作了封装:WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext sc)方法来获得WebApplicationContext:this

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
        return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
    }
————————————————————————————————————————————————————————————————————————————————————————————————————————————
public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
        Assert.notNull(sc, "ServletContext must not be null");
        Object attr = sc.getAttribute(attrName);
        if (attr == null) {
            return null;
        }
        if (attr instanceof RuntimeException) {
            throw (RuntimeException) attr;
        }
        if (attr instanceof Error) {
            throw (Error) attr;
        }
        if (attr instanceof Exception) {
            IllegalStateException ex = new IllegalStateException();
            ex.initCause((Exception) attr);
            throw ex;
        }
        if (!(attr instanceof WebApplicationContext)) {
            throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr);
        }
        return (WebApplicationContext) attr;
    }

经过servletContext获取上下文,不足之处在于开发者必须可以先得到servletContext做为入参传入,因此使用起来不是很方便。spa

从线程Map中获取spring上下文。在ContextLoader中有静态方法来获取spring上下文:

public static WebApplicationContext getCurrentWebApplicationContext() {
    return (WebApplicationContext) currentContextPerThread.get(Thread.currentThread().getContextClassLoader());
}

这种方法类比与上述方式更加便捷,再也不须要感知到servletcontext的存在。

spring获取上下文方式------实现ApplicationContextAware接口。这种方式最通用,不单单局限于web应用。咱们仅须要在用到的类中,让其继承ApplicationContextAwar接口,并实现set方法,就可以让容器在启动的时候把上下文注入到当前对象中。举例如流程引擎中的“开始节点”,获取spring容器上下文并存入PE的context中,方便后续节点可以使用spring容器:

public class InitParamNode implements  ApplicationContextAware{
    private static final String PARAM_PLUGS = "param.plugs";
    private static final String PARAM_EXTENDS = "param.extends";
    private static final String PARAM_SIGNS = "param.signs";
    private static final String SPRING_CONTEXT = "springContext";
    private ApplicationContext applicationContext;
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext=applicationContext;
         
    }
     
    public Map<String,Object> execute(PostParam postParam){
        Map<String,Object> context=new HashMap<String,Object>();
         
        context.put(SPRING_CONTEXT, this.applicationContext);
         
        if(postParam.getCommodityExtParam()!=null){
            context.put(PARAM_SIGNS, postParam.getCommodityExtParam().getSignParam());
            context.put(PARAM_EXTENDS, postParam.getCommodityExtParam().getExtendParam());
            context.put(PARAM_PLUGS, postParam.getCommodityExtParam().getPluginParam());
        }
     
         
         
        return context;
         
    }
 
}

3、

相关文章
相关标签/搜索