tomcat源码研究之 web.xml中load-on-startup标签的含义
(1)
在servlet的配置当中,<load-on-startup>5</load-on-startup>的含义是:
标记容器是否在启动的时候就加载这个servlet。
当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;
当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。
正数的值越小,启动该servlet的优先级越高。
web.xml的配置如下:
<servlet>
<servlet-name>initservlet</servlet-name>
<servlet-class>com.ccc.lxy.util.initServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
(2) 在我的tomcat源码研究中我已经介绍到,tomcat本身就是组件构成的容器,我们的应用程序程序将对应到Context组件
而Context组件实现是StandardContext这个类,而Servlet对应的组件是Wrapper,它的实现是StandardWrapper,
既然当load-on-startup值大于等于0时候 在启动时候 那么我看StandardContext 这个类启动时的情况
protected synchronized void startInternal() throws LifecycleException { if(log.isDebugEnabled()) log.debug("Starting " + getBaseName()); // Send j2ee.state.starting notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.starting", this.getObjectName(), sequenceNumber.getAndIncrement()); broadcaster.sendNotification(notification); } setConfigured(false); boolean ok = true; // Currently this is effectively a NO-OP but needs to be called to // ensure the NamingResources follows the correct lifecycle if (namingResources != null) { namingResources.start(); } // Add missing components as necessary if (webappResources == null) { // (1) Required by Loader if (log.isDebugEnabled()) log.debug("Configuring default Resources"); try { if ((getDocBase() != null) && (getDocBase().endsWith(".war")) && (!(new File(getBasePath())).isDirectory())) setResources(new WARDirContext()); else setResources(new FileDirContext()); } catch (IllegalArgumentException e) { log.error("Error initializing resources: " + e.getMessage()); ok = false; } } // 代码省略... if (ok) { if (!listenerStart()) { log.error( "Error listenerStart"); ok = false; } } try { // Start manager if ((manager != null) && (manager instanceof Lifecycle)) { ((Lifecycle) getManager()).start(); } } catch(Exception e) { log.error("Error manager.start()", e); ok = false; } // Configure and call application filters if (ok) { if (!filterStart()) { log.error("Error filterStart"); ok = false; } } // Load and initialize all "load on startup" servlets //加载servlet的loadOnStardup属性 if (ok) { if (!loadOnStartup(findChildren())){ log.error("Error loadOnStartup"); ok = false; } } // Start ContainerBackgroundProcessor thread super.threadStart(); //代码省略
从上述代码中,我可知Context组件在启动时调用方法 loadOnStartup这个方法
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(); //从这儿可知loadonStartup 小于0时 跳过, //也就是只有大于等于0时才进行的 初始化化加载了 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); } //下面是具体初始化加载的loadonStartup>=0的Servlet // 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("standardWrapper.loadException", getName()), StandardWrapper.getRootCause(e)); // NOTE: load errors (including a servlet that throws // UnavailableException from tht init() method) are NOT // fatal to application startup, excepted if failDeploymentIfServletLoadedOnStartupFails is specified if(getComputedFailCtxIfServletStartFails()) { return false; } } } } return true; }
从上面可知只有loadonStartup>=0时才调用wrapper的load
而且从上面可知将这些大于等于0的Servlet都放入一个treemap, treemap恰恰是一个排序map且升序map 所以loadonstartup 越小越先遍历到 越先执行load
而Wrapper的load就是StandardWrapper的load方法具体如下
public synchronized void load() throws ServletException { //实例化一个Servlet instance = loadServlet(); //实例化完成以后开始调用InitServlet方法 if (!instanceInitialized) { initServlet(instance); } if (isJspServlet) { StringBuilder oname = new StringBuilder(MBeanUtils.getDomain(getParent())); oname.append(":type=JspMonitor,name="); oname.append(getName()); oname.append(getWebModuleKeyProperties()); 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); } } }
具体调用InitServlet方法如下:
private synchronized void initServlet(Servlet servlet) throws ServletException { if (instanceInitialized && !singleThreadModel) return; // Call the initialization method of this servlet try { instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT, servlet); //调用Servlet的init方法 if( Globals.IS_SECURITY_ENABLED) { boolean success = false; try { //反射调用init方法 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(ServletConfig config)方法然后 在这方法中init()方法了了} servlet.init(facade); } instanceInitialized = true; instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet); } catch (UnavailableException f) { instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet, f); unavailable(f); throw f; } catch (ServletException f) { instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet, f); // If the servlet wanted to be unavailable it would have // said so, so do not call unavailable(null). throw f; } catch (Throwable f) { ExceptionUtils.handleThrowable(f); getServletContext().log("StandardWrapper.Throwable", f ); instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet, f); // If the servlet wanted to be unavailable it would have // said so, so do not call unavailable(null). throw new ServletException (sm.getString("standardWrapper.initException", getName()), f); } }
从这里可知我们Servlet调用情况