前言
今天我们来看看Tomcat是如何处理请求的。之前已经讲到了 adapter.service(request, response);了,我们就接着这里入手,我们知道adapter对应的是一个CoyoteAdapter。
CoyoteAdapter
service(request, response)
/**
* Service method.
*/
@Override
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res)
throws Exception {
//一开始就是将org.apache.coyote.Request/Response转为org.apache.catalina.connector.Request/Response
//
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
if (request == null) {
// Create objects
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
// Set query string encoding
req.getParameters().setQueryStringEncoding
(connector.getURIEncoding());
}
postParseSuccess = postParseRequest(req, request, res, response);
// Calling the container
//然后就是开始调用请求相应的Servlet的 connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
}
} catch (IOException e) {
// Ignore
} finally {
}
}
}
这个方法的作用:
1.将Request转化为继承了HttpServletRequest的org.apache.catalina.connector.Request
2.通过postParseRequest(req, request, res, response);得到对应的context和warpper
3.调用了相应的方法来处理请求。即connector.getService().getContainer()..getFirst().invoke(request, response);这是我们重点要关注的内容
第一条就不多说了
我们先说第二条
postParseRequest
while (mapRequired) {
// This will map the the latest version by default
connector.getMapper().map(serverName, decodedURI, version,
request.getMappingData());
request.setContext((Context) request.getMappingData().context);
request.setWrapper((Wrapper) request.getMappingData().wrapper);
// If there is no context at this point, it is likely no ROOT context
// has been deployed
if (request.getContext() == null) {
res.setStatus(404);
res.setMessage("Not found");
// No context, so use host
Host host = request.getHost();
// Make sure there is a host (might not be during shutdown)
if (host != null) {
host.logAccess(request, response, 0, true);
}
return false;
}
这里就给出了该请求的context和warpper,这里就涉及到了如何找到request请求对应的Servlet了。
我们需要关注
connector.getMapper().map(serverName, decodedURI, version, request.getMappingData());
通过这个方法找到对应的Servlet。
我们来分析一下这个方法:
connector.getMapper()调用的是Connector类的mapper属性
/**
* Mapper.
*/
protected Mapper mapper = new Mapper();
/**
* Mapper listener.
*/
protected MapperListener mapperListener = new MapperListener(mapper, this);
所以调用的是Mapper的mapper()方法
看到这个方法名,我们就可以想到,肯定是去到一个集合里面去匹配我们对应的Servlet和Context等信息。那么我们就必须要知道全部的Servlet等信息是何时放入Mapper的集合里面的。
到底是在哪里注册了这些信息到Mapper里呢。
我们要回到connector.start()
我们看到Connector的startInternal()方法,我们可以看到方法的最后
@Override
protected void startInternal() throws LifecycleException {
// Validate settings before starting
if (getPort() < 0) {
throw new LifecycleException(sm.getString(
"coyoteConnector.invalidPort", Integer.valueOf(getPort())));
}
setState(LifecycleState.STARTING);
try {
protocolHandler.start();
} catch (Exception e) {
String errPrefix = "";
if(this.service != null) {
errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
}
throw new LifecycleException
(errPrefix + " " + sm.getString
("coyoteConnector.protocolHandlerStartFailed"), e);
}
mapperListener.start();
}
最后调用了mapperListener.start();
而mapperListener的初始化用到了Mapper
protected MapperListener mapperListener = new MapperListener(mapper, this);
是的,注册信息到Mapper里就是通过mapperListener.start();
mapperListener.start()
@Override
public void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
// Find any components that have already been initialized since the
// MBean listener won't be notified as those components will have
// already registered their MBeans
findDefaultHost();
Engine engine = (Engine) connector.getService().getContainer();
addListeners(engine);
Container[] conHosts = engine.findChildren();
for (Container conHost : conHosts) {
Host host = (Host) conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
// Registering the host will register the context and wrappers
registerHost(host);
}
}
}
private void registerHost(Host host) {
String[] aliases = host.findAliases();
mapper.addHost(host.getName(), aliases, host);
for (Container container : host.findChildren()) {
if (container.getState().isAvailable()) {
registerContext((Context) container);
}
}
if(log.isDebugEnabled()) {
log.debug(sm.getString("mapperListener.registerHost",
host.getName(), domain, connector));
}
}
private void registerContext(Context context) {
String contextPath = context.getPath();
if ("/".equals(contextPath)) {
contextPath = "";
}
Container host = context.getParent();
javax.naming.Context resources = context.getResources();
String[] welcomeFiles = context.findWelcomeFiles();
List<WrapperMappingInfo> wrappers = new ArrayList<WrapperMappingInfo>();
for (Container container : context.findChildren()) {
prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);
if(log.isDebugEnabled()) {
log.debug(sm.getString("mapperListener.registerWrapper",
container.getName(), contextPath, connector));
}
}
mapper.addContextVersion(host.getName(), host, contextPath,
context.getWebappVersion(), context, welcomeFiles, resources,
wrappers, context.getMapperContextRootRedirectEnabled(),
context.getMapperDirectoryRedirectEnabled());
if(log.isDebugEnabled()) {
log.debug(sm.getString("mapperListener.registerContext",
contextPath, connector));
}
}
这段代码就是将Host,Context以及Warpper都注册到Mapper中,我们这里着重讲一下Warpper的注册。
registerContext
先通过prepareWrapperMappingInfo将Warpper包装成WrapperMappingInfo,然后再通过addContextVersion将WrapperMappingInfo注册到Mapper中
mapper.addContextVersion
public void addContextVersion(String hostName, Object host, String path,
String version, Object context, String[] welcomeResources,
javax.naming.Context resources, Collection<WrapperMappingInfo> wrappers,
boolean mapperContextRootRedirectEnabled, boolean mapperDirectoryRedirectEnabled) {
Host mappedHost = exactFind(hosts, hostName);
//...省略了大段代码
synchronized (mappedHost) {
ContextVersion newContextVersion = new ContextVersion(version, context);
newContextVersion.path = path;
newContextVersion.slashCount = slashCount;
newContextVersion.welcomeResources = welcomeResources;
newContextVersion.resources = resources;
newContextVersion.mapperContextRootRedirectEnabled = mapperContextRootRedirectEnabled;
newContextVersion.mapperDirectoryRedirectEnabled = mapperDirectoryRedirectEnabled;
if (wrappers != null) {
addWrappers(newContextVersion, wrappers);
}
}
主要是addWrappers(newContextVersion, wrappers);完成了注册
private void addWrappers(ContextVersion contextVersion,
Collection<WrapperMappingInfo> wrappers) {
for (WrapperMappingInfo wrapper : wrappers) {
addWrapper(contextVersion, wrapper.getMapping(),
wrapper.getWrapper(), wrapper.isJspWildCard(),
wrapper.isResourceOnly());
}
}
终于到了最终注册的方法
addWrapper()
protected void addWrapper(ContextVersion context, String path,
Object wrapper, boolean jspWildCard, boolean resourceOnly) {
synchronized (context) {
if (path.endsWith("/*")) {
// Wildcard wrapper
String name = path.substring(0, path.length() - 2);
Wrapper newWrapper = new Wrapper(name, wrapper, jspWildCard,
resourceOnly);
Wrapper[] oldWrappers = context.wildcardWrappers;
Wrapper[] newWrappers =
new Wrapper[oldWrappers.length + 1];
if (insertMap(oldWrappers, newWrappers, newWrapper)) {
context.wildcardWrappers = newWrappers;
int slashCount = slashCount(newWrapper.name);
if (slashCount > context.nesting) {
context.nesting = slashCount;
}
}
} else if (path.startsWith("*.")) {
// Extension wrapper
String name = path.substring(2);
Wrapper newWrapper = new Wrapper(name, wrapper, jspWildCard,
resourceOnly);
Wrapper[] oldWrappers = context.extensionWrappers;
Wrapper[] newWrappers =
new Wrapper[oldWrappers.length + 1];
if (insertMap(oldWrappers, newWrappers, newWrapper)) {
context.extensionWrappers = newWrappers;
}
} else if (path.equals("/")) {
// Default wrapper
Wrapper newWrapper = new Wrapper("", wrapper, jspWildCard,
resourceOnly);
context.defaultWrapper = newWrapper;
} else {
// Exact wrapper
final String name;
if (path.length() == 0) {
// Special case for the Context Root mapping which is
// treated as an exact match
name = "/";
} else {
name = path;
}
Wrapper newWrapper = new Wrapper(name, wrapper, jspWildCard,
resourceOnly);
Wrapper[] oldWrappers = context.exactWrappers;
Wrapper[] newWrappers =
new Wrapper[oldWrappers.length + 1];
if (insertMap(oldWrappers, newWrappers, newWrapper)) {
context.exactWrappers = newWrappers;
}
}
}
}
这个方法代码也非常多,他主要做了以下工作:
如果请求的uri是/*结尾的话就将Warpper插入到wildcardWrappers这个Warpper数组中
如果是以*.开始的话就将Warpper插入到extensionWrappers中
如果是/的话就将Warpper插入到defaultWrapper
如果是指定的url,没有使用通配符的话,就插入到exactWrappers中
到现在我们了解了Servlet,Context信息是如何注册到Mapper中了,现在可以回头来看Mapperd的mapper()方法了
mapper()
public void map(MessageBytes host, MessageBytes uri, String version,
MappingData mappingData)
throws Exception {
if (host.isNull()) {
host.getCharChunk().append(defaultHostName);
}
host.toChars();
uri.toChars();
internalMap(host.getCharChunk(), uri.getCharChunk(), version,
mappingData);
}
private final void internalMap(CharChunk host, CharChunk uri,
String version, MappingData mappingData) throws Exception {
//...省略大段代码
// Wrapper mapping
if (!contextVersion.isPaused()) {
internalMapWrapper(contextVersion, uri, mappingData);
}
}
我们主要关注查找匹配的Servlet的方法internalMapWrapper
internalMapWrapper
/**
* Wrapper mapping.
*/
private final void internalMapWrapper(ContextVersion contextVersion,
CharChunk path,
MappingData mappingData)
throws Exception {
int pathOffset = path.getOffset();
int pathEnd = path.getEnd();
boolean noServletPath = false;
int length = contextVersion.path.length();
if (length == (pathEnd - pathOffset)) {
noServletPath = true;
}
int servletPath = pathOffset + length;
path.setOffset(servletPath);
// Rule 1 -- Exact Match
Wrapper[] exactWrappers = contextVersion.exactWrappers;
internalMapExactWrapper(exactWrappers, path, mappingData);
// Rule 2 -- Prefix Match
boolean checkJspWelcomeFiles = false;
Wrapper[] wildcardWrappers = contextVersion.wildcardWrappers;
if (mappingData.wrapper == null) {
internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,
path, mappingData);
if (mappingData.wrapper != null && mappingData.jspWildCard) {
char[] buf = path.getBuffer();
if (buf[pathEnd - 1] == '/') {
/*
* Path ending in '/' was mapped to JSP servlet based on
* wildcard match (e.g., as specified in url-pattern of a
* jsp-property-group.
* Force the context's welcome files, which are interpreted
* as JSP files (since they match the url-pattern), to be
* considered. See Bugzilla 27664.
*/
mappingData.wrapper = null;
checkJspWelcomeFiles = true;
} else {
// See Bugzilla 27704
mappingData.wrapperPath.setChars(buf, path.getStart(),
path.getLength());
mappingData.pathInfo.recycle();
}
}
}
if(mappingData.wrapper == null && noServletPath &&
contextVersion.mapperContextRootRedirectEnabled) {
// The path is empty, redirect to "/"
path.append('/');
pathEnd = path.getEnd();
mappingData.redirectPath.setChars
(path.getBuffer(), pathOffset, pathEnd - pathOffset);
path.setEnd(pathEnd - 1);
return;
}
// Rule 3 -- Extension Match
Wrapper[] extensionWrappers = contextVersion.extensionWrappers;
if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
internalMapExtensionWrapper(extensionWrappers, path, mappingData,
true);
}
// Rule 4 -- Welcome resources processing for servlets
if (mappingData.wrapper == null) {
boolean checkWelcomeFiles = checkJspWelcomeFiles;
if (!checkWelcomeFiles) {
char[] buf = path.getBuffer();
checkWelcomeFiles = (buf[pathEnd - 1] == '/');
}
if (checkWelcomeFiles) {
for (int i = 0; (i < contextVersion.welcomeResources.length)
&& (mappingData.wrapper == null); i++) {
path.setOffset(pathOffset);
path.setEnd(pathEnd);
path.append(contextVersion.welcomeResources[i], 0,
contextVersion.welcomeResources[i].length());
path.setOffset(servletPath);
// Rule 4a -- Welcome resources processing for exact macth
internalMapExactWrapper(exactWrappers, path, mappingData);
// Rule 4b -- Welcome resources processing for prefix match
if (mappingData.wrapper == null) {
internalMapWildcardWrapper
(wildcardWrappers, contextVersion.nesting,
path, mappingData);
}
// Rule 4c -- Welcome resources processing
// for physical folder
if (mappingData.wrapper == null
&& contextVersion.resources != null) {
Object file = null;
String pathStr = path.toString();
try {
file = contextVersion.resources.lookup(pathStr);
} catch(NamingException nex) {
// Swallow not found, since this is normal
}
if (file != null && !(file instanceof DirContext) ) {
internalMapExtensionWrapper(extensionWrappers, path,
mappingData, true);
if (mappingData.wrapper == null
&& contextVersion.defaultWrapper != null) {
mappingData.wrapper =
contextVersion.defaultWrapper.object;
mappingData.requestPath.setChars
(path.getBuffer(), path.getStart(),
path.getLength());
mappingData.wrapperPath.setChars
(path.getBuffer(), path.getStart(),
path.getLength());
mappingData.requestPath.setString(pathStr);
mappingData.wrapperPath.setString(pathStr);
}
}
}
}
path.setOffset(servletPath);
path.setEnd(pathEnd);
}
}
代码很长,其实就是规定了几个匹配的规则:
Rule1:先按照指定的url匹配,看是否有注册完整的相同url对应的Servlet。即在extartWappers里面找
Rule2:匹配/*通配符,在wildcardWrappers中查找
Rule3:按照后缀名匹配 .jsp ,.jspx等,即在extensionWrappers中查找。
Rule4:在指定欢迎页面中查找:
Rule7: 按照/匹配,即defaultWrapper
所以按照顺序 /*的优先级要比/高,这一点在讲解SpringMVC的时候会说,这一点很重要。
好了,在这里就把如何查找到request匹配的Servlet等信息讲解清楚了,将这些信息注册到request中之后,我们可以关注下一步操作了。
首先我们可以很容器的知道connector.getService().getContainer().得到的是StandardEngine实例,那我们先把目光转移到这个类上。
我们发现这个类并没有实现getPipeline()方法,这个方法的实现在其父类ContainerBase中
public Pipeline getPipeline() {
return (this.pipeline);
}
就是得到这个类的pipeline属性。
那当前这个类的pipeline属性是在哪里注入的呢。答案是初始化的时候。
public StandardEngine() {
super();
pipeline.setBasic(new StandardEngineValve());
/* Set the jmvRoute using the system property jvmRoute */
try {
setJvmRoute(System.getProperty("jvmRoute"));
} catch(Exception ex) {
log.warn(sm.getString("standardEngine.jvmRouteFail"));
}
// By default, the engine will hold the reloading thread
backgroundProcessorDelay = 10;
}
//StandardPipeline
@Override
public Valve getFirst() {
if (first != null) {
return first;
}
return basic;
}
通过上面的代码我们可以得出connector.getService().getContainer()..getFirst()对应的是StandardEngineValve实例。
我们把目光转向StandardEngineValve的invoke()方法
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Host to be used for this Request
Host host = request.getHost();
if (host == null) {
response.sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
// Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}
我们发现没有做什么实际性的处理,而是将任务交由子容器Host处理。
我们看源码发现 host.getPipeline().getFirst()对应的是StandardHostValve,和上面一样,依然是把实际的处理交给子容器,这样的设计是不是让你想起了责任链模式呢,不慌,之后还有更流畅的责任链模式。
context.getPipeline().getFirst().invoke(request, response);
中间过程就不分析了,直接来到StandardWarpperValve的invoke()
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
try {
if (!unavailable) {
servlet = wrapper.allocate();
}
// Create the filter chain for this request
ApplicationFilterFactory factory =
ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain =
factory.createFilterChain(request, wrapper, servlet);
// Reset comet flag value after creating the filter chain
request.setComet(false);
// Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
try {
if ((servlet != null) && (filterChain != null)) {
// Swallow output if needed
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else if (comet) {
filterChain.doFilterEvent(request.getEvent());
request.setComet(true);
} else {
filterChain.doFilter(request.getRequest(),
response.getResponse());
}
} finally {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
context.getLogger().info(log);
}
}
} else {
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else if (comet) {
request.setComet(true);
filterChain.doFilterEvent(request.getEvent());
} else {
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
}
}
}
}
}
这个方法的代码非常长,我只拿了一小部分过来
这个方法的主要作用是:
1.通过 servlet = wrapper.allocate();得到servlet
2.创建一个FilterChain链实例
3.调用filterChain.doFilter (request.getRequest(), response.getResponse());
wrapper.allocate();
@Override
public Servlet allocate() throws ServletException {
// If we are currently unloading this servlet, throw an exception
if (unloading) {
throw new ServletException(sm.getString("standardWrapper.unloading", getName()));
}
boolean newInstance = false;
// If not SingleThreadedModel, return the same instance every time
if (!singleThreadModel) {
// Load and initialize our instance if necessary
if (instance == null || !instanceInitialized) {
synchronized (this) {
if (instance == null) {
try {
if (log.isDebugEnabled()) {
log.debug("Allocating non-STM instance");
}
// Note: We don't know if the Servlet implements
// SingleThreadModel until we have loaded it.
instance = loadServlet();
if (!instanceInitialized) {
initServlet(instance);
}
}
}
}
instance = loadServlet();完成了实例化Servlet的工作
loadServlet()
““java
public synchronized Servlet loadServlet() throws ServletException {
Servlet servlet;
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
servlet = (Servlet) instanceManager.newInstance(servletClass);
} catch (ClassCastException e) {
unavailable(null);
// Restore the context ClassLoader
}
processServletSecurityAnnotation(servlet.getClass());
initServlet(servlet);
return servlet;
}
““
servlet = (Servlet) instanceManager.newInstance(servletClass);
得到了Servlet实例,接着还有一个方法 initServlet(servlet);这个方法我们需要关注一下:
这个方法最终其实就是调用了servlet.init(ServletConfig config),这个方法,我们肯定不陌生,只要我们接触过Servlet就一定会了解这个方法的作用,我们可以在这个方法里面通过config得到我们在servlet节点里面配置的init-param的键值对,取出我们想要的值,可是,你在使用Servlet的init()方法时,有想过到底是怎么做到的吗,下面我们就来讲讲是怎样做到的。
““java
private synchronized void initServlet(Servlet servlet)
throws ServletException {
if (instanceInitialized && !singleThreadModel) return;
// Call the initialization method of this servlet
try {
instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,
servlet);
if( Globals.IS_SECURITY_ENABLED) {
boolean success = false;
try {
Object[] args = new Object[] { facade };
SecurityUtil.doAsPrivilege("init",
servlet,
classType,
args);
success = true;
} finally {
if (!success) {
// destroy() will not be called, thus clear the reference now
SecurityUtil.remove(servlet);
}
}
} else {
servlet.init(facade);
}
instanceInitialized = true;
instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
servlet);
} catch (UnavailableException f) {
instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
servlet, f);
unavailable(f);
throw f;
} catch (ServletException f) {
instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
servlet, f);
// If the servlet wanted to be unavailable it would have
// said so, so do not call unavailable(null).
throw f;
} catch (Throwable f) {
ExceptionUtils.handleThrowable(f);
getServletContext().log("StandardWrapper.Throwable", f );
instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
servlet, f);
// If the servlet wanted to be unavailable it would have
// said so, so do not call unavailable(null).
throw new ServletException
(sm.getString("standardWrapper.initException", getName()), f);
}
}
““
虽然代码很长,我们需要关注的只有这一行 servlet.init(facade);
servlet.init(facade);
很明显我们的注意力要集中到facade上,很明显是相关的配置参数信息已经注册到了facade中了,这样我们才能在程序中能够获取得到相关参数的值。
我们来看这个facade对应的实现类到底是什么,参看StandardWarpper的源码可知
protected StandardWrapperFacade facade =
new StandardWrapperFacade(this);
facade是StandardWarpperFacade实例,并且实例化的时候注入了StandardWarpper对象。
我们进入StandardWarpperFacade,查看他对ServletConfig接口方法的实现
public final class StandardWrapperFacade
implements ServletConfig {
// ----------------------------------------------------------- Constructors
/**
* Create a new facade around a StandardWrapper.
*/
public StandardWrapperFacade(StandardWrapper config) {
super();
this.config = config;
}
// ----------------------------------------------------- Instance Variables
/**
* Wrapped config.
*/
private ServletConfig config = null;
/**
* Wrapped context (facade).
*/
private ServletContext context = null;
// -------------------------------------------------- ServletConfig Methods
@Override
public String getServletName() {
return config.getServletName();
}
@Override
public ServletContext getServletContext() {
if (context == null) {
context = config.getServletContext();
if ((context != null) && (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();
}
}
我们会发现,其实都是调用的StandardWarpper中的方法,只是做了一下封装而已,StandardWarpper也实现了ServletConfig接口。
首先看StandardWarpper的getServletContext(),看到底是怎样得到ServletContext接口的实现类的,这个号称是Servlet上下文的类到底是什么呢。
/**
* Return the servlet context with which this servlet is associated.
*/
@Override
public ServletContext getServletContext() {
if (parent == null)
return (null);
else if (!(parent instanceof Context))
return (null);
else
return (((Context) parent).getServletContext());
}
我们发现是调用的父容器StandardContext的getServletContext()方法,这样就对了嘛,要不然怎么能称得上是上下文呢,如果在Servlet这一层就直接取到了,那只能在单个的Servlet中使用了。
StandardContext.getServletContext()
@Override
public ServletContext getServletContext() {
if (context == null) {
context = new ApplicationContext(this);
if (altDDName != null)
context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
}
return (context.getFacade());
}
代码很好理解,我们终于知道了代表Servlet上下文的类是ApplicationContext,在实例化的时候注入了StandardContext,显然我们可以吧该类当做StandardContext的一个包装类,而在这个类里面有包装了一个ApplicationContextFacade,我们得到的ServletContext的实现类实际上就是ApplicationContextFacade实例。其实都是封装,Java就是这样,层层封装,有兴趣的可以看看这两个类的具体实现。
上面讲了getServeltContext的实现,下面也要讲一个getInitParameter的实现
//StandardWarpperFacade
@Override
public String getInitParameter(String name) {
//实际调用的StandardWarpper的方法
return config.getInitParameter(name);
}
//StandardWarpper
@Override
public String getInitParameter(String name) {
return (findInitParameter(name));
}
protected HashMap<String, String> parameters = new HashMap<String, String>();
@Override
public String findInitParameter(String name) {
try {
parametersLock.readLock().lock();
return parameters.get(name);
} finally {
parametersLock.readLock().unlock();
}
}
@Override
public void addInitParameter(String name, String value) {
try {
parametersLock.writeLock().lock();
parameters.put(name, value);
} finally {
parametersLock.writeLock().unlock();
}
fireContainerEvent("addInitParameter", name);
}
还记得解析WEB.xml配置文件吗,当我们把WebXML类的信息注册到StadardContext中的时候,servlet的参数配置就是通过addInitParameter添加进去的,讲到这里你就应该为什么可以取到参数了吧。
通过上面的分析,我们知道了ServletConfig的实现类是StandardWarpperFacade,是对StandardWarpper的一个封装。
ServletContext的实现类是ApplicationContextFacade,这是对StandardContext的封装。
创建FilterChain实例
终于到创建FilterChain过滤连实例了。
ApplicationFilterFactory factory =
ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain =
factory.createFilterChain(request, wrapper, servlet);
public ApplicationFilterChain createFilterChain
(ServletRequest request, Wrapper wrapper, Servlet servlet) {
// get the dispatcher type
DispatcherType dispatcher = null;
if (request.getAttribute(Globals.DISPATCHER_TYPE_ATTR) != null) {
dispatcher = (DispatcherType) request.getAttribute(
Globals.DISPATCHER_TYPE_ATTR);
}
String requestPath = null;
Object attribute = request.getAttribute(
Globals.DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null){
requestPath = attribute.toString();
}
// If there is no servlet to execute, return null
if (servlet == null)
return (null);
boolean comet = false;
// Create and initialize a filter chain object
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
comet = req.isComet();
if (Globals.IS_SECURITY_ENABLED) {
// Security: Do not recycle
//创建一个FilterChain实例
filterChain = new ApplicationFilterChain();
if (comet) {
req.setFilterChain(filterChain);
}
} else {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
// Request dispatcher in use
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
filterChain.setSupport
(((StandardWrapper)wrapper).getInstanceSupport());
// Acquire the filter mappings for this Context
StandardContext context = (StandardContext) wrapper.getParent();
//找到所有的FilterMap
FilterMap filterMaps[] = context.findFilterMaps();
// If there are no filter mappings, we are done
if ((filterMaps == null) || (filterMaps.length == 0))
return (filterChain);
// Acquire the information we will need to match filter mappings
String servletName = wrapper.getName();
// Add the relevant path-mapped filters to this filter chain
//找到所有符合条件的Filter
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
// Add filters that match on servlet name second
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
boolean isCometFilter = false;
if (comet) {
try {
isCometFilter = filterConfig.getFilter() instanceof CometFilter;
} catch (Exception e) {
// Note: The try catch is there because getFilter has a lot of
// declared exceptions. However, the filter is allocated much
// earlier
}
if (isCometFilter) {
filterChain.addFilter(filterConfig);
}
} else {
filterChain.addFilter(filterConfig);
}
}
// Return the completed filter chain
return (filterChain);
}
这个方法也是巨长,他主要做了如下工作:
首先创建一个FilterChain实例ApplicationFilterChain
然后通过StandardContext得到所有的FilterMap,然后筛选出这次请求匹配的过滤器。
然后将这些过滤器添加到过滤链实例中。 filterChain.addFilter(filterConfig);
void addFilter(ApplicationFilterConfig filterConfig) {
// Prevent the same filter being added multiple times
for(ApplicationFilterConfig filter:filters)
if(filter==filterConfig)
return;
if (n == filters.length) {
ApplicationFilterConfig[] newFilters =
new ApplicationFilterConfig[n + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;
}
filters[n++] = filterConfig;
}
我们看到就是把过滤器对应的类ApplicationFilterConfig放入到ApplicationFilterChain的filters数组中。
最后我们要调用ApplicationFilterConfig的doFilter来处理请求。
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
else {
internalDoFilter(request,response);
}
}
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = null;
try {
filter = filterConfig.getFilter();
support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
filter, request, response);
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege
("doFilter", filter, classType, args, principal);
} else {
//
filter.doFilter(request, response, this);
}
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response);
} catch (IOException e) {
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw e;
} catch (ServletException e) {
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw e;
} catch (RuntimeException e) {
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw new ServletException
(sm.getString("filterChain.filter"), e);
}
return;
}
// We fell off the end of the chain -- call the servlet instance
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
servlet, request, response);
if (request.isAsyncSupported()
&& !support.getWrapper().isAsyncSupported()) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse)) {
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
servlet.service(request, response);
}
} else {
servlet.service(request, response);
}
}
这段代码不难理解,他使用了责任链的设计模式,这种设计模式,我们已经不陌生了,在Mybatis的Cache的设计中以及讲过。这里通过遍历数组来实现,具体的实现还是值得我们细细品味的。
当所有的有效过滤器都执行完后,我们会执行 servlet.service(request, response);
来处理我们的请求,我们把返回的数据放在response中,而response已经对socket.getOutputStream封装了,所以返回的数据能够成功返回。
End…