现在做网站有前途吗/山东泰安网络推广
从零开始手写Tomcat的教程13节---Host和Engine
- Host接口
- StandardHost实现类
- StandardHostValve类
- StandardHostWrapper类
- 为什么必须要有一个Host容器
- 应用程序
- 小结
- Engine接口
- StandardEngine类
- StandardEngineValve类
- 应用程序
- 小结
Host接口
public interface Host extends Container {// ----------------------------------------------------- Manifest Constants/*** The ContainerEvent event type sent when a new alias is added* by <code>addAlias()</code>.*/public static final String ADD_ALIAS_EVENT = "addAlias";/*** The ContainerEvent event type sent when an old alias is removed* by <code>removeAlias()</code>.*/public static final String REMOVE_ALIAS_EVENT = "removeAlias";// ------------------------------------------------------------- Properties/*** Return the application root for this Host. This can be an absolute* pathname, a relative pathname, or a URL.*/public String getAppBase();/*** Set the application root for this Host. This can be an absolute* pathname, a relative pathname, or a URL.** @param appBase The new application root*/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);/*** Set the DefaultContext* for new web applications.** @param defaultContext The new DefaultContext*/public void addDefaultContext(DefaultContext defaultContext);/*** Retrieve the DefaultContext for new web applications.*/public DefaultContext getDefaultContext();/*** Return the canonical, fully qualified, name of the virtual host* this Container represents.*/public String getName();/*** Set the canonical, fully qualified, name of the virtual host* this Container represents.** @param name Virtual host name** @exception IllegalArgumentException if name is null*/public void setName(String name);// --------------------------------------------------------- Public Methods/*** Import the DefaultContext config into a web application context.** @param context web application context to import default context*/public void importDefaultContext(Context context);/*** Add an alias name that should be mapped to this same Host.** @param alias The alias to be added*/public void addAlias(String alias);/*** Return the set of alias names for this Host. If none are defined,* a zero length array is returned.*/public String[] findAliases();/*** Return the Context that would be used to process the specified* host-relative request URI, if any; otherwise return <code>null</code>.** @param uri Request URI to be mapped*/public Context map(String uri);/*** Remove the specified alias name from the aliases for this Host.** @param alias Alias name to be removed*/public void removeAlias(String alias);}
StandardHost实现类
/*** Create a new StandardHost component with the default basic Valve.*/public StandardHost() {super();pipeline.setBasic(new StandardHostValve());}
<Host name="www.dhy.com" appBase="dhy"unpackWARs="true" autoDeploy="true"><context path="/xpy" docBase="xpy" ></context></Host>
Deployer接口具体是干啥的,大家看了上面的xml配置,估计就会明白了。
Host对应一个域名,该域名在操作系统层面的文件系统中映射到一个目录,该目录下可以有很多子目录,这些子目录会挨个映射到当前Host下面所管理的context容器上去
/*** Start this host*/public synchronized void start() throws LifecycleException {// Set error report valveif ((errorReportValveClass != null)&& (!errorReportValveClass.equals(""))) {try {Valve valve = (Valve) Class.forName(errorReportValveClass).newInstance();addValve(valve);} catch (Throwable t) {log(sm.getString("standardHost.invalidErrorReportValveClass", errorReportValveClass));}}// Set dispatcher valveaddValve(new ErrorDispatcherValve());super.start();}
/*** The Java class name of the default error reporter implementation class * for deployed web applications.*/private String errorReportValveClass ="org.apache.catalina.valves.ErrorReportValve";
这里Host的start初始化方法主要就是添加了两个关于错误日志处理的阀门,随后调用父类ContainerBase的start方法,启动相关组件,调用子容器的start初始化方法,因为这些逻辑都是通用的,因此都放到了父类的start方法中实现
父类ContainerBase的start方法/*** Prepare for active use of the public methods of this Component.** @exception LifecycleException if this component detects a fatal error* that prevents it from being started*/@Overridepublic synchronized void start() throws LifecycleException {// Validate and update our current component stateif (started)throw new LifecycleException(sm.getString("containerBase.alreadyStarted", logName()));// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);addDefaultMapper(this.mapperClass);started = true;// Start our subordinate components, if anyif ((loader != null) && (loader instanceof Lifecycle))((Lifecycle) loader).start();if ((logger != null) && (logger instanceof Lifecycle))((Lifecycle) logger).start();if ((manager != null) && (manager instanceof Lifecycle))((Lifecycle) manager).start();if ((cluster != null) && (cluster instanceof Lifecycle))((Lifecycle) cluster).start();if ((realm != null) && (realm instanceof Lifecycle))((Lifecycle) realm).start();if ((resources != null) && (resources instanceof Lifecycle))((Lifecycle) resources).start();// Start our Mappers, if anyMapper mappers[] = findMappers();for (int i = 0; i < mappers.length; i++) {if (mappers[i] instanceof Lifecycle)((Lifecycle) mappers[i]).start();}// Start our child containers, if anyContainer children[] = findChildren();for (int i = 0; i < children.length; i++) {if (children[i] instanceof Lifecycle)((Lifecycle) children[i]).start();}// Start the Valves in our pipeline (including the basic), if anyif (pipeline instanceof Lifecycle)((Lifecycle) pipeline).start();// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(START_EVENT, null);// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);}
ContainerBase类的invoke方法因为是通用逻辑抽取,因此实现是固定的:
public void invoke(Request request, Response response)throws IOException, ServletException {//调用当前容器管道中的阀门进行处理pipeline.invoke(request, response);}
StandardHostValve类
StandardHostValve的invoke方法如下:
public void invoke(Request request, Response response,ValveContext valveContext)throws IOException, ServletException {// Validate the request and response object typesif (!(request.getRequest() instanceof HttpServletRequest) ||!(response.getResponse() instanceof HttpServletResponse)) {return; // NOTE - Not much else we can do generically}// Select the Context to be used for this RequestStandardHost host = (StandardHost) getContainer();//根据map方法找到当前请求对应的context对象Context context = (Context) host.map(request, true);if (context == null) {((HttpServletResponse) response.getResponse()).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,sm.getString("standardHost.noContext"));return;}// Bind the context CL to the current threadThread.currentThread().setContextClassLoader(context.getLoader().getClassLoader());//如果存在session的话,更新最后一次访问的时间// Update the session last access time for our session (if any)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();}}// Ask this Context to process this request//将当前请求接着传递给找到的context对象进行处理context.invoke(request, response);}
这里调用的其实是父类ContainerBase的map方法:
public Container map(Request request, boolean update) {// Select the Mapper we will useMapper mapper = findMapper(request.getRequest().getProtocol());if (mapper == null)return (null);// Use this Mapper to perform this mapping//这里最终调用的是StandardHostMapper的map方法,下面再分析,这里最终传入的其实就是请求URI中的context路径部分return (mapper.map(request, update));}
StandardHost类的map方法:
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 prefixif (debug > 1)log(" Trying the longest context path prefix");Context context = null;//拿到当前请求context路径部分String mapuri = uri;while (true) {//去子容器中找到该contextcontext = (Context) findChild(mapuri);if (context != null)break;int slash = mapuri.lastIndexOf('/');if (slash < 0)break;mapuri = mapuri.substring(0, slash);}// If no Context matches, select the default Contextif (context == null) {if (debug > 1)log(" Trying the default context");context = (Context) findChild("");}// Complain if no Context has been selectedif (context == null) {log(sm.getString("standardHost.mappingError", uri));return (null);}// Return the mapped Context (if any)if (debug > 0)log(" Mapped to context '" + context.getPath() + "'");return (context);}
StandardHostWrapper类
说明继承了ContainerBase的容器,只要调用了父类的start方法,就会添加一个默认的映射器
protected void addDefaultMapper(String mapperClass) {// Do we need a default Mapper?if (mapperClass == null)return;if (mappers.size() >= 1)return;// Instantiate and add a default Mappertry {Class clazz = Class.forName(mapperClass);Mapper mapper = (Mapper) clazz.newInstance();mapper.setProtocol("http");addMapper(mapper);} catch (Exception e) {log(sm.getString("containerBase.addDefaultMapper", mapperClass),e);}}
/*** The Java class name of the default Mapper class for this Container.*/private String mapperClass ="org.apache.catalina.core.StandardHostMapper";
上面源码已经给出过了
关于这一点可以参考第12节
public Container map(Request request, boolean update) {// Has this request already been mapped?if (update && (request.getContext() != null))return (request.getContext());// Perform mapping on our request URIString uri = ((HttpRequest) request).getDecodedRequestURI();//调用Host的map方法获得当前URI对应的context对象Context context = host.map(uri);// Update the request (if requested) and return the selected Contextif (update) {request.setContext(context);if (context != null)((HttpRequest) request).setContextPath(context.getPath());else((HttpRequest) request).setContextPath(null);}return (context);}
为什么必须要有一个Host容器
applicationConfig方法主要是来解析web.xml配置文件的,该方法在start方法中被调用,start方法在ContextConfig监听到Context容器的启动的start事件时,被调用。
应用程序
connector改为关联一个Host了,然后会设置Host下面的context,host对应的域名和目录名
小结
Host洋洋洒洒讲了那么多,下面对Host这部分内容做一个小结先:
Engine接口
public interface Engine extends Container {// ------------------------------------------------------------- Properties/*** Return the default hostname for this Engine.*/public String getDefaultHost();/*** Set the default hostname for this Engine.** @param defaultHost The new default host*/public void setDefaultHost(String defaultHost);/*** Retrieve the JvmRouteId for this engine.*/public String getJvmRoute();/*** Set the JvmRouteId for this engine.** @param jvmRouteId the (new) JVM Route ID. Each Engine within a cluster* must have a unique JVM Route ID.*/public void setJvmRoute(String jvmRouteId);/*** Return the <code>Service</code> with which we are associated (if any).*/public Service getService();/*** Set the <code>Service</code> with which we are associated (if any).** @param service The service that owns this Engine*/public void setService(Service service);/*** Set the DefaultContext* for new web applications.** @param defaultContext The new DefaultContext*/public void addDefaultContext(DefaultContext defaultContext);/*** Retrieve the DefaultContext for new web applications.*/public DefaultContext getDefaultContext();// --------------------------------------------------------- Public Methods/*** Import the DefaultContext config into a web application context.** @param context web application context to import default context*/public void importDefaultContext(Context context);}
StandardEngine类
/*** Create a new StandardEngine component with the default basic Valve.*/public StandardEngine() {super();pipeline.setBasic(new StandardEngineValve());}
/*** Add a child Container, only if the proposed child is an implementation* of Host.** @param child Child container to be added*/public void addChild(Container child) {if (!(child instanceof Host))throw new IllegalArgumentException(sm.getString("standardEngine.notHost"));super.addChild(child);}
/*** Disallow any attempt to set a parent for this Container, since an* Engine is supposed to be at the top of the Container hierarchy.** @param container Proposed parent Container*/public void setParent(Container container) {throw new IllegalArgumentException(sm.getString("standardEngine.notParent"));}
StandardEngineValve类
public void invoke(Request request, Response response,ValveContext valveContext)throws IOException, ServletException {// Validate the request and response object typesif (!(request.getRequest() instanceof HttpServletRequest) ||!(response.getResponse() instanceof HttpServletResponse)) {return; // NOTE - Not much else we can do generically}// Validate that any HTTP/1.1 request included a host headerHttpServletRequest 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;}// Select the Host to be used for this RequestStandardEngine engine = (StandardEngine) getContainer();//通过request找到对应的host,不用我多说了吧,和context的步骤基本一致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;}// Ask this Host to process this requesthost.invoke(request, response);}
应用程序