这边文章主要介绍的是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包下,

 /**
* 启动这个Host.
*
* @exception LifecycleException
* 如果此组件检测到阻止其启动的致命错误
*
*/
public synchronized void start() throws LifecycleException {
// 如果 errorReportValveClass 阀的 完全限定名 不为空 的话
if ((errorReportValveClass != null) && (!errorReportValveClass.equals(""))) {
try {
Valve valve = (Valve) Class.forName(errorReportValveClass).newInstance();
//添加这个ErrorReportValve阀
addValve(valve);
} catch (Throwable t) {
log(sm.getString("standardHost.invalidErrorReportValveClass", errorReportValveClass));
}
} //添加一个ErrorDispatcherValve阀
addValve(new ErrorDispatcherValve()); super.start(); }

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

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

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

 /**
*
* 返回一个Context实例 来处理这个 相对于Host容器的 相对URI所代表的请求,如果没有则返回 <code>null</code>
*
* @param uri
* 要被映射的请求URI
*/
public Context map(String uri) { if (debug > 0)
log("Mapping request URI '" + uri + "'");
if (uri == null)
return (null); // Match on the longest possible context path prefix
// 匹配可能是最长的Context路径前缀
if (debug > 1)
log(" Trying the longest context path prefix");
Context context = null;
String mapuri = uri;
while (true) {
// 不断尝试根据路径去子容器中找对应的Context
context = (Context) findChild(mapuri);
if (context != null)
break;
int slash = mapuri.lastIndexOf('/');
if (slash < 0)
break;
// 不断截取路径最后一个/之前的路径 做匹配路径
mapuri = mapuri.substring(0, slash);
} // 如果没有匹配到Context 则选择 默认的Context
if (context == null) {
if (debug > 1)
log(" Trying the default context");
context = (Context) findChild("");
} //如果还是没有选中的 Context 直接返回null 并返回 错误信息
if (context == null) {
log(sm.getString("standardHost.mappingError", uri));
return (null);
} // 返回映射的上下文(如果有的话)
if (debug > 0)
log(" Mapped to context '" + context.getPath() + "'");
return (context); }

在Tomcat 5 和之后的版本中,映射器组件已经移除,Context实例是通过request对象获取。

StandardHostMapper类

  在Tomcat4 中,ContainerBase类 (也就是StandardHost的父类),会调用其addDefaultMapper()方法创建一个默认的映射器,默认的映射器的类型 mapperClass属性的值决定,下面是ContainerBase类的addDefaultMapper实现

 /**
* 如果没有显式配置,则添加默认Mapper实现
*
* @param mapperClass
* Mapper实现的java完全限定类名
*/
protected void addDefaultMapper(String mapperClass) { // 若限定名为null 则证明我们不需要映射器 直接返回
if (mapperClass == null)
return;
//如果已经存在了mapper 则也直接返回
if (mappers.size() >= 1)
return; // 根据指定的限定名 初始化并添加一个 映射器默
try {
Class clazz = Class.forName(mapperClass);
Mapper mapper = (Mapper) clazz.newInstance();
//固定http协议
mapper.setProtocol("http");
addMapper(mapper);
} catch (Exception e) {
log(sm.getString("containerBase.addDefaultMapper", mapperClass), e);
} }

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

private String mapperClass = "org.apache.catalina.core.StandardHostMapper";

Standardhost类的start方法 在方法的默认会调用父类的start方法确保默认映射器的创建完成。

注意:学习到这里的朋友 也可能会和我有同样的 疑问 StandardContext 在start方法的结尾难道也调用了 其父类ContainerBase的start方法了么,答案是不是的,在Tomcat4中,StandardContext类创建默认映射器的方法略有不同,它的start方法并不会调用其父类的Start方法,StandardContext类的Start方法会自己调用addDefaultMapper方法 来创建默认的映射器。

当然StandardHostMapper类中最重要的方法 还是map方法,这个map方法 与StandardContextMapper类的map方法相比就要简单的多的多了

StandardHostMapper类的map方法展示

 public Container map(Request request, boolean update) {
// 如果这个request已经获得了映射的Context对象则直接返回
if (update && (request.getContext() != null))
return (request.getContext()); // 对我们的请求URI执行映射
String uri = ((HttpRequest) request).getDecodedRequestURI();
//还调用host的map 可见中重点逻辑在于host的map方法
Context context = host.map(uri); //需要更新请求中的映射Context对象么 并返回所选的上下文
if (update) {
request.setContext(context);
if (context != null)
((HttpRequest) request).setContextPath(context.getPath());
else
((HttpRequest) request).setContextPath(null);
}
return (context); }

在来一个StandardContextMapper对象的map方法 做一下对比

 /**
*
* 根据指的request 从 StandardContext对象的子容器中 找到 匹配的 Wrapper容器,若无则返回null
*
* @param request
* 要被处理的request
* @param update
* 是否更新request中的Wrapper
*
* @exception IllegalArgumentException
* 如果路径的相对部分不能被URL解码
*/
public Container map(Request request, boolean update) { int debug = context.getDebug(); // 这个请求已经被映射了一个wrapper对象了么?
if (update && (request.getWrapper() != null))
return (request.getWrapper()); //先获取到相对于Context的URI 就是将请求的整个URI截掉Context的URI 后剩下的URI,
String contextPath =
((HttpServletRequest) request.getRequest()).getContextPath();
String requestURI = ((HttpRequest) request).getDecodedRequestURI();
String relativeURI = requestURI.substring(contextPath.length()); if (debug >= 1)
context.log("Mapping contextPath='" + contextPath +
"' with requestURI='" + requestURI +
"' and relativeURI='" + relativeURI + "'"); // 应用规范中的标准请求URI映射规则
Wrapper wrapper = null;
String servletPath = relativeURI;
String pathInfo = null;
String name = null; // 规则 1 -- 精确匹配
if (wrapper == null) {
if (debug >= 2)
context.log(" Trying exact match(试着精确匹配)");
if (!(relativeURI.equals("/")))
//根据相对于Context的URI 从Context容器的serveletMapping集合中找到对应wrapper的名字
name = context.findServletMapping(relativeURI);
if (name != null)
//如果扎到了名字 则利用Context的 findChild方法 从其子容器中根据名字 找到对应wrapper
wrapper = (Wrapper) context.findChild(name);
if (wrapper != null) {
servletPath = relativeURI;
pathInfo = null;
}
} // 规则 2 -- 前缀匹配
if (wrapper == null) {
if (debug >= 2)
context.log(" Trying prefix match(试着前缀匹配)");
servletPath = relativeURI;
while (true) {
//前缀匹配 就是 把 相对Context的URI 作为前缀 后面加上/*看能不能找到name
name = context.findServletMapping(servletPath + "/*");
if (name != null)
wrapper = (Wrapper) context.findChild(name);
if (wrapper != null) {
pathInfo = relativeURI.substring(servletPath.length());
if (pathInfo.length() == 0)
pathInfo = null;
break;
}
int slash = servletPath.lastIndexOf('/');
if (slash < 0)
break;
//逐一减掉最后的/之后的URI
servletPath = servletPath.substring(0, slash);
}
} // Rule 3 -- 扩展匹配
if (wrapper == null) {
if (debug >= 2)
context.log(" Trying extension match(试着扩展匹配)");
//最后一个斜杠的位置
int slash = relativeURI.lastIndexOf('/');
//如果存在一个斜杠
if (slash >= 0) {
//截取最后一个斜杠之后的URI
String last = relativeURI.substring(slash);
//斜杠之后URI中最后一个.的位置
int period = last.lastIndexOf('.');
//如果斜杠之后URI存在 .
if (period >= 0) {
//匹配字符串 = * + 斜杠URI 最后一个.之后的URI
String pattern = "*" + last.substring(period);
//根据 扩展匹配规则 寻找name
name = context.findServletMapping(pattern);
if (name != null)
wrapper = (Wrapper) context.findChild(name);
if (wrapper != null) {
servletPath = relativeURI;
pathInfo = null;
}
}
}
} // 规则 4 -- 默认匹配规则
if (wrapper == null) {
if (debug >= 2)
context.log(" Trying default match(试着默认匹配)");
//直接斜杠匹配
name = context.findServletMapping("/");
if (name != null)
wrapper = (Wrapper) context.findChild(name);
if (wrapper != null) {
servletPath = relativeURI;
pathInfo = null;
}
} // 更新请求中的Wrapper(如果请求 update为true ),然后返回此包装器
if ((debug >= 1) && (wrapper != null))
context.log(" Mapped to servlet '" + wrapper.getName() +
"' with servlet path '" + servletPath +
"' and path info '" + pathInfo +
"' and update=" + update);
//如果需要更新则 将新匹配到的wrpper 更新到request中
if (update) {
request.setWrapper(wrapper);
((HttpRequest) request).setServletPath(servletPath);
((HttpRequest) request).setPathInfo(pathInfo);
}
return (wrapper); }

可以看到host的映射器的map方法只是简单的调用了 host的map方法而已。

StandardHostValue(StandardHost的基础阀)

  org.apache.catalina.core.StandardHostValue类时StandardHost实例的基础阀,当有引入的HTTp请求时,会调用StandardHost的involve方法 继而 调用ContainerBase类的invoke方法 继而在调用 其管道的invoke方法 继而在调用 StandardHostValue的invoke方法

对其进行处理,

 public void invoke(Request request, Response response,
ValveContext valveContext)
throws IOException, ServletException { // 验证请求和响应对象类型是否有效
if (!(request.getRequest() instanceof HttpServletRequest) ||
!(response.getResponse() instanceof HttpServletResponse)) {
return; // NOTE - Not much else we can do generically
} // 选择一个Context来处理这个请求
StandardHost host = (StandardHost) getContainer();
Context context = (Context) host.map(request, true);
if (context == null) {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
sm.getString("standardHost.noContext"));
return;
} //将上Context的类加载器绑定到当前线程 Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader()); // 更新会话的最后访问时间(如果有的话)
HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
String sessionId = hreq.getRequestedSessionId();
if (sessionId != null) {
Manager manager = context.getManager();
if (manager != null) {
Session session = manager.findSession(sessionId);
if ((session != null) && session.isValid())
session.access();
}
} // 请求此Context处理此请求
context.invoke(request, response); }

注意:在获取Context实例的时候有一个往复的过程,上面的map方法需要两个参数,该方法定义在ContainerBase类中,ContainerBase类中的map方法会找到其子容器的映射器,在本例中是StandardHost实例,并调用映射器的 map方法,

然后invoke方法会获取与该request对象相互关联的session对象,并调用其access方法 access方法会修改session对象的最后访问时间,下面是 org.apahce.catalina.session.StandardSession类中access方法的实现

 public void access() {

         this.isNew = false;
this.lastAccessedTime = this.thisAccessedTime;
this.thisAccessedTime = System.currentTimeMillis(); }

最后 invoke方法调用Context实例的invoke方法来处理HTTp请求。

  为什么必须要有一个Host容器

  在tomcat4 和 tomcat 5 中实际部署中,若一个Context实例使用ContextConfig对象进行配置,就必须要使用一个Host对象,原因如下:

  使用ContextConfig对象需要知道应用程序web.xml文件的位置,在其 applicationConfig方法中它会试图打开web.xml文件,下面是 applicationConfig方法的片段;

    synchronized (webDigester) {
try {
URL url = servletContext.getResource(Constants.ApplicationWebXml); InputSource is = new InputSource(url.toExternalForm());
is.setByteStream(stream);
webDigester.setDebug(getDebug());
if (context instanceof StandardContext) {
((StandardContext) context).setReplaceWelcomeFiles(true);
}
webDigester.clear();
webDigester.push(context);
webDigester.parse(is);
} catch (SAXParseException e) {

其中,Constants.ApplicationWebXml的值为

public static final String ApplicationWebXml = "/WEB-INF/web.xml";

web.xml文件的相对路径,servletContext是一个 org.apache.catalina.core.ApplicationContext类型(实现了javax.servlet.servletContext接口)的对象;

下面是 ApplicationContext类的getResource方法的部分实现代码

 public URL getResource(String path)
throws MalformedURLException { DirContext resources = context.getResources();
if (resources != null) {
String fullPath = context.getName() + path; // this is the problem. Host must not be null
String hostName = context.getParent().getName();

注意最后一行到代码 是需要 使用到Context对象的父容器的名字的,如果要使用ContexConfig实例来进行配置的话,Context实例必须有一个Host实例作为其父容器,简单的来说,除非你自已实现一个ContextConfig类,替换掉配置StandardContext对象的ContextConfig对象,否则你必须使用一个Host容器。

咱们搞一个 实例 试一下 重点看下 怎么使用Host容器的 两个类 一个 简单的ContextConfig 的实现 SimpleContextConfig 主要是为了 在StandardContext在启动时 将 其Configured属性赋值为true,

第二 就是咱们启动的 类 ,话不多说 搞起

第一个简单的ContextConfig类实现 simpleContextConfig

 package myex13.pyrmont.core;

 import org.apache.catalina.Context;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener; /**
* <p>
* <b>Title:SimpleContextConfig.java</b>
* </p>
* <p>
* Copyright:ChenDong 2018
* </p>
* <p>
* Company:仅学习时使用
* </p>
* <p>
* 类功能描述:简单的ContextConfig配置类,本例中的功能只是为了在StandardContext
* 在start方法执行时,为了让StandardContext可用,必须将其属性 configured 正确配置标志
* 设置为true;StandContext才能被标记为可用且启动成功
* </p>
*
* @author 陈东
* @date 2018年12月16日 下午1:16:34
* @version 1.0
*/
public class SimpleContextConfig implements LifecycleListener { /**
*
* <p>
* Title: lifecycleEvent
* </p>
*
* @date 2018年12月16日 下午1:16:34
*
* <p>
* 功能描述:
* </p>
*
* @param event
*
*/
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (Lifecycle.START_EVENT.equals(event.getType())) {
Context context = (Context) event.getLifecycle();
context.setConfigured(true);
} } }

第二个 就是咱们的启动类了

package myex13.pyrmont.startup;

import org.apache.catalina.Connector;
import org.apache.catalina.Context;
import org.apache.catalina.Host;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Loader;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.http.HttpConnector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.loader.WebappLoader; import myex13.pyrmont.core.SimpleContextConfig; /**
* <p>
* <b>Title:Bootstrap1.java</b>
* </p>
* <p>
* Copyright:ChenDong 2018
* </p>
* <p>
* Company:仅学习时使用
* </p>
* <p>
* 类功能描述: 该示例 重点在于说明Host容器最为顶层容器的使用方法,
* </p>
*
* @author 陈东
* @date 2018年12月16日 下午1:27:11
* @version 1.0
*/
public class Bootstrap1 { /**
*
* <p>
* Title: main
* </p>
*
* @date 2018年12月16日 下午1:27:11
*
* <p>
* 功能描述:
* </p>
*
* @param args
*
*/
public static void main(String[] args) {
// 设置系统属性
System.setProperty("catalina.base", System.getProperty("user.dir"));
// 实例化一个连接器
Connector connector = new HttpConnector(); // ---------------------------实例化 对应Servlet的Wrapper
// 对应 PrimitiveServlet
Wrapper wrapper1 = new StandardWrapper();
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet"); // 对应 ModernServlet Wrapper wrapper2 = new StandardWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet"); // -----------------------------实例化 Context 示例
Context context = new StandardContext();
context.setPath("/app1");
context.setDocBase("app1");
context.addChild(wrapper1);
context.addChild(wrapper2);
// 为Context配置一个监听事件 配置器 在咱们这个例子中 不做任何配置 直接将 Context的configured属性配置为ture
LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener); // 重点来了。。。。重点来了,,,,,,,重点来了 ,,,,,期盼已经的Host 在下面即将登场
Host host = new StandardHost();
host.addChild(context);
host.setName("localhost");
host.setAppBase("webapps");
Loader loader = new WebappLoader();
context.setLoader(loader);
// -------给context增加 servletMapping
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
connector.setContainer(host); try { connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) host).start(); // 方便随时停止
System.in.read();
((Lifecycle) host).stop();
} catch (Exception e) {
e.printStackTrace();
} } }

注意 为了 Host 需要为其创建根路径的 文件夹滴

看下我的文件结构

为Host容器 设置的根路径就是 webapps,然后为其子容器Context 设置的根路径为 app1.

Engine接口

  Engine容器是org.apache.catalina.Engine接口的实例。Engine容器也就是Tomat的Servlet引擎,当部署Tomcat时要支持多个虚拟机的话,就需要使用Engine容器,事实上,一般情况下,部署的Tomcat都会使用一个Engine容器

 package org.apache.catalina;

 /**
*
* <p>
* <b>Title:Engine.java</b>
* </p>
* <p>
* Copyright:ChenDong 2018
* </p>
* <p>
* Company:仅学习时使用
* </p>
* <p>
* 类功能描述:
* <h1>Engine</h1> 表示整个Container Servlet 引擎,标准实现是
* <code>org.apache.catalina.core.StandardEngine</code>
* </p>
*
* @author
* @date 2018年12月16日 下午3:30:08
* @version 1.0
*/
public interface Engine extends Container { // ------------------------------------------------------------- Properties /**
*
* 从引擎中返回一个默认的虚拟机容器名
*/
public String getDefaultHost(); /**
*
* 为引擎设置一个默认的虚拟机容器名
*
* @param defaultHost
* 新的默认的虚拟机容器名
*/
public void setDefaultHost(String defaultHost); /**
* 检索此引擎的 {@code JvmRouteId}
*/
public String getJvmRoute(); /**
* Set the JvmRouteId for this engine. 设置此引擎的 {@code JvmRouteId}
*
* @param jvmRouteId
* (新的)JVM路由ID(JvmRouteId)。集群中的每个引擎必须具有唯一的JVM路由ID(JvmRouteId)
*/
public void setJvmRoute(String jvmRouteId); /**
*
* 返回与我们相关联的 <code>Service</code> (如果有的话)
*/
public Service getService(); /**
* 设置与该引擎相关的 <code>Service</code> ,如果有的话
*
* @param service
* 引擎拥有的服务
*/
public void setService(Service service); /**
* 为新的web应用程序设置DefaultContext.
*
* @param defaultContext
* 新的 DefaultContext
*/
public void addDefaultContext(DefaultContext defaultContext); /**
* 检索新Web应用程序的DefaultContext。
*/
public DefaultContext getDefaultContext(); // --------------------------------------------------------- Public Methods /**
* 将DefaultContext配置导入到web应用程序Context文中。
*
* @param context
* 导入DefaultContext的web应用程序Context
*/
public void importDefaultContext(Context context); }

  在Engine容器中,可以设置一个默认的Host容器或者一个默认的Context容器,注意,Engine容器可以与一个服务器实例相关联,服务器咱们之后的文章再说。那么下面介绍一下 Catalina中 Engine的默认标准实现

StandardEngine

  org.apache.catalina.core.StandardEngine类时 Engine接口的标准实现,相比于StandardContext类和StandardHost类,StandardEngine类相对小一些,在实例化的时候,StandardEngine类会添加一个基础阀,如其默认的构造器

     /**
*
* 使用默认无参数构造器创建一个 设置了基础阀{@link StandardEngineValve}的{@link StandardEngine}实例
*/
public StandardEngine() { super();
pipeline.setBasic(new StandardEngineValve()); }

  作为一个顶层容器,Engine容器可以由子容器,而它的子容器只能是Host容器,所以,若是给它设置了一个非Host类型的子容器,就会抛出异常,下面是StandardEngine类的addChild方法的实现代码;

 /**
*
* 添加一个子容器,但是它的子容器仅限为 Host类型的Container
*
* @param child
* 要被添加的子容器
*/
public void addChild(Container child) {
//子容器非Host直接抛出异常
if (!(child instanceof Host))
throw new IllegalArgumentException(sm.getString("standardEngine.notHost"));
super.addChild(child); }

  此外,因为Engine容器已经是顶层容器了 所有是不可能也不允许在拥有父容器了。如果调用StandardEngine类的setParent方法,为其添加一个父容器时,就会抛出异常

 /**
*
* 因为StandardEngine已经贵为顶层容器,不可能在有父容器了,所以若setParent方法被触发 直接抛出错误
*
* @param container
* 建议父容器
*/
public void setParent(Container container) { throw new IllegalArgumentException(sm.getString("standardEngine.notParent")); }

  StandardEngineValue

org.apache.catalina.core.StandardEngineValue类时StandardEngine容器的基础阀,那么套路那还是那个老一套 ,这里就不啰嗦了,  直接看invoke方法

 public void invoke(Request request, Response response, ValveContext valveContext)
throws IOException, ServletException {
//看看 请求 和 响应 是不是有效滴
if (!(request.getRequest() instanceof HttpServletRequest)
|| !(response.getResponse() instanceof HttpServletResponse)) {
return; // NOTE - Not much else we can do generically
} // 验证任何HTTP/1.1的请求 是否包括主机头
HttpServletRequest hrequest = (HttpServletRequest) request;
if ("HTTP/1.1".equals(hrequest.getProtocol()) && (hrequest.getServerName() == null)) {
((HttpServletResponse) response.getResponse()).sendError(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHostHeader", request.getRequest().getServerName()));
return;
} // 选择用于此请求的host容器
StandardEngine engine = (StandardEngine) getContainer();
Host host = (Host) engine.map(request, true);
if (host == null) {
((HttpServletResponse) response.getResponse()).sendError(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost", request.getRequest().getServerName()));
return;
} //让 host容器的invoke方法 处理请求
host.invoke(request, response); }

有点老生常谈的感觉,老套路,在验证了request和response对象的类型之后,invoke反方得到Host实例,用于处理请求,invoke方法会通过调用Engine的map方法获取host对象,但是StandardEngine类没有实现Map反方 所以该是 去ContainerBase的map方法

基础阀invoke 调用 ContainerBase的map方法 ,然后ContainerBase的map方法 根据 请求的协议 找到对应映射器,所以下面咱们该看StandardEngineMapper的map方法了

 public Container map(Request request, boolean update) {

         int debug = engine.getDebug();

         // 提取请求的服务器名称
String server = request.getRequest().getServerName();
if (server == null) {
// 没有就搞个默认的
server = engine.getDefaultHost();
// 需要更新就让request更新一下
if (update)
request.setServerName(server);
}
// 没有server 的不用玩了 直接返回null
if (server == null)
return (null);
server = server.toLowerCase();
if (debug >= 1)
engine.log("Mapping server name '" + server + "'"); // 直接查找匹配的子子容器
if (debug >= 2)
engine.log(" Trying a direct match");
Host host = (Host) engine.findChild(server); // 通过别名查找匹配的host。
if (host == null) {
if (debug >= 2)
engine.log(" Trying an alias match");
Container children[] = engine.findChildren();
for (int i = 0; i < children.length; i++) {
String aliases[] = ((Host) children[i]).findAliases();
for (int j = 0; j < aliases.length; j++) {
if (server.equals(aliases[j])) {
host = (Host) children[i];
break;
}
}
if (host != null)
break;
}
} // 尝试使用“默认”Host
if (host == null) {
if (debug >= 2)
engine.log(" Trying the default host");
host = (Host) engine.findChild(engine.getDefaultHost());
} // Update the Request if requested, and return the selected Host
; // No update to the Request is required
return (host); }

关于Engine容器 的一些重要的点 上面基本都展示l 下面来一个示例

 package myex13.pyrmont.startup;

 import org.apache.catalina.Connector;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Loader;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.http.HttpConnector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.loader.WebappLoader; import ex20.pyrmont.standardmbeantest.StandardAgent;
import myex13.pyrmont.core.SimpleContextConfig; /**
* <p>
* <b>Title:Bootstrap2.java</b>
* </p>
* <p>
* Copyright:ChenDong 2018
* </p>
* <p>
* Company:仅学习时使用
* </p>
* <p>
* 类功能描述: 重点在于说明如何使用作为顶层容器Engine的实例,
* </p>
*
* @author 陈东
* @date 2018年12月16日 下午4:10:27
* @version 1.0
*/
public class Bootstrap2 { /**
*
* <p>
* Title: main
* </p>
*
* @date 2018年12月16日 下午4:10:27
*
* <p>
* 功能描述:
* </p>
*
* @param args
*
*/
public static void main(String[] args) {
System.setProperty("catalina.base", System.getProperty("user.dir"));
// -----------实例化连接器
Connector connector = new HttpConnector(); // 实例化Wrapepr
Wrapper wrapper1 = new StandardWrapper();
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet"); Wrapper wrapper2 = new StandardWrapper(); wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet"); // 实例化 Context
Context context = new StandardContext();
// 设置根路径
context.setPath("/app1");
// 设置根文件夹
context.setDocBase("app1");
context.addChild(wrapper1);
context.addChild(wrapper2);
// 添加配置监听器
LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener); // 实例化一个host容器
Host host = new StandardHost();
host.addChild(context);
host.setName("localhost");
// 设置host的根路径文件
host.setAppBase("webapps");
Loader loader = new WebappLoader();
context.setLoader(loader); context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern"); // 实例化一个 Engine容器
Engine engine = new StandardEngine(); engine.addChild(host);
// 设置默认的Host容器 对应上文的 Host设置的名字
engine.setDefaultHost("localhost"); connector.setContainer(engine); try {
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) engine).start(); System.in.read();
((Lifecycle) engine).stop(); } catch (Exception e) {
e.printStackTrace();
} } }

  上面已经将Host  和 Engine容器 分别介绍了与其相关的类,也粉别展示了Host  和 Enginer容器作为 Tomcat中顶层容器的使用方法。那么就先搞这些吧

Tomcat中的Host和Engine级别的servlet容器的更多相关文章

  1. how tomcat works 读书笔记(二)----------一个简单的servlet容器

    app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/arti ...

  2. tomcat中的URL参数为中文,servlet接收后显示乱码

    URL中参数的值为中文时,servlet接收后显示为乱码,如下图: 这时候需要修改tomcat的中的server.xml文件.该文件路径为 tomcat安装目录下的conf文件夹.   为修改前的se ...

  3. 攻城狮在路上(肆)How tomcat works(二) 一个简单的servlet容器

    该节在上一节的基础上增加了所谓对静态资源和动态资源访问的不同控制流程.示例里面采用的是对路径“/servlet/”进行了特殊处理. 一. 主要还是从HttpServer1中的main方法开始,先解析出 ...

  4. Tomcat中的服务器组件和 服务组件

    开始学习Tocmat时,都是学习如何通过实例化一个连接器 和 容器 来获得一个Servlet容器,并将连接器  和 servlet容器相互关联,但是之前学习的都只有一个连接器可以使用,该连接器服务80 ...

  5. 在tomcat中添加虚拟主机,myeclipse中整合jdk和tomcat

    * 虚拟主机技术  ---- 在tomcat中配置 <Host> 元素  1.下载搭建tomcat中网站 --- baidu 2.在c盘 新建虚拟主机目录 baidu , 在虚拟主机目录中 ...

  6. tomcat 中无法添加项目等问题的解决方案

    博客地址:http://www.moonxy.com 一.前言 今天新建了一个 maven 项目,添加程序文件之后,发现无法添加项目,然后修改配置,将应用添加到了 tomcat,启动时又报错,解决出现 ...

  7. tomcat(5)servlet容器

    [0]README 0.0)本文部分文字描写叙述转自:"深入剖析tomcat",旨在学习 tomcat(5)servlet容器 的基础知识. 0.1)intro to servle ...

  8. 对于Servlet、Servlet容器以及一个Servlet容器-Tomcat

    Servlet.Servlet容器等内容讲解 转载自http://blog.csdn.net/iAm333 对于Servlet.Servlet容器以及一个Servlet容器-Tomcat这些概念讲解的 ...

  9. Tomcat是一个Servlet容器?

    "Tomcat是一个Servlet容器",这句话对于2019年的程序员应该是耳熟能详的. 单纯的思考一下这句话,我们可以抽象出来这么一段代码: class Tomcat { Lis ...

随机推荐

  1. Innodb内存结构

      聚集索引与非聚集索引: 聚集索引:主键,有序,存储顺序与内存一致 非聚集索引:非主键,无序 聚集索引在叶子节点存储的是表中的数据 非聚集索引在叶子节点存储的是主键和索引列 使用非聚集索引查询出数据 ...

  2. 消息中间件RabbitMQ的使用

    原理场景 MQ在所有项目里面都很常见, 1.减少非紧急性任务对整个业务流程造成的延时: 2.减少高并发对系统所造成的性能上的影响: 举例几个场景: 1.给注册完成的用户派发优惠券.加积分.发消息等(派 ...

  3. CodeIgniter启用缓存和清除缓存的方法

    Codeigniter支持缓存技术,以达到最快的速度.尽管CI已经相当高效了,但是网页中的动态内容.主机的内存CPU和数据库读取速度等因素直接影响了网页的加载速度.依靠网页缓存,你的网页可以达到近乎静 ...

  4. POJ 1135 -- Domino Effect(单源最短路径)

     POJ 1135 -- Domino Effect(单源最短路径) 题目描述: 你知道多米诺骨牌除了用来玩多米诺骨牌游戏外,还有其他用途吗?多米诺骨牌游戏:取一 些多米诺骨牌,竖着排成连续的一行,两 ...

  5. [MyBatis]五分钟向MySql数据库插入一千万条数据 批量插入 用时5分左右

    本例代码下载:https://files.cnblogs.com/files/xiandedanteng/InsertMillionComparison20191012.rar 我的数据库环境是mys ...

  6. known

    邻接表 https://blog.csdn.net/Violet_ljp/article/details/80556460 Dijkstra 算法实现原理 https://www.jianshu.co ...

  7. 用SQL语句操作Sqlite数据库的示例代码

    import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.databa ...

  8. Android四层体系架构

    Application应用层 应用是用Java语言编写的运行在虚拟机上的程序,即图中最上层的蓝色部分.手机的上层应用其实,Google最开始时就在Android系统中捆绑了一些核心应用比如e-mail ...

  9. spark简单快速学习及打开UI界面---1

    1.远程集群测试 import org.apache.spark.{SparkContext, SparkConf} import scala.math.random /** * 利用spark进行圆 ...

  10. Requests API

    Requests API http://docs.python-requests.org/en/latest/ requests的具体安装过程请看: http://docs.python-reques ...