Tomcat启动分析(十一) - Wrapper组件

2019-04-03  本文已影响0人  buzzerrookie

在Tomcat中,Wrapper组件封装了servlet定义和参数。

创建过程

前面一篇文章提到ContextConfig监听器响应配置开始事件时会解析web.xml,进而将每个servlet定义都包装成Wrapper,这是由Context组件的createWrapper方法实现的。
StandardContext类实现的createWrapper方法代码如下:

@Override
public Wrapper createWrapper() {

    Wrapper wrapper = null;
    if (wrapperClass != null) {
        try {
            wrapper = (Wrapper) wrapperClass.getConstructor().newInstance();
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            log.error("createWrapper", t);
            return null;
        }
    } else {
        wrapper = new StandardWrapper();
    }

    synchronized (wrapperLifecyclesLock) {
        for (int i = 0; i < wrapperLifecycles.length; i++) {
            try {
                Class<?> clazz = Class.forName(wrapperLifecycles[i]);
                LifecycleListener listener =
                    (LifecycleListener) clazz.getConstructor().newInstance();
                wrapper.addLifecycleListener(listener);
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error("createWrapper", t);
                return null;
            }
        }
    }

    synchronized (wrapperListenersLock) {
        for (int i = 0; i < wrapperListeners.length; i++) {
            try {
                Class<?> clazz = Class.forName(wrapperListeners[i]);
                ContainerListener listener =
                        (ContainerListener) clazz.getConstructor().newInstance();
                wrapper.addContainerListener(listener);
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error("createWrapper", t);
                return null;
            }
        }
    }

    return wrapper;
}

Wrapper组件

Wrapper接口继承了Container接口,StandardWrapper是默认实现类,同其他容器组件一样,它也继承自ContainerBase类。

public class StandardWrapper extends ContainerBase
    implements ServletConfig, Wrapper, NotificationEmitter {

    private final Log log = LogFactory.getLog(StandardWrapper.class); // must not be static

    protected static final String[] DEFAULT_SERVLET_METHODS = new String[] {
                                                    "GET", "HEAD", "POST" };
    public StandardWrapper() {

        super();
        swValve=new StandardWrapperValve();
        pipeline.setBasic(swValve);
        broadcaster = new NotificationBroadcasterSupport();

    }

    protected long available = 0L;
    protected final AtomicInteger countAllocated = new AtomicInteger(0);

    /**
     * The facade associated with this wrapper.
     */
    protected final StandardWrapperFacade facade = new StandardWrapperFacade(this);

    protected volatile Servlet instance = null;
    protected volatile boolean instanceInitialized = false;

    /**
     * The load-on-startup order value (negative value means load on
     * first call) for this servlet.
     */
    protected int loadOnStartup = -1;

    /**
     * Mappings associated with the wrapper.
     */
    protected final ArrayList<String> mappings = new ArrayList<>();

    /**
     * The initialization parameters for this servlet, keyed by
     * parameter name.
     */
    protected HashMap<String, String> parameters = new HashMap<>();

    // 省略一些代码

    /**
     * Multipart config
     */
    protected MultipartConfigElement multipartConfigElement = null;

    /**
     * Async support
     */
    protected boolean asyncSupported = false;
    protected boolean enabled = true;
    private boolean overridable = false;
    // 省略一些代码
}

重要的成员变量如下:

StandardWrapper的构造函数为自己的Pipeline添加了基本阀StandardWrapperValve。

组件初始化

StandardWrapper类并没有重写initInternal方法,因此它的初始化过程只是为自己创建了一个线程池用于启动和停止自己的子容器。

组件启动

StandardWrapper类的startInternal方法如下所示:

@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);
    }

}

加载servlet

前面一篇文章提到StandardContext启动时会加载被标识为“启动加载”的servlet,这是通过StandardContext类的loadOnStartup方法实现的:

public boolean loadOnStartup(Container children[]) {

    // Collect "load on startup" servlets that need to be initialized
    TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
    for (int i = 0; i < children.length; i++) {
        Wrapper wrapper = (Wrapper) children[i];
        int loadOnStartup = wrapper.getLoadOnStartup();
        if (loadOnStartup < 0)
            continue;
        Integer key = Integer.valueOf(loadOnStartup);
        ArrayList<Wrapper> list = map.get(key);
        if (list == null) {
            list = new ArrayList<>();
            map.put(key, list);
        }
        list.add(wrapper);
    }

    // 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("standardContext.loadOnStartup.loadException",
                        getName(), wrapper.getName()), StandardWrapper.getRootCause(e));
                // NOTE: load errors (including a servlet that throws
                // UnavailableException from the init() method) are NOT
                // fatal to application startup
                // unless failCtxIfServletStartFails="true" is specified
                if(getComputedFailCtxIfServletStartFails()) {
                    return false;
                }
            }
        }
    }
    return true;

}

loadOnStartup方法遍历Context的子容器(即所有Wrapper),对load-on-startup属性值大于等于0的servlet按从小到大的顺序依次加载。加载由Wrapper的load方法完成。StandardWrapper类实现的load方法如下:

@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("Error registering JSP monitoring with jmx " +
                        instance);
        }
    }
}

loadServlet方法会加载并初始化该Wrapper封装的servlet。

上一篇下一篇

猜你喜欢

热点阅读