前言
上篇文章讲到了Context的启动,在 Context 的 startInternal 方法中调用了子容器的 start 方法,Context 的子容器则是 Wrapper,Wrapper 的实现类是 StandardWrapper。StandardWrapper 没有重载 initInternal 方法。
1. StandardWrapper#startInternal 方法
/** * 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 属性的值。
/** * 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 的可用时间的。
2. StandardWrapper#load 方法
在上篇文章中,讲到了Context的startInternal 方法中做了一件事情就是调用 Wrapper 的 load 方法(在 StandardContext#loadOnStartup 中调用的)。在 StandardContext#startInternal 中先调用 Wrapper的 start 方法,然后调用 Wrapper 的 load 方法。
/** * 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 方法。
可以看出 Wrapper 里有一个 Servlet 属性,Wrapper 正是对 Servlet 的包装。
/** * 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 方法。
小结
本文分析了 Wrapper 容器的启动,wrapper 在 tomcat 启动过程中的关键点就是初始化了 Servlet。