今天来看看Tomcat中的Request&Response设计结构,这里主要分析Request的结构,至于Response一样的思想!引用网上的一幅图!
是不是看起来很杂,看看How tomcat work提供的一幅图,相对比较简单!
作为web container,Tomcat还需要在request中保存许多内部信息及使用方法,这些信息和方法不应该暴露给web开发人员,否则会造成不必要的麻烦。
那么,Tomcat中是如何实现只向开发人员提供有限接口,而内部提供更多接口的呢?
ServletRequest: 这个接口来自javax包,是JSP Spec中定义必须要实现的。
Request: Catalina内部的Request接口,为ServletRequest提供了许多补充方法,供 Catalina内部使用。
HttpServletRequest: 来自javax包,是JSP Spec中定义作为Http的request必须要实现的。
HttpRequest: Catalina内部的Request接口,为HttpServletRequest提供了许多补充方法,供Catalina内部使用。
Default Pattern缺省模式,实际上这本身并不能算是一种模式,最多只能是一种编程方法,对于接口的所有子类,提供一个默认实现,在这里就是RequestBase及HttpRequestBase所做的工作,HttpRquestBase向HttpRquestImpl提供了HttpRequest和HttpServletRequest的默认实现。这样以后添加新的继承就只需要实现几个方法。
Adapter Pattern适配器模式,我们已经知道HttpRequestImpl中包含了HttpRequest和HttpServletRequest定义的所有方法的实现,然而对于Catalina组件,他们并不需要知道任何关于HttpServletRequest接口中定义的内容。因此,我们可以通过适配器模式将任何继承了HttpRequest和HttpServletRequest的类封装起来,并只提供HttpRequest提供的接口(可以看到HttpRequestWrapper继承HttpRequest)。这怎么理解呢?
首先从适配器模式的原理入手,(引用java与模式的一句话)适配器的作用就是做接口的转换。那么这里的转换是怎么体现出来的呢?从上面的图中可以看出HttpRequestBase与HttpRequestImpl等类都是既实现了HttpRequest接口又实现了HttpServletRequest接口,但是对于Catalina组件,它们并不需要用到HttpServletRequest的方法。看HttpquestWrapper的源码实现你就能明白了!
org.apache.catalina Interface HttpRequest
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.connector;
import java.security.Principal;
import java.util.Locale;
import javax.servlet.http.Cookie;
import org.apache.catalina.HttpRequest;
/**
* Abstract convenience class that wraps a Catalina-internal <b>HttpRequest</b>
* object. By default, all methods are delegated to the wrapped request,
* but subclasses can override individual methods as required to provide the
* functionality that they require.
*
* @author Craig R. McClanahan
* @version $Revision: 466595 $ $Date: 2006-10-21 23:24:41 +0100 (Sat, 21 Oct 2006) $
* @deprecated
*/
public abstract class HttpRequestWrapper
extends RequestWrapper
implements HttpRequest {
// ----------------------------------------------------------- Constructors
/**
* Construct a wrapper for the specified request.
*
* @param request The request to be wrapped
*/
public HttpRequestWrapper(HttpRequest request) {
super(request);
}
// --------------------------------------------------------- Public Methods
/**
* Add a Cookie to the set of Cookies associated with this Request.
*
* @param cookie The new cookie
*/
public void addCookie(Cookie cookie) {
((HttpRequest) request).addCookie(cookie);
}
/**
* Add a Header to the set of Headers associated with this Request.
*
* @param name The new header name
* @param value The new header value
*/
public void addHeader(String name, String value) {
((HttpRequest) request).addHeader(name, value);
}
/**
* Add a Locale to the set of preferred Locales for this Request. The
* first added Locale will be the first one returned by getLocales().
*
* @param locale The new preferred Locale
*/
public void addLocale(Locale locale) {
((HttpRequest) request).addLocale(locale);
}
/**
* Clear the collection of Cookies associated with this Request.
*/
public void clearCookies() {
((HttpRequest) request).clearCookies();
}
/**
* Clear the collection of Headers associated with this Request.
*/
public void clearHeaders() {
((HttpRequest) request).clearHeaders();
}
/**
* Clear the collection of Locales associated with this Request.
*/
public void clearLocales() {
((HttpRequest) request).clearLocales();
}
/**
* Set the authentication type used for this request, if any; otherwise
* set the type to <code>null</code>. Typical values are "BASIC",
* "DIGEST", or "SSL".
*
* @param type The authentication type used
*/
public void setAuthType(String type) {
((HttpRequest) request).setAuthType(type);
}
/**
* Set the context path for this Request. This will normally be called
* when the associated Context is mapping the Request to a particular
* Wrapper.
*
* @param path The context path
*/
public void setContextPath(String path) {
((HttpRequest) request).setContextPath(path);
}
/**
* Set the HTTP request method used for this Request.
*
* @param method The request method
*/
public void setMethod(String method) {
((HttpRequest) request).setMethod(method);
}
/**
* Set the query string for this Request. This will normally be called
* by the HTTP Connector, when it parses the request headers.
*
* @param query The query string
*/
public void setQueryString(String query) {
((HttpRequest) request).setQueryString(query);
}
/**
* Set the path information for this Request. This will normally be called
* when the associated Context is mapping the Request to a particular
* Wrapper.
*
* @param path The path information
*/
public void setPathInfo(String path) {
((HttpRequest) request).setPathInfo(path);
}
/**
* Set a flag indicating whether or not the requested session ID for this
* request came in through a cookie. This is normally called by the
* HTTP Connector, when it parses the request headers.
*
* @param flag The new flag
*/
public void setRequestedSessionCookie(boolean flag) {
((HttpRequest) request).setRequestedSessionCookie(flag);
}
/**
* Set the requested session ID for this request. This is normally called
* by the HTTP Connector, when it parses the request headers.
*
* @param id The new session id
*/
public void setRequestedSessionId(String id) {
((HttpRequest) request).setRequestedSessionId(id);
}
/**
* Set a flag indicating whether or not the requested session ID for this
* request came in through a URL. This is normally called by the
* HTTP Connector, when it parses the request headers.
*
* @param flag The new flag
*/
public void setRequestedSessionURL(boolean flag) {
((HttpRequest) request).setRequestedSessionURL(flag);
}
/**
* Set the unparsed request URI for this Request. This will normally be
* called by the HTTP Connector, when it parses the request headers.
*
* @param uri The request URI
*/
public void setRequestURI(String uri) {
((HttpRequest) request).setRequestURI(uri);
}
/**
* Set the servlet path for this Request. This will normally be called
* when the associated Context is mapping the Request to a particular
* Wrapper.
*
* @param path The servlet path
*/
public void setServletPath(String path) {
((HttpRequest) request).setServletPath(path);
}
/**
* Set the Principal who has been authenticated for this Request. This
* value is also used to calculate the value to be returned by the
* <code>getRemoteUser()</code> method.
*
* @param principal The user Principal
*/
public void setUserPrincipal(Principal principal) {
((HttpRequest) request).setUserPrincipal(principal);
}
}
我们说过HttpRequestBase和HttpRequestImpl类都是实现了HttpRequest和HttpServletRequest接口的,那么加入现在把HttpRequestBase实例或者是把HttpRequestImpl实例传入到HttpRequestWrapper类中,那么HttpRequestWrapper类就拥有了它们的实例。但是对于HttpRequestWrapper来说,HttpRequestBase和HttpRequestImpl实例暴露出来的接口太多了,也就是一些Catalina组件没必要用到的接口,那么这时候就把ttpRequestWrapper设计成适配器类,通过实现接口的转换,把HttpRequestBase和HttpRequestImpl要用到的接口重新封装一下,就实现了接口的转换了。
3,Facade Pattern门面模式,门面模式将内部复杂的实现隐藏起来,并提供一个简单的接口给外部的用户使用。在这里,HttpRequestFacade类将只提供HttpServletRequest中定义的方法给web开发人员。 这个模式很金典,我也是最近才掌握的!其实在Tomcat的设计中,我个人觉得它的适配器模式和门面模式设计感觉都差不多。但是实际中,这两种模式还是有一些区别的。而RequestFacade和HttpRequestFacade都是门面模式设计出来的外观类。
简单说说这样的好处,上面我们说过HttpRequestImpl类都是实现了HttpRequest和HttpServletRequest接口,也就是说HttpRequestImpl暴露了HttpRequest和HttpServletRequest接口里面的方法,但对于实际开发,我们不希望把HttpRequest接口里面定义的方法也暴露给开发人员,那么就找了一个HttpRequestFacade来一些不像暴露出来的接口给隐藏掉。我们来看看HttpRequestFacade的实现!
org.apache.catalina Interface HttpRequestFacade
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.connector;
import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import org.apache.catalina.HttpRequest;
import org.apache.catalina.session.StandardSessionFacade;
/**
* Facade class that wraps a Catalina-internal <b>HttpRequest</b>
* object. All methods are delegated to the wrapped request.
*
* @author Remy Maucherat
* @version $Revision: 466595 $ $Date: 2006-10-21 23:24:41 +0100 (Sat, 21 Oct 2006) $
*/
public final class HttpRequestFacade
extends RequestFacade
implements HttpServletRequest {
// ----------------------------------------------------------- Constructors
/**
* Construct a wrapper for the specified request.
*
* @param request The request to be wrapped
*/
public HttpRequestFacade(HttpRequest request) {
super(request);
}
// --------------------------------------------- HttpServletRequest Methods
public String getAuthType() {
return ((HttpServletRequest) request).getAuthType();
}
public Cookie[] getCookies() {
return ((HttpServletRequest) request).getCookies();
}
public long getDateHeader(String name) {
return ((HttpServletRequest) request).getDateHeader(name);
}
public String getHeader(String name) {
return ((HttpServletRequest) request).getHeader(name);
}
public Enumeration getHeaders(String name) {
return ((HttpServletRequest) request).getHeaders(name);
}
public Enumeration getHeaderNames() {
return ((HttpServletRequest) request).getHeaderNames();
}
public int getIntHeader(String name) {
return ((HttpServletRequest) request).getIntHeader(name);
}
public String getMethod() {
return ((HttpServletRequest) request).getMethod();
}
public String getPathInfo() {
return ((HttpServletRequest) request).getPathInfo();
}
public String getPathTranslated() {
return ((HttpServletRequest) request).getPathTranslated();
}
public String getContextPath() {
return ((HttpServletRequest) request).getContextPath();
}
public String getQueryString() {
return ((HttpServletRequest) request).getQueryString();
}
public String getRemoteUser() {
return ((HttpServletRequest) request).getRemoteUser();
}
public boolean isUserInRole(String role) {
return ((HttpServletRequest) request).isUserInRole(role);
}
public java.security.Principal getUserPrincipal() {
return ((HttpServletRequest) request).getUserPrincipal();
}
public String getRequestedSessionId() {
return ((HttpServletRequest) request).getRequestedSessionId();
}
public String getRequestURI() {
return ((HttpServletRequest) request).getRequestURI();
}
public StringBuffer getRequestURL() {
return ((HttpServletRequest) request).getRequestURL();
}
public String getServletPath() {
return ((HttpServletRequest) request).getServletPath();
}
public HttpSession getSession(boolean create) {
HttpSession session =
((HttpServletRequest) request).getSession(create);
if (session == null)
return null;
else
return new StandardSessionFacade(session);
}
public HttpSession getSession() {
return getSession(true);
}
public boolean isRequestedSessionIdValid() {
return ((HttpServletRequest) request).isRequestedSessionIdValid();
}
public boolean isRequestedSessionIdFromCookie() {
return ((HttpServletRequest) request).isRequestedSessionIdFromCookie();
}
public boolean isRequestedSessionIdFromURL() {
return ((HttpServletRequest) request).isRequestedSessionIdFromURL();
}
public boolean isRequestedSessionIdFromUrl() {
return ((HttpServletRequest) request).isRequestedSessionIdFromURL();
}
}
假如这时把HttpRequestImpl实例传入到HttpRequestFacade类来,那么在HttpRequestFacade类中就去访问HttpRequestImpl实例的任何方法,但是对于我们的要求是不能把HttpRequestImpl的所有接口都暴露出来,那么这时利用门面模式把HttpRequestFacade设计成一个外观类,并且去实现HttpServletRequest接口,那么它就只暴露了HttpServletRequest接口里面的方法而已,而巧妙的把HttpRequest接口的方法给隐藏起来了!而且HttpRequestImpl是不对外公开的!
引用别人总结的话:适配器着重于将一个接口转换成另一个接口,门面模式则着重于将复杂的内部实现隐藏起来,提供一个简单的通用接口。
其实上面的这两种模式,在多线程并发也有很大的作用,具体我会在后面的多线程中进行总结!
好了,设计模式的东西就讲到这里了,下一节看看如果具体去实现HttpRequestImpl和HttpResponseImpl的方法。