欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

tomcat源码研究之 web.xml中load-on-startup标签的含义

程序员文章站 2022-06-06 22:02:26
...

(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调用情况