您好,登錄后才能下訂單哦!
經(jīng)過(guò)死磕Tomcat7源碼之一:解析web.xml,已經(jīng)知道webapp的配置信息是如何解析到內(nèi)存中。接下來(lái),就是如何將對(duì)應(yīng)的組件對(duì)象初始化化。分析所有的組件初始化過(guò)程,根本不可能。本文重點(diǎn)針對(duì)闡明3個(gè)主要組件的初始化過(guò)程,分別是:servlet,listener,filter。通過(guò)本文,你可以掌握以下知識(shí)點(diǎn)
了解組件初始化調(diào)用序列
組件servlet,listener,filter組件的初始化順序
listener的初始化過(guò)程
servlet的初始化過(guò)程
filter的初始化過(guò)程
1.組件初始化序列
通過(guò)《解析web.xml》,我們可以了解,tomcat在自動(dòng)完成webapp應(yīng)用的部署時(shí),完成了web.xml信息的解析,也就是說(shuō)webapp組件配置元信息,tomcat已經(jīng)拿到了。接下來(lái),就是根據(jù)配置的元信息規(guī)則,初始化組件。
通過(guò)上圖,知道組件對(duì)象的初始化主要從StandardContext完成的, StandardContext對(duì)應(yīng)著耳熟能詳?shù)膽?yīng)用,比如webapp目錄下的 docs,ROOT,manager...。
1組件初始化過(guò)程使用的是線程啟動(dòng)的。
代碼參考o(jì)rg.apache.catalina.core.ContainerBase.startInternal
List<Future<Void>> results = new ArrayList<Future<Void>>(); for (int i = 0; i < children.length; i++) { results.add(startStopExecutor.submit(new StartChild(children[i]))); } boolean fail = false; for (Future<Void> result : results) { try { result.get(); } catch (Exception e) { log.error(sm.getString("containerBase.threadedStartFailed"), e); fail = true; } }
2.組件servlet,listener,filter組件的初始化順序
筆者,創(chuàng)建了1個(gè)名為"helloapp"的webapp應(yīng)用,分別聲明了多個(gè)lister,多個(gè)servlet,filter。通過(guò)多次調(diào)整各組件在web.xml中的相對(duì)順序。得出如下結(jié)論。
組件執(zhí)行順序按Listener,F(xiàn)ilter,Servlet進(jìn)行。
Servlet的load-on-startup影響Servlet的啟動(dòng)順序,詳情見(jiàn)2.1節(jié)說(shuō)明
Filter之間的初始化順序,與<filter-name>中的字符排序規(guī)則有關(guān),經(jīng)測(cè)試與默認(rèn)排序規(guī)則相反。
Listener之間的初始化順序,與在web.xml聲明的順序一致。這一點(diǎn)非常重要,如果使用一些mvc框架,安全框架時(shí),如果使用Listener來(lái)完成過(guò)濾攔截的話,一定要注意Listener的聲明順序。
2.1servlet中l(wèi)oad-on-startup的規(guī)則說(shuō)明
標(biāo)記容器是否在啟動(dòng)的時(shí)候就加載這個(gè)servlet。
當(dāng)值為0或者大于0時(shí),表示容器在應(yīng)用啟動(dòng)時(shí)就加載這個(gè)servlet;
當(dāng)是一個(gè)負(fù)數(shù)時(shí)或者沒(méi)有指定時(shí),則指示容器在該servlet被選擇時(shí)才加載。
正數(shù)的值越小,啟動(dòng)該servlet的優(yōu)先級(jí)越高。
3.servlet的初始化過(guò)程
org.apache.catalina.core.StandardWrapper
public boolean loadOnStartup(Container children[]) { // Collect "load on startup" servlets that need to be initialized TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<Integer, ArrayList<Wrapper>>(); for (int i = 0; i < children.length; i++) { Wrapper wrapper = (Wrapper) children[i]; int loadOnStartup = wrapper.getLoadOnStartup(); //如果小于0,跳過(guò) if (loadOnStartup < 0) continue; Integer key = Integer.valueOf(loadOnStartup); ArrayList<Wrapper> list = map.get(key); if (list == null) { list = new ArrayList<Wrapper>(); 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; }
通過(guò)源碼分析,我們清楚了load-on-startup 小于0時(shí),表示servlet不需要在啟動(dòng)時(shí)初始化。
protected volatile boolean instanceInitialized = false; public synchronized void load() throws ServletException { instance = loadServlet(); if (!instanceInitialized) { initServlet(instance); } if (isJspServlet) { // } }
通過(guò)分析load()方法, 主要邏輯保證Servlet初始化一次。注意instanceInitialized 變量聲明為volatile類型,保證線程安全。
private synchronized void initServlet(Servlet servlet) throws ServletException { // Call the initialization method of this servlet try { instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT, servlet); if( Globals.IS_SECURITY_ENABLED) { boolean success = false; try { Object[] args = new Object[] { facade }; SecurityUtil.doAsPrivilege("init", servlet, classType, args); success = true; } finally { if (!success) { // destroy() will not be called, thus clear the reference now SecurityUtil.remove(servlet); } } } else { servlet.init(facade); } instanceInitialized = true; instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet); } catch (UnavailableException f) { ... } }
initServlet方法,主要調(diào)用servlet的init方法,完成servlet組件的初始化工作,以及觸發(fā)beforeInit和afterInit事件,觸發(fā)操作org.apache.catalina.InstanceListener.instanceEvent(InstanceEvent event)。
4.listener的初始化過(guò)程
4.1 listener類結(jié)構(gòu)圖
4.2 listenerStart方法
/** * Configure the set of instantiated application event listeners * for this Context. Return <code>true</code> if all listeners wre * initialized successfully, or <code>false</code> otherwise. */ public boolean listenerStart() { // Sort listeners in two arrays ArrayList<Object> eventListeners = new ArrayList<Object>(); ArrayList<Object> lifecycleListeners = new ArrayList<Object>(); for (int i = 0; i < results.length; i++) { if ((results[i] instanceof ServletContextAttributeListener) || (results[i] instanceof ServletRequestAttributeListener) || (results[i] instanceof ServletRequestListener) || (results[i] instanceof HttpSessionAttributeListener)) { eventListeners.add(results[i]); } if ((results[i] instanceof ServletContextListener) || (results[i] instanceof HttpSessionListener)) { lifecycleListeners.add(results[i]); } } ... for (int i = 0; i < instances.length; i++) { if (instances[i] == null) continue; if (!(instances[i] instanceof ServletContextListener)) continue; ServletContextListener listener = (ServletContextListener) instances[i]; try { fireContainerEvent("beforeContextInitialized", listener); if (noPluggabilityListeners.contains(listener)) { listener.contextInitialized(tldEvent); } else { listener.contextInitialized(event); } fireContainerEvent("afterContextInitialized", listener); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); fireContainerEvent("afterContextInitialized", listener); getLogger().error (sm.getString("standardContext.listenerStart", instances[i].getClass().getName()), t); ok = false; } } return (ok); }
通過(guò)分析listenerStart方法片段,可以知道tomcat將listener分為2類,分別是eventListener和lifecycleListener兩大類。并將listener加入到StandardHost中,并觸發(fā)beforeContextInitialized事件,和afterContextInitialized事件。
類型 | Listener名稱 |
eventListener | ServletContextAttributeListener |
eventListener | ServletRequestAttributeListener |
eventListener | ServletRequestListener |
eventListener | HttpSessionAttributeListener |
lifecycleListener | HttpSessionListener |
lifecycleListener noPluggabilityListener | ServletContextListener |
5.filter 初始化過(guò)程
5.1 filterStart方法,遍歷FilterDefs,初始化Filter,并放入filterConfigsMap。
這兒維護(hù)的filterConfigsMap,將來(lái)會(huì)在StandardWrapperValve.invoke方法中調(diào)用。而StandardWrapperValve作為servlet為pipeline模式中處理用戶請(qǐng)求流程中的一個(gè)節(jié)點(diǎn),所以也就實(shí)現(xiàn)了filter攔截請(qǐng)求的目的。
public boolean filterStart() { if (getLogger().isDebugEnabled()) getLogger().debug("Starting filters"); // Instantiate and record a FilterConfig for each defined filter boolean ok = true; synchronized (filterConfigs) { filterConfigs.clear(); for (Entry<String, FilterDef> entry : filterDefs.entrySet()) { String name = entry.getKey(); if (getLogger().isDebugEnabled()) getLogger().debug(" Starting filter '" + name + "'"); ApplicationFilterConfig filterConfig = null; try { filterConfig = new ApplicationFilterConfig(this, entry.getValue()); filterConfigs.put(name, filterConfig); } catch (Throwable t) { t = ExceptionUtils.unwrapInvocationTargetException(t); ExceptionUtils.handleThrowable(t); getLogger().error (sm.getString("standardContext.filterStart", name), t); ok = false; } } } return (ok); }
5.2 ApplicationFilterConfig構(gòu)造函數(shù)
ApplicationFilterConfig(Context context, FilterDef filterDef) throws ClassCastException, ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException, InvocationTargetException, NamingException { super(); this.context = context; this.filterDef = filterDef; // Allocate a new filter instance if necessary if (filterDef.getFilter() == null) { getFilter(); } else { this.filter = filterDef.getFilter(); getInstanceManager().newInstance(filter); initFilter(); } } Filter getFilter() throws ClassCastException, ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException, InvocationTargetException, NamingException { // Return the existing filter instance, if any if (this.filter != null) return (this.filter); // Identify the class loader we will be using String filterClass = filterDef.getFilterClass(); //構(gòu)造filter對(duì)象 this.filter = (Filter) getInstanceManager().newInstance(filterClass); initFilter(); return (this.filter); } private void initFilter() throws ServletException { if (context instanceof StandardContext && context.getSwallowOutput()) { try { SystemLogHandler.startCapture(); filter.init(this);//調(diào)用filter的初始化 } finally { String capturedlog = SystemLogHandler.stopCapture(); if (capturedlog != null && capturedlog.length() > 0) { getServletContext().log(capturedlog); } } } else { filter.init(this); } // Expose filter via JMX registerJMX(); }
最后,組件已經(jīng)初始化了。現(xiàn)在理所當(dāng)然應(yīng)該可以處理用戶請(qǐng)求了。都知道我們用戶請(qǐng)求信息都在HttpServletRequest對(duì)象中。那么用戶的 HTTP socket流信息,怎么一步步轉(zhuǎn)換成HttpServletRequest對(duì)象的呢。下次詳聊。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。