1

前言
上篇文章讲到了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 的包装。

3. StandardWrapper#backgroundProcess 方法

/**
 * 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。


客官
36 声望25 粉丝