文章內容
1.Tomcat框架概述和分析
2.Servlet的實現原理概述
3.Servlet在Tomcat中作用
1.Tomcat框架概述和分析
Tomcat作爲典型的Servlet容器(除此之外還有Jetty、Weblogic、JBOSS等),分析和了解Tomcat的整體框架對於瞭解Servlet在Web開發中的作用很有幫助,有利於加深理解;
Tomcat服務器模塊劃分十分清晰,主要有三個部分組成:
Service服務:可以具體到一個Web應用,它負責管理整個應用,包括各個組件生命週期的管理和組件之間的聯繫;
Connector:負責一個應用的對外事務,負責處理瀏覽器的請求和生成Response,以及完成將請求傳遞給具體的處理線程;
Container:實際處理每一個Connector傳遞過來的TCP請求,是一個鏈式的處理過程;
其他組件:包括Jasper、logging等組件,主要完成一些附加或者通用的工作;
由此,我們可以認爲Servlet作爲Container的一部分,主要任務是處理請求並得到響應的結果
2.Servlet的實現原理概述
爲了分析Servlet的實現原理,需要進一步分析Container的組成和工作流程,一般我們將Container叫做Servlet容器;
Container 容器的設計用的是典型的責任鏈的設計模式,它有四個子容器組件構成,分別是:Engine、Host、Context、Wrapper;這樣的形式類似於MVC框架中攔截器和過濾器的設計;
Engine:一個對的Servlet容器引擎;
Host:負責虛擬主機的域名映射,特別的對於通過war包發佈的應用,需要配置Host,一個Engine可以配置多個Host虛擬主機;
Context:應用的具體運行環境和依賴關係,一個Host可以有多個Context,對應多個應用(和通過Service發佈的多個應用略有不同);
Wrapper:對於單個Servlet的封裝,在Container中Servlet被封裝成Wrapper統一調用,Wrapper負責的工作包括的 Servlet 的裝載、初始化、執行以及資源回收;Wrapper是最底層的容器;
要簡單瞭解Servlet的實現原理,首先查看Wrapper的定義
在org.apache.catalina.core包中給出了StandardWrapper的實現:
重點關注loadServlet()方法:
public synchronized Servlet loadServlet() throws ServletException {
// Nothing to do if we already have an instance or an instance pool
if (!singleThreadModel && (instance != null))
return instance;
PrintStream out = System.out;
if (swallowOutput) {
SystemLogHandler.startCapture();
}
Servlet servlet;
try {
long t1=System.currentTimeMillis();
// Complain if no servlet class has been specified
if (servletClass == null) {
unavailable(null);
throw new ServletException
(sm.getString("standardWrapper.notClass", getName()));
}
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
servlet = (Servlet) instanceManager.newInstance(servletClass);
} catch (ClassCastException e) {
unavailable(null);
// Restore the context ClassLoader
throw new ServletException
(sm.getString("standardWrapper.notServlet", servletClass), e);
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
unavailable(null);
// Added extra log statement for Bugzilla 36630:
// http://issues.apache.org/bugzilla/show_bug.cgi?id=36630
if(log.isDebugEnabled()) {
log.debug(sm.getString("standardWrapper.instantiate", servletClass), e);
}
// Restore the context ClassLoader
throw new ServletException
(sm.getString("standardWrapper.instantiate", servletClass), e);
}
if (multipartConfigElement == null) {
MultipartConfig annotation =
servlet.getClass().getAnnotation(MultipartConfig.class);
if (annotation != null) {
multipartConfigElement =
new MultipartConfigElement(annotation);
}
}
processServletSecurityAnnotation(servlet.getClass());
// Special handling for ContainerServlet instances
if ((servlet instanceof ContainerServlet) &&
(isContainerProvidedServlet(servletClass) ||
((Context) getParent()).getPrivileged() )) {
((ContainerServlet) servlet).setWrapper(this);
}
classLoadTime=(int) (System.currentTimeMillis() -t1);
if (servlet instanceof SingleThreadModel) {
if (instancePool == null) {
instancePool = new Stack<Servlet>();
}
singleThreadModel = true;
}
initServlet(servlet);
fireContainerEvent("load", this);
loadTime=System.currentTimeMillis() -t1;
} finally {
if (swallowOutput) {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
if (getServletContext() != null) {
getServletContext().log(log);
} else {
out.println(log);
}
}
}
}
return servlet;
}
關鍵代碼有以下幾條:
//創建實例
servlet = (Servlet) instanceManager.newInstance(servletClass);
//設置實例的Wrapper
((ContainerServlet) servlet).setWrapper(this);
//設置Servlet運行模式
if (servlet instanceof SingleThreadModel) {
if (instancePool == null) {
instancePool = new Stack<Servlet>();
}
singleThreadModel = true;
}
//初始化並啓動Servlet,完成附加工作
initServlet(servlet);
fireContainerEvent("load", this); //可以看出Tomcat的典型的設計模式
DefaultServlet定義
在org\apache\catalina\servlets包中給出了DefaultServlet的定義,主要包括init()、doGet()、doPost()方法和抽象方法;
public void init() throws ServletException {
if (getServletConfig().getInitParameter("debug") != null)
debug = Integer.parseInt(getServletConfig().getInitParameter("debug"));
if (getServletConfig().getInitParameter("input") != null)
input = Integer.parseInt(getServletConfig().getInitParameter("input"));
if (getServletConfig().getInitParameter("output") != null)
output = Integer.parseInt(getServletConfig().getInitParameter("output"));
listings = Boolean.parseBoolean(getServletConfig().getInitParameter("listings"));
if (getServletConfig().getInitParameter("readonly") != null)
readOnly = Boolean.parseBoolean(getServletConfig().getInitParameter("readonly"));
if (getServletConfig().getInitParameter("sendfileSize") != null)
sendfileSize =
Integer.parseInt(getServletConfig().getInitParameter("sendfileSize")) * 1024;
fileEncoding = getServletConfig().getInitParameter("fileEncoding");
globalXsltFile = getServletConfig().getInitParameter("globalXsltFile");
contextXsltFile = getServletConfig().getInitParameter("contextXsltFile");
localXsltFile = getServletConfig().getInitParameter("localXsltFile");
readmeFile = getServletConfig().getInitParameter("readmeFile");
if (getServletConfig().getInitParameter("useAcceptRanges") != null)
useAcceptRanges = Boolean.parseBoolean(getServletConfig().getInitParameter("useAcceptRanges"));
// Sanity check on the specified buffer sizes
if (input < 256)
input = 256;
if (output < 256)
output = 256;
if (debug > 0) {
log("DefaultServlet.init: input buffer size=" + input +
", output buffer size=" + output);
}
// Load the proxy dir context.
resources = (ProxyDirContext) getServletContext()
.getAttribute(Globals.RESOURCES_ATTR);
if (resources == null) {
try {
resources =
(ProxyDirContext) new InitialContext()
.lookup(RESOURCES_JNDI_NAME);
} catch (NamingException e) {
// Failed
throw new ServletException("No resources", e);
}
}
if (resources == null) {
throw new UnavailableException("No resources");
}
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Serve the requested resource, including the data content
serveResource(request, response, true);
}
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
doGet(request, response);
}
3.Servlet在Tomcat中作用
1.Servlet容器通過將瀏覽器請求逐層傳遞,最終到達Servlet完成實際的處理邏輯;
2.Servlet的創建和管理由最底層容器Wrapper來完成;
3.Servlet通過統一接口來完成特定的操作,包括初始化、加載、doGet、doPost等;
注:關於Tomcat的框架,除了通過源碼分析,從配置文件server.xml的結構來分析是很清晰的: