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、

相關文章
相關標籤/搜索