前言
上篇文章講到了Context的啓動,在 Context 的 startInternal 方法中調用了子容器的 start 方法,Context 的子容器則是 Wrapper,Wrapper 的實現類是 StandardWrapper。StandardWrapper 沒有重載 initInternal 方法。
1. StandardWrapper#startInternal 方法web
/** * Start this component and implement the requirements * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ @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); } }
startInternal 的邏輯也很簡單,就是調用父類 ContainerBase 的 startInternal 方法,而後調用 setAvailable(0L) 方法設置 available 屬性的值。apache
/** * The date and time at which this servlet will become available (in * milliseconds since the epoch), or zero if the servlet is available. * If this value equals Long.MAX_VALUE, the unavailability of this * servlet is considered permanent. */ protected long available = 0L; /** * Set the available date/time for this servlet, in milliseconds since the * epoch. If this date/time is Long.MAX_VALUE, it is considered to mean * that unavailability is permanent and any request for this servlet will return * an SC_NOT_FOUND error. If this date/time is in the future, any request for * this servlet will return an SC_SERVICE_UNAVAILABLE error. * * @param available The new available date/time */ @Override public void setAvailable(long available) { long oldAvailable = this.available; if (available > System.currentTimeMillis()) this.available = available; else this.available = 0L; support.firePropertyChange("available", Long.valueOf(oldAvailable), Long.valueOf(this.available)); }
從 available 的註釋能夠看出,它的做用是表示 Servlet 的可用時間的。segmentfault
2. StandardWrapper#load 方法
在上篇文章中,講到了Context的startInternal 方法中作了一件事情就是調用 Wrapper 的 load 方法(在 StandardContext#loadOnStartup 中調用的)。在 StandardContext#startInternal 中先調用 Wrapper的 start 方法,而後調用 Wrapper 的 load 方法。tomcat
/** * The (single) possibly uninitialized instance of this servlet. */ protected volatile Servlet instance = null; /** * Load and initialize an instance of this servlet, if there is not already * at least one initialized instance. This can be used, for example, to * load servlets that are marked in the deployment descriptor to be loaded * at server startup time. * <p> * <b>IMPLEMENTATION NOTE</b>: Servlets whose classnames begin with * <code>org.apache.catalina.</code> (so-called "container" servlets) * are loaded by the same classloader that loaded this class, rather than * the classloader for the current web application. * This gives such classes access to Catalina internals, which are * prevented for classes loaded for web applications. * * @exception ServletException if the servlet init() method threw * an exception * @exception ServletException if some other loading problem occurs */ @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(sm.getString("standardWrapper.jspMonitorError", instance)); } } }
load 方法邏輯很簡單,先調用 loadServlet() 獲取一個 Servlet 對象,就是經過 servletClass 屬性指定的類名,調用 InstanceManager#newInstance(servletClass) 方法來建立一個 Servlet 對象。
而後調用 initServlet(instance) 來初始化這個 Servlet 對象,也就是調用這個 Servlet 對象的 init 方法。app
能夠看出 Wrapper 裏有一個 Servlet 屬性,Wrapper 正是對 Servlet 的包裝。jsp
3. StandardWrapper#backgroundProcess 方法ide
/** * Execute a periodic task, such as reloading, etc. This method will be * invoked inside the classloading context of this container. Unexpected * throwables will be caught and logged. */ @Override public void backgroundProcess() { super.backgroundProcess(); if (!getState().isAvailable()) return; if (getServlet() instanceof PeriodicEventListener) { ((PeriodicEventListener) getServlet()).periodicEvent(); } }
backgroundProcess 方法很簡單,只是調用一下父類 ContainerBase 的 backgroundProcess 方法。ui
小結
本文分析了 Wrapper 容器的啓動,wrapper 在 tomcat 啓動過程當中的關鍵點就是初始化了 Servlet。this