这边文章主要介绍的是Host容器 和 Engine容器。如果你想在同一个Tomcat上部署运行多个Context容器的话,你就需要使用Host容器,从理论上来讲,如果你的Tomcat只想要部署一个Context容器的话,你可以不使用Host容器。

在org.apache.catalina.Context接口的描述有下一段话:

  Context容器的父容器通常是Host容器,也有可能是其他实现,或者如果不是必要的话,就可以不使用父容器。

  但是 在tomcat的实际部署中,总会使用一个Host容器,在下面在解释原因,

  Engine容器表示Catalina的整个Servlet引擎,如果使用了Engine容器,那么它总是处于容器层级的最顶层,添加到Enginer容器中的子容器通常是org.apache.catalina.Host 或者 org.apahce.catalina.Context的实现,默认情况下Tomcat会使用一个Engine容器并且使用一个Host容器作为其子容器,

Host接口 

  host容器是 org.apahce.catalina.Host接口的实例,Host接口继承自Container接口

package org.apache.catalina;

/**
 * 
 * <p>
 * <b>Title:Host.java</b>
 * </p>
 * <p>
 * Copyright:ChenDong 2018
 * </p>
 * <p>
 * Company:仅学习时使用
 * </p>
 * <p>
 * 类功能描述:Host是表示Catalina servlet引擎中的虚拟主机的容器。它在以下类型的场景中很有用:
 * 
 * 
 * 
 * 您希望使用拦截器来查看此特定虚拟主机处理的每个请求。
 * 
 * 您希望使用独立的HTTP连接器运行Catalina,但是仍然希望支持多个虚拟主机。
 * 
 * 通常,在部署连接到Web服务器(如Apache)的Catalina时,您不会使用主机,因为连接器将利用Web服务器的设施来确定应该使用哪个上下文(
 * 或者甚至哪个包装器)来处理这个请求。
 * 
 * 附加到主机的父容器通常是一个引擎,但是可以是一些其他的实现,或者如果不必要的话可以省略。
 * 
 * 
 * 
 * 附加到主机的子容器通常是上下文的实现(表示单个servlet上下文)。
 * </p>
 * 
 * @author 
 * @date 2018年12月15日 下午9:28:58
 * @version 1.0
 */
public interface Host extends Container {

    // ----------------------------------------------------- Manifest Constants

    /**
     *
     * 
     * 当使用<code>addAlias()</code>方法添加新的别名时发送的 {@code ContainerEvent}事件类型。
     */
    public static final String ADD_ALIAS_EVENT = "addAlias";

    /**
     * 当使用<code>removeAlias()</code>移除一个旧的别名时 触发的 {@code ContainerEvent}事件类型
     */
    public static final String REMOVE_ALIAS_EVENT = "removeAlias";

    // ------------------------------------------------------------- Properties

    /**
     * 返回此{@code Host}容器的 根路径,它可以是 绝对路径、相对路径、或者URL
     */
    public String getAppBase();

    /**
     * 
     * 为这个{@code Host}容器 设置一个根路径,它可以是 绝对路径、相对路径、或者URL
     *
     * @param appBase
     *            新的容器根路径
     */
    public void setAppBase(String appBase);

    /**
     * Return the value of the auto deploy flag. If true, it indicates that this
     * host's child webapps should be discovred and automatically deployed.
     */
    public boolean getAutoDeploy();

    /**
     * Set the auto deploy flag value for this host.
     * 
     * @param autoDeploy
     *            The new auto deploy flag
     */
    public void setAutoDeploy(boolean autoDeploy);

    /**
     * 
     * 为新的web应用程序设置 {@code DefaultContext}。
     * 
     * @param defaultContext
     *            新的 DefaultContext
     */
    public void addDefaultContext(DefaultContext defaultContext);

    /**
     * 为新的web应用程序检索 并返回 DefaultContext.
     */
    public DefaultContext getDefaultContext();

    /**
     * 返回此容器表示的虚拟主机的规范、完全限定的名称
     */
    public String getName();

    /**
     * 设置此容器表示的虚拟主机的规范、完全限定的名称
     *
     * @param name
     *            虚拟主机的名称
     *
     * @exception IllegalArgumentException
     *                如果这个名字是 {@code null}
     */
    public void setName(String name);

    // --------------------------------------------------------- Public Methods

    /**
     * 
     * 将DefaultContext 的 config 导入到web应用程序上下文中。
     * 
     * @param context
     *            导入默认Context的web应用程序Context
     */
    public void importDefaultContext(Context context);

    /**
     * 添加应该映射到同一主机的别名
     *
     * @param alias
     *            要被添加的别名
     */
    public void addAlias(String alias);

    /**
     * 
     * 返回此主机的别名集。如果没有定义,则返回一个零长度数组
     */
    public String[] findAliases();

    /**
     * 
     * 返回一个用来处理引用Http请求的 Context 根据 请求的URI 若果不存在则返回
     *
     * @param uri
     *            Request URI to be mapped
     */
    public Context map(String uri);

    /**
     * 从此主机的别名中删除指定的别名
     *
     * @param alias
     *            要被删除的别名
     */
    public void removeAlias(String alias);

}

下面说下它在Tomat中的标准实现

StandardHost类

  在Catalina中的  org.apache.catalina.core.StandardHost类 是 org.apache.catalin.Host接口的标准实现,该类继承自 org.apache.catalina.core.ContainerBase类 ,实现了 Host 和 Deployer接口。

与StandardContext 和 StandardWrapper 类 相似,StandardHost类的构造器函数会将一个基础阀的实例 添加到其管道对相中。

    /**
     * 
     * 创建一个带有基础阀的 {@code  StandardHost}实例
     */
    public StandardHost() {

        super();
        pipeline.setBasic(new StandardHostValve());

    }

那么 它的基础阀 就是 org.apahce.catalina.core.StandardHostValue类的实例,

  当调用 StandardHost 类的 start()方法时,StandardHost实例 会新添加两个阀,分别是 ErrorReportValue类 和 ErrorDispatcherValue类的实例,这个两个阀均位于org.apahce.catalina.values包下,

 1 /**
 2      * 启动这个Host.
 3      *
 4      * @exception LifecycleException
 5      *                如果此组件检测到阻止其启动的致命错误
 6      * 
 7      */
 8     public synchronized void start() throws LifecycleException {
 9         // 如果 errorReportValveClass 阀的 完全限定名 不为空 的话 
10         if ((errorReportValveClass != null) && (!errorReportValveClass.equals(""))) {
11             try {
12                 Valve valve = (Valve) Class.forName(errorReportValveClass).newInstance();
13                 //添加这个ErrorReportValve阀
14                 addValve(valve);
15             } catch (Throwable t) {
16                 log(sm.getString("standardHost.invalidErrorReportValveClass", errorReportValveClass));
17             }
18         }
19 
20         //添加一个ErrorDispatcherValve阀
21         addValve(new ErrorDispatcherValve());
22 
23         super.start();
24 
25     }

变量 errorReportValueClass的值 定义在StandardHost类中;

private String errorReportValveClass = "org.apache.catalina.valves.ErrorReportValve";

  每当引入一个Http请求的时候,都会调用StandardHost实例的 invoke方法,由于StandardHost类并没有提供invoke方法的实现,因此它会调用父类 ContainerBase 类的 invoke方法,而ContainerBase类 的invoke方法将会调用StandardHost类的 基础阀StandardHostValue实例的invoke方法,StandardHostValue的invoke方法将会调用StandardHostr类的map方法来获取响应的Context实例来处理Http请求。

 1 /**
 2      * 
 3      * 返回一个Context实例 来处理这个 相对于Host容器的 相对URI所代表的请求,如果没有则返回 <code>null</code>
 4      *
 5      * @param uri
 6      *            要被映射的请求URI
 7      */
 8     public Context map(String uri) {
 9 
10         if (debug > 0)
11             log("Mapping request URI '" + uri + "'");
12         if (uri == null)
13             return (null);
14 
15         // Match on the longest possible context path prefix
16         // 匹配可能是最长的Context路径前缀
17         if (debug > 1)
18             log("  Trying the longest context path prefix");
19         Context context = null;
20         String mapuri = uri;
21         while (true) {
22             // 不断尝试根据路径去子容器中找对应的Context
23             context = (Context) findChild(mapuri);
24             if (context != null)
25                 break;
26             int slash = mapuri.lastIndexOf('/');
27             if (slash < 0)
28                 break;
29             // 不断截取路径最后一个/之前的路径 做匹配路径
30             mapuri = mapuri.substring(0, slash);
31         }
32 
33         
34         // 如果没有匹配到Context 则选择 默认的Context
35         if (context == null) {
36             if (debug > 1)
37                 log("  Trying the default context");
38             context = (Context) findChild("");
39         }
40 
41         //如果还是没有选中的 Context 直接返回null 并返回 错误信息
42         if (context == null) {
43             log(sm.getString("standardHost.mappingError", uri));
44             return (null);
45         }
46 
47         // 返回映射的上下文(如果有的话)
48         if (debug > 0)
49             log(" Mapped to context '" + context.getPath() + "'");
50         return (context);
51 
52     }

版权声明:本文为ChenD原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/ChenD/p/10125124.html