Tomcat啟動(dòng)分析(十一) - Wrapper組件

在Tomcat中,Wrapper組件封裝了servlet定義和參數(shù)。

創(chuàng)建過(guò)程

前面一篇文章提到ContextConfig監(jiān)聽(tīng)器響應(yīng)配置開(kāi)始事件時(shí)會(huì)解析web.xml,進(jìn)而將每個(gè)servlet定義都包裝成Wrapper,這是由Context組件的createWrapper方法實(shí)現(xiàn)的。
StandardContext類實(shí)現(xiàn)的createWrapper方法代碼如下:

@Override
public Wrapper createWrapper() {

    Wrapper wrapper = null;
    if (wrapperClass != null) {
        try {
            wrapper = (Wrapper) wrapperClass.getConstructor().newInstance();
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            log.error("createWrapper", t);
            return null;
        }
    } else {
        wrapper = new StandardWrapper();
    }

    synchronized (wrapperLifecyclesLock) {
        for (int i = 0; i < wrapperLifecycles.length; i++) {
            try {
                Class<?> clazz = Class.forName(wrapperLifecycles[i]);
                LifecycleListener listener =
                    (LifecycleListener) clazz.getConstructor().newInstance();
                wrapper.addLifecycleListener(listener);
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error("createWrapper", t);
                return null;
            }
        }
    }

    synchronized (wrapperListenersLock) {
        for (int i = 0; i < wrapperListeners.length; i++) {
            try {
                Class<?> clazz = Class.forName(wrapperListeners[i]);
                ContainerListener listener =
                        (ContainerListener) clazz.getConstructor().newInstance();
                wrapper.addContainerListener(listener);
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error("createWrapper", t);
                return null;
            }
        }
    }

    return wrapper;
}
  • StandardContext類的wrapperClass成員變量表示W(wǎng)rapper接口的實(shí)現(xiàn)類杂抽,如果沒(méi)有在配置中指定,那么默認(rèn)使用StandardWrapper韩脏;
  • 為Wrapper添加生命周期事件監(jiān)聽(tīng)器和容器事件監(jiān)聽(tīng)器缩麸。

Wrapper組件

Wrapper接口繼承了Container接口,StandardWrapper是默認(rèn)實(shí)現(xiàn)類赡矢,同其他容器組件一樣杭朱,它也繼承自ContainerBase類。

public class StandardWrapper extends ContainerBase
    implements ServletConfig, Wrapper, NotificationEmitter {

    private final Log log = LogFactory.getLog(StandardWrapper.class); // must not be static

    protected static final String[] DEFAULT_SERVLET_METHODS = new String[] {
                                                    "GET", "HEAD", "POST" };
    public StandardWrapper() {

        super();
        swValve=new StandardWrapperValve();
        pipeline.setBasic(swValve);
        broadcaster = new NotificationBroadcasterSupport();

    }

    protected long available = 0L;
    protected final AtomicInteger countAllocated = new AtomicInteger(0);

    /**
     * The facade associated with this wrapper.
     */
    protected final StandardWrapperFacade facade = new StandardWrapperFacade(this);

    protected volatile Servlet instance = null;
    protected volatile boolean instanceInitialized = false;

    /**
     * The load-on-startup order value (negative value means load on
     * first call) for this servlet.
     */
    protected int loadOnStartup = -1;

    /**
     * Mappings associated with the wrapper.
     */
    protected final ArrayList<String> mappings = new ArrayList<>();

    /**
     * The initialization parameters for this servlet, keyed by
     * parameter name.
     */
    protected HashMap<String, String> parameters = new HashMap<>();

    // 省略一些代碼

    /**
     * Multipart config
     */
    protected MultipartConfigElement multipartConfigElement = null;

    /**
     * Async support
     */
    protected boolean asyncSupported = false;
    protected boolean enabled = true;
    private boolean overridable = false;
    // 省略一些代碼
}

重要的成員變量如下:

  • instance表示W(wǎng)rapper封裝的servlet實(shí)例吹散;
  • instanceInitialized表示上述servlet實(shí)例是否已被初始化弧械;
  • loadOnStartup表示servlet配置的load-on-startup值;
  • mappings表示該servlet關(guān)聯(lián)的映射空民;
  • parameters表示該servlet的初始化參數(shù)刃唐,以參數(shù)名為鍵;
  • multipartConfigElement表示該servlet的Multipart配置。

StandardWrapper的構(gòu)造函數(shù)為自己的Pipeline添加了基本閥StandardWrapperValve画饥。

組件初始化

StandardWrapper類并沒(méi)有重寫initInternal方法衔瓮,因此它的初始化過(guò)程只是為自己創(chuàng)建了一個(gè)線程池用于啟動(dòng)和停止自己的子容器。

組件啟動(dòng)

StandardWrapper類的startInternal方法如下所示:

@Override
protected synchronized void startInternal() throws LifecycleException {

    // Send j2ee.state.starting notification
    if (this.getObjectName() != null) {
        Notification notification = new Notification("j2ee.state.starting",
                                                    this.getObjectName(),
                                                    sequenceNumber++);
        broadcaster.sendNotification(notification);
    }

    // Start up this component
    super.startInternal();

    setAvailable(0L);

    // Send j2ee.state.running notification
    if (this.getObjectName() != null) {
        Notification notification =
            new Notification("j2ee.state.running", this.getObjectName(),
                            sequenceNumber++);
        broadcaster.sendNotification(notification);
    }

}
  • 發(fā)送j2ee.state.starting通知抖甘;
  • 調(diào)用基類ContainerBase類的startInternal方法报辱,先啟動(dòng)子容器組件,然后啟動(dòng)Pipeline单山,最后發(fā)布LifecycleState.STARTING事件給添加到Host組件自身的生命周期事件監(jiān)聽(tīng)器;
  • 發(fā)送j2ee.state.running通知幅疼。

加載servlet

前面一篇文章提到StandardContext啟動(dòng)時(shí)會(huì)加載被標(biāo)識(shí)為“啟動(dòng)加載”的servlet米奸,這是通過(guò)StandardContext類的loadOnStartup方法實(shí)現(xiàn)的:

public boolean loadOnStartup(Container children[]) {

    // Collect "load on startup" servlets that need to be initialized
    TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
    for (int i = 0; i < children.length; i++) {
        Wrapper wrapper = (Wrapper) children[i];
        int loadOnStartup = wrapper.getLoadOnStartup();
        if (loadOnStartup < 0)
            continue;
        Integer key = Integer.valueOf(loadOnStartup);
        ArrayList<Wrapper> list = map.get(key);
        if (list == null) {
            list = new ArrayList<>();
            map.put(key, list);
        }
        list.add(wrapper);
    }

    // Load the collected "load on startup" servlets
    for (ArrayList<Wrapper> list : map.values()) {
        for (Wrapper wrapper : list) {
            try {
                wrapper.load();
            } catch (ServletException e) {
                getLogger().error(sm.getString("standardContext.loadOnStartup.loadException",
                        getName(), wrapper.getName()), StandardWrapper.getRootCause(e));
                // NOTE: load errors (including a servlet that throws
                // UnavailableException from the init() method) are NOT
                // fatal to application startup
                // unless failCtxIfServletStartFails="true" is specified
                if(getComputedFailCtxIfServletStartFails()) {
                    return false;
                }
            }
        }
    }
    return true;

}

loadOnStartup方法遍歷Context的子容器(即所有Wrapper),對(duì)load-on-startup屬性值大于等于0的servlet按從小到大的順序依次加載爽篷。加載由Wrapper的load方法完成悴晰。StandardWrapper類實(shí)現(xiàn)的load方法如下:

@Override
public synchronized void load() throws ServletException {
    instance = loadServlet();

    if (!instanceInitialized) {
        initServlet(instance);
    }

    if (isJspServlet) {
        StringBuilder oname = new StringBuilder(getDomain());

        oname.append(":type=JspMonitor");

        oname.append(getWebModuleKeyProperties());

        oname.append(",name=");
        oname.append(getName());

        oname.append(getJ2EEKeyProperties());

        try {
            jspMonitorON = new ObjectName(oname.toString());
            Registry.getRegistry(null, null)
                .registerComponent(instance, jspMonitorON, null);
        } catch( Exception ex ) {
            log.info("Error registering JSP monitoring with jmx " +
                        instance);
        }
    }
}

loadServlet方法會(huì)加載并初始化該Wrapper封裝的servlet。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末逐工,一起剝皮案震驚了整個(gè)濱河市铡溪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌泪喊,老刑警劉巖棕硫,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異袒啼,居然都是意外死亡哈扮,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門蚓再,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)滑肉,“玉大人,你說(shuō)我怎么就攤上這事摘仅“忻恚” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵娃属,是天一觀的道長(zhǎng)六荒。 經(jīng)常有香客問(wèn)我,道長(zhǎng)矾端,這世上最難降的妖魔是什么恬吕? 我笑而不...
    開(kāi)封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮须床,結(jié)果婚禮上铐料,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好钠惩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布柒凉。 她就那樣靜靜地躺著,像睡著了一般篓跛。 火紅的嫁衣襯著肌膚如雪膝捞。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天愧沟,我揣著相機(jī)與錄音蔬咬,去河邊找鬼。 笑死沐寺,一個(gè)胖子當(dāng)著我的面吹牛林艘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播混坞,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼狐援,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了究孕?” 一聲冷哼從身側(cè)響起啥酱,我...
    開(kāi)封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厨诸,沒(méi)想到半個(gè)月后镶殷,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡微酬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年批钠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片得封。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡埋心,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出忙上,到底是詐尸還是另有隱情拷呆,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布疫粥,位于F島的核電站茬斧,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏梗逮。R本人自食惡果不足惜项秉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望慷彤。 院中可真熱鬧娄蔼,春花似錦怖喻、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至涕癣,卻和暖如春哗蜈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坠韩。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工距潘, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留只搁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓须蜗,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親明肮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 在學(xué)習(xí)Servlet是找到一篇不錯(cuò)的文章,轉(zhuǎn)載一下柿估。學(xué)習(xí)心得,Servlet其實(shí)只是個(gè)接口,相當(dāng)于是定義了一個(gè)標(biāo)準(zhǔn)...
    君子若蓮閱讀 1,204評(píng)論 1 16
  • 0 系列目錄# WEB請(qǐng)求處理 WEB請(qǐng)求處理一:瀏覽器請(qǐng)求發(fā)起處理 WEB請(qǐng)求處理二:Nginx請(qǐng)求反向代理 本...
    七寸知架構(gòu)閱讀 13,886評(píng)論 22 190
  • Based on Java? Servlet Specification v3.1 [TOC] Servlet和S...
    0x70e8閱讀 1,305評(píng)論 0 7
  • “原則”很重要陷猫,小的時(shí)候聽(tīng)人說(shuō),講原則講原則绣檬,但心里卻沒(méi)什么概念足陨。 長(zhǎng)大后娇未,隨著更多人事物的接觸墨缘,漸漸的明白...
    馬家駒閱讀 413評(píng)論 1 0
  • 外部變量是在函數(shù)外部定義的全局變量零抬,它的作用域是從變量的定義處開(kāi)始,到本程序文件的結(jié)尾平夜。在此作用域內(nèi)蝶棋,全局變量可為...
    月震閱讀 6,352評(píng)論 2 0