摘要:本文将带你了解 WebApp源码之web组件初始化,希望本文对大家学WEBAPP有所帮助
了解组件初始化调用序列
组件servlet,listener,filter组件的初始化顺序
listener的初始化过程
servlet的初始化过程
filter的初始化过程
1.组件初始化序列
通过《解析web.xml》,我们可以了解,tomcat在自动完成webapp应用的部署时,完成了web.xml信息的解析,也就是说webapp组件配置元信息,tomcat已经拿到了。接下来,就是根据配置的元信息规则,初始化组件。
通过上图,知道组件对象的初始化主要从StandardContext完成的, StandardContext对应着耳熟能详的应用,比如webapp目录下的 docs,ROOT,manager...。
1组件初始化过程使用的是线程启动的。
代码参考org.apache.catalina.core.ContainerBase.startInternal List results = new ArrayList(); for (int i = 0; i < children.length; i++) { results.add(startStopExecutor.submit(new StartChild(children[i]))); } boolean fail = false; for (Future result : results) { try { result.get(); } catch (Exception e) { log.error(sm.getString("containerBase.threadedStartFailed"), e); fail = true; } } 2.组件servlet,listener,filter组件的初始化顺序 笔者,创建了1个名为"helloapp"的webapp应用,分别声明了多个lister,多个servlet,filter。通过多次调整各组件在web.xml中的相对顺序。得出如下结论。 · 组件执行顺序按Listener,Filter,Servlet进行。 · · Servlet的load-on-startup影响Servlet的启动顺序,详情见2.1节说明 · · Filter之间的初始化顺序,与中的字符排序规则有关,经测试与默认排序规则相反。 · · Listener之间的初始化顺序,与在web.xml声明的顺序一致。这一点非常重要,如果使用一些mvc框架,安全框架时,如果使用Listener来完成过滤拦截的话,一定要注意Listener的声明顺序。 · 2.1servlet中load-on-startup的规则说明 o 标记容器是否在启动的时候就加载这个servlet。 o o 当值为0或者大于0时,表示容器在应用启动时就加载这个servlet; o o 当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。 o o 正数的值越小,启动该servlet的优先级越高。 o 3.servlet的初始化过程 org.apache.catalina.core.StandardWrapper public boolean loadOnStartup(Container children[]) { // Collect "load on startup" servlets that need to be initialized TreeMap map = new TreeMap(); for (int i = 0; i < children.length; i++) { Wrapper wrapper = (Wrapper) children[i]; int loadOnStartup = wrapper.getLoadOnStartup(); //如果小于0,跳过 if (loadOnStartup < 0) continue; Integer key = Integer.valueOf(loadOnStartup); ArrayList 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 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; } 通过源码分析,我们清楚了load-on-startup 小于0时,表示servlet不需要在启动时初始化。 protected volatile boolean instanceInitialized = false; public synchronized void load() throws ServletException { instance = loadServlet(); if (!instanceInitialized) { initServlet(instance); } if (isJspServlet) { // } } 通过分析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方法,主要调用servlet的init方法,完成servlet组件的初始化工作,以及触发beforeInit和afterInit事件,触发操作org.apache.catalina.InstanceListener.instanceEvent(InstanceEvent event)。
4.listener的初始化过程
4.1 listener类结构图
4.2 listenerStart方法 /** * Configure the set of instantiated application event listeners * for this Context. Return true if all listeners wre * initialized successfully, or false otherwise. */ public boolean listenerStart() { // Sort listeners in two arrays ArrayList eventListeners = new ArrayList(); ArrayList lifecycleListeners = new ArrayList(); 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); } 通过分析listenerStart方法片段,可以知道tomcat将listener分为2类,分别是eventListener和lifecycleListener两大类。并将listener加入到StandardHost中,并触发beforeContextInitialized事件,和afterContextInitialized事件。 类型 Listener名称 eventListener ServletContextAttributeListener eventListener ServletRequestAttributeListener eventListener ServletRequestListener eventListener HttpSessionAttributeListener lifecycleListener HttpSessionListener lifecycleListener noPluggabilityListener ServletContextListener 5.filter 初始化过程 5.1 filterStart方法,遍历FilterDefs,初始化Filter,并放入filterConfigsMap。 这儿维护的filterConfigsMap,将来会在StandardWrapperValve.invoke方法中调用。而StandardWrapperValve作为servlet为pipeline模式中处理用户请求流程中的一个节点,所以也就实现了filter拦截请求的目的。 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 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构造函数 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(); //构造filter对象 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);//调用filter的初始化 } finally { String capturedlog = SystemLogHandler.stopCapture(); if (capturedlog != null && capturedlog.length() > 0) { getServletContext().log(capturedlog); } } } else { filter.init(this); } // Expose filter via JMX registerJMX(); }
最后,组件已经初始化了。
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之WebApp频道
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号