在 Tomcat 中 Servlet 是如何工作的

版权声明:作者:N3verL4nd 出处: https://blog.csdn.net/lgh1992314/article/details/80210497

基于Tomcat 8.5.30

解析 Servlet

Web 应用的初始化工作是在 org.apache.catalina.startup.ContextConfigconfigureStart() 方法中实现的,应用的初始化主要是要解析web.xml 文件,这个文件描述了一个 Web 应用的关键信息,也是一个 Web 应用的入口。
web.xml 中的配置会被解析为一个org.apache.tomcat.util.descriptor.web.WebXml 对象。
接下去将会将 WebXml 对象中的属性设置到 Context 容器中,这里包括创建 Servlet 对象、filter、listener 等等。这段代码在org.apache.catalina.startup.ContextConfigconfigureContext(WebXml webxml)方法中。

WebXml 中private final Map<String,ServletDef> servlets = new HashMap<>(); 即为保存 Servlet 的容器。(包括web.xml中和注解中的Servlet)
另外 ServletDef 保存的仅仅是 Servlet 的相关信息,并不是一个 Servlet 实例。

for (ServletDef servlet : webxml.getServlets().values()) {
    Wrapper wrapper = context.createWrapper();
    // ...
    context.addChild(wrapper);
}

这段代码清楚的描述了如何将 Servlet 包装成 Context 容器中的 StandardWrapper,这里有个疑问,为什么要将 Servlet 包装成 StandardWrapper 而不直接是 Servlet 对象。
这里 StandardWrapper 是 Tomcat 容器中的一部分,它具有容器的特征,而 Servlet 是一个独立的 web 开发标准,不应该强耦合在 Tomcat 中。

除了将 Servlet 包装成 StandardWrapper 并作为子容器添加到 Context 中,其它的所有 web.xml 属性都被解析到 Context 中,所以说 Context 容器才是真正运行 Servlet 的 Servlet 容器。一个 Web 应用对应一个 Context 容器,容器的配置属性由应用的 web.xml 指定,这样我们就能理解 web.xml 到底起到什么作用了。

StandardWrapper 保存了 Servlet 实例以及配置信息。

public class StandardWrapper extends ContainerBase
    implements ServletConfig, Wrapper, NotificationEmitter {
        // ...
        protected volatile Servlet instance = null;
        // ...
}

创建 Servlet 对象

load-on-startup >= 0

如果 Servlet 的 load-on-startup 配置项大于 0,那么在 Context 容器启动的时候就会被实例化。
在 conf 下的 web.xml 文件中定义了一些默认的配置项,其定义了两个 Servlet,分别是:org.apache.catalina.servlets.DefaultServlet 和 org.apache.jasper.servlet.JspServlet 它们的 load-on-startup 分别是 1 和 3,也就是当 Tomcat 启动时这两个 Servlet 就会被启动。

org.apache.catalina.core.StandardContext#loadOnStartup
这里写图片描述

org.apache.catalina.core.StandardWrapper#load

public synchronized void load() throws ServletException {
    instance = loadServlet();
    // 核心代码
    if (!instanceInitialized) {
        initServlet(instance);
    }
    // ...
}

org.apache.catalina.core.StandardWrapper#loadServlet

public synchronized Servlet loadServlet() throws ServletException {
    if (!singleThreadModel && (instance != null))
        return instance;
    // ...
    Servlet servlet;
    InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
    servlet = (Servlet) instanceManager.newInstance(servletClass);

    initServlet(servlet);
}

创建 Servlet 对象的相关类结构:
创建 Servlet 对象的相关类结构

load-on-startup < 0

也就是我们访问时才创建 该 Servlet

此时调用的是org.apache.catalina.core.StandardWrapper#allocate

public Servlet allocate() throws ServletException {
    instance = loadServlet();
    // ...
    if (!instanceInitialized) {
        initServlet(instance);

    }
    // ...
    return instance;
}

初始化 Servlet

org.apache.catalina.core.StandardWrapper#initServlet

private synchronized void initServlet(Servlet servlet) throws ServletException {
    if (instanceInitialized && !singleThreadModel) return;
    // ...
    servlet.init(facade);
    instanceInitialized = true;
    // ...
}

servlet.init(facade);调用的就是 Servlet 接口中的 public void init(ServletConfig config) throws ServletException; 方法。
也即完成了 Servlet 的初始化工作。

StandardWrapperFacade 则是 StandardWrapper 的装饰类。

protected final StandardWrapperFacade facade = new StandardWrapperFacade(this);
package org.apache.catalina.core;

import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;

public final class StandardWrapperFacade
    implements ServletConfig {

    private final ServletConfig config;
    private ServletContext context = null;

    public StandardWrapperFacade(StandardWrapper config) {
        super();
        this.config = config;
    }


    @Override
    public String getServletName() {
        return config.getServletName();
    }

    @Override
    public ServletContext getServletContext() {
        if (context == null) {
            context = config.getServletContext();
            if (context instanceof ApplicationContext) {
                context = ((ApplicationContext) context).getFacade();
            }
        }
        return (context);
    }

    @Override
    public String getInitParameter(String name) {
        return config.getInitParameter(name);
    }


    @Override
    public Enumeration<String> getInitParameterNames() {
        return config.getInitParameterNames();
    }
}

而我们常说的 Tomcat 是一个 Servlet 容器,其实更准确的讲是 StandardContext

public class StandardContext extends ContainerBase
        implements Context, NotificationEmitter {}

ContainerBase 中有:
protected final HashMap<String, Container> children = new HashMap<>();

猜你喜欢

转载自blog.csdn.net/lgh1992314/article/details/80210497