我們已經知道,在tomcat中有四種類型的servlet容器,分別為Engine、Host、Context 和Wrapper,本文接下來對tomcat中Wrapper接口的標準實現進行說明。
對于每個引入的HTTP請求,連接器都會調用與其關聯的servlet容器的invoke()方法;然后,servlet容器會調用所有子容器的invoke()方法
這里面的流程通常是servlet容器調用其管道對象的invoke()方法,其管道對象的invoke()方法最后調用其基礎閥,管道對象的基礎閥里面會調用子容器的invoke()方法;子容器的invoke()方法的調用序列與之相同。
在tomcat中,servlet類可以實現javax.servlet.SingleThreadModel接口,這樣的servlet類也稱為SingleThreadModel(STM)servlet類;根據Servlet規范,實現此接口的目的是保證servlet實例一次只處理一個請求。
StandardWrapper對象的主要任務是載入它所代表的servlet類,并進行實例化;不過StandardWrapper類并不調用servlet的service方法,該任務由StandardWrapperValve對象(StandardWrapper實例的管道對象中的基礎閥)完成;StandardWrapperValve對象通過調用StandardWrapper實例的allocate()方法獲取servlet實例,在獲取實例后,StandardWrapperValve實例就會調用servlet實例的service()方法
對于STM 類型的servlet類與非STM 類型的servlet類,StandardWrapper實例的載入方式是不一樣的;對于非STM 類型的servlet類,StandardWrapper實例只會載入一次,對于隨后的請求都會返回servlet的同一個實例,它假設該servlet類的service()方法在多線程環境中是線程安全的。
而對于STM 類型的servlet類,StandardWrapper實例必須保證每一時刻只能有一個線程在執行STM servlet類的service()方法;StandardWrapper實例通過將STM 類型的servlet實例保存在一個java.util.Stack類型的棧中
public
Servlet allocate()
throws
ServletException {
if
(debug >= 1
)
log(
"Allocating an instance"
);
//
If we are currently unloading this servlet, throw an exception
if
(unloading)
throw
new
ServletException
(sm.getString(
"standardWrapper.unloading"
, getName()));
//
If not SingleThreadedModel, return the same instance every time
if
(!
singleThreadModel) {
//
Load and initialize our instance if necessary
if
(instance ==
null
) {
synchronized
(
this
) {
if
(instance ==
null
) {
try
{
instance
=
loadServlet();
}
catch
(ServletException e) {
throw
e;
}
catch
(Throwable e) {
throw
new
ServletException
(sm.getString(
"standardWrapper.allocate"
), e);
}
}
}
}
if
(!
singleThreadModel) {
if
(debug >= 2
)
log(
" Returning non-STM instance"
);
countAllocated
++
;
return
(instance);
}
}
synchronized
(instancePool) {
while
(countAllocated >=
nInstances) {
//
Allocate a new instance if possible, or else wait
if
(nInstances <
maxInstances) {
try
{
instancePool.push(loadServlet());
nInstances
++
;
}
catch
(ServletException e) {
throw
e;
}
catch
(Throwable e) {
throw
new
ServletException
(sm.getString(
"standardWrapper.allocate"
), e);
}
}
else
{
try
{
instancePool.wait();
}
catch
(InterruptedException e) {
;
}
}
}
if
(debug >= 2
)
log(
" Returning allocated STM instance"
);
countAllocated
++
;
return
(Servlet) instancePool.pop();
}
}
而在loadServlet()方法中,首先獲取要加載的servlet的完整類名及類加載器,然后通過類加載器加載該servlet類,然后實例化該servlet類,最后調用該servlet實例的init()方法初始化(傳入javax.servlet.ServletConfig對象)
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;
SystemLogHandler.startCapture();
Servlet servlet
=
null
;
try
{
//
If this "servlet" is really a JSP file, get the right class.
//
HOLD YOUR NOSE - this is a kludge that avoids having to do special
//
case Catalina-specific code in Jasper - it also requires that the
//
servlet path be replaced by the <jsp-file> element content in
//
order to be completely effective
String actualClass =
servletClass;
if
((actualClass ==
null
) && (jspFile !=
null
)) {
Wrapper jspWrapper
=
(Wrapper)
((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
if
(jspWrapper !=
null
)
actualClass
=
jspWrapper.getServletClass();
}
//
Complain if no servlet class has been specified
if
(actualClass ==
null
) {
unavailable(
null
);
throw
new
ServletException
(sm.getString(
"standardWrapper.notClass"
, getName()));
}
//
Acquire an instance of the class loader to be used
Loader loader =
getLoader();
if
(loader ==
null
) {
unavailable(
null
);
throw
new
ServletException
(sm.getString(
"standardWrapper.missingLoader"
, getName()));
}
ClassLoader classLoader
=
loader.getClassLoader();
//
Special case class loader for a container provided servlet
if
(isContainerProvidedServlet(actualClass)) {
classLoader
=
this
.getClass().getClassLoader();
log(sm.getString
(
"standardWrapper.containerServlet"
, getName()));
}
//
Load the specified servlet class from the appropriate class loader
Class classClass =
null
;
try
{
if
(classLoader !=
null
) {
System.out.println(
"Using classLoader.loadClass"
);
classClass
=
classLoader.loadClass(actualClass);
}
else
{
System.out.println(
"Using forName"
);
classClass
=
Class.forName(actualClass);
}
}
catch
(ClassNotFoundException e) {
unavailable(
null
);
throw
new
ServletException
(sm.getString(
"standardWrapper.missingClass"
, actualClass),
e);
}
if
(classClass ==
null
) {
unavailable(
null
);
throw
new
ServletException
(sm.getString(
"standardWrapper.missingClass"
, actualClass));
}
//
Instantiate and initialize an instance of the servlet class itself
try
{
servlet
=
(Servlet) classClass.newInstance();
}
catch
(ClassCastException e) {
unavailable(
null
);
//
Restore the context ClassLoader
throw
new
ServletException
(sm.getString(
"standardWrapper.notServlet"
, actualClass), e);
}
catch
(Throwable e) {
unavailable(
null
);
//
Restore the context ClassLoader
throw
new
ServletException
(sm.getString(
"standardWrapper.instantiate"
, actualClass), e);
}
//
Check if loading the servlet in this web application should be
//
allowed
if
(!
isServletAllowed(servlet)) {
throw
new
SecurityException
(sm.getString(
"standardWrapper.privilegedServlet"
,
actualClass));
}
//
Special handling for ContainerServlet instances
if
((servlet
instanceof
ContainerServlet) &&
isContainerProvidedServlet(actualClass)) {
System.out.println(
"calling setWrapper"
);
((ContainerServlet) servlet).setWrapper(
this
);
System.out.println(
"after calling setWrapper"
);
}
//
Call the initialization method of this servlet
try
{
instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,
servlet);
servlet.init(facade);
//
Invoke jspInit on JSP pages
if
((loadOnStartup > 0) && (jspFile !=
null
)) {
//
Invoking jspInit
HttpRequestBase req =
new
HttpRequestBase();
HttpResponseBase res
=
new
HttpResponseBase();
req.setServletPath(jspFile);
req.setQueryString(
"jsp_precompile=true"
);
servlet.service(req, res);
}
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) {
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);
}
//
Register our newly initialized instance
singleThreadModel = servlet
instanceof
SingleThreadModel;
if
(singleThreadModel) {
if
(instancePool ==
null
)
instancePool
=
new
Stack();
}
fireContainerEvent(
"load",
this
);
}
finally
{
String log
=
SystemLogHandler.stopCapture();
if
(log !=
null
&& log.length() > 0
) {
if
(getServletContext() !=
null
) {
getServletContext().log(log);
}
else
{
out.println(log);
}
}
}
return
servlet;
}
?StandardWrapper類的loadServlet()方法在載入servlet類后,會調用該servlet實例的init()方法,該方法需要傳入一個javax.servlet.ServletConfig實例作為參數;那么,?StandardWrapper對象是如何獲取ServletConfig對象的呢
答案就在StandardWrapper類本身,該類不僅實現了Wrapper接口,還實現了javax.servlet.ServletConfig接口,下面是相關方法實現
/**
* Return the initialization parameter value for the specified name,
* if any; otherwise return <code>null</code>.
*
*
@param
name Name of the initialization parameter to retrieve
*/
public
String getInitParameter(String name) {
return
(findInitParameter(name));
}
/**
* Return the set of initialization parameter names defined for this
* servlet. If none are defined, an empty Enumeration is returned.
*/
public
Enumeration getInitParameterNames() {
synchronized
(parameters) {
return
(
new
Enumerator(parameters.keySet()));
}
}
/**
* Return the servlet context with which this servlet is associated.
*/
public
ServletContext getServletContext() {
if
(parent ==
null
)
return
(
null
);
else
if
(!(parent
instanceof
Context))
return
(
null
);
else
return
(((Context) parent).getServletContext());
}
/**
* Return the name of this servlet.
*/
public
String getServletName() {
return
(getName());
}
StandardWrapper實例在它的loadServlet()方法里面調用它所載入的servlet類的實例的init()方法,該方法需要一個javax.servlet.ServletConfig類型參數,理論上StandardWrapper對象可以將自身傳入init()方法,不過為了類型安全,StandardWrapper類將自身實例包裝成StandardWrapperFacade類的一個實例,然后再傳給init()方法
public
final
class
StandardWrapperFacade
implements
ServletConfig {
/**
* Create a new facede around a StandardWrapper.
*/
public
StandardWrapperFacade(StandardWrapper config) {
super
();
this
.config =
(ServletConfig) config;
}
/**
* Wrapped config.
*/
private
ServletConfig config =
null
;
public
String getServletName() {
return
config.getServletName();
}
public
ServletContext getServletContext() {
ServletContext theContext
=
config.getServletContext();
if
((theContext !=
null
) &&
(theContext
instanceof
ApplicationContext))
theContext
=
((ApplicationContext) theContext).getFacade();
return
(theContext);
}
public
String getInitParameter(String name) {
return
config.getInitParameter(name);
}
public
Enumeration getInitParameterNames() {
return
config.getInitParameterNames();
}
}
StandardWrapperValve類是StandardWrapper實例中的基礎閥,要完成兩個操作
(1)執行與該servlet相關聯的全部過濾器
(2)調用servlet實例的service()方法
public
void
invoke(Request request, Response response,
ValveContext valveContext)
throws
IOException, ServletException {
//
Initialize local variables we may need
boolean
unavailable =
false
;
Throwable throwable
=
null
;
StandardWrapper wrapper
=
(StandardWrapper) getContainer();
ServletRequest sreq
=
request.getRequest();
ServletResponse sres
=
response.getResponse();
Servlet servlet
=
null
;
HttpServletRequest hreq
=
null
;
if
(sreq
instanceof
HttpServletRequest)
hreq
=
(HttpServletRequest) sreq;
HttpServletResponse hres
=
null
;
if
(sres
instanceof
HttpServletResponse)
hres
=
(HttpServletResponse) sres;
//
Check for the application being marked unavailable
if
(!
((Context) wrapper.getParent()).getAvailable()) {
hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString(
"standardContext.isUnavailable"
));
unavailable
=
true
;
}
//
Check for the servlet being marked unavailable
if
(!unavailable &&
wrapper.isUnavailable()) {
log(sm.getString(
"standardWrapper.isUnavailable"
,
wrapper.getName()));
if
(hres ==
null
) {
;
//
NOTE - Not much we can do generically
}
else
{
long
available =
wrapper.getAvailable();
if
((available > 0L) && (available <
Long.MAX_VALUE))
hres.setDateHeader(
"Retry-After"
, available);
hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString(
"standardWrapper.isUnavailable"
,
wrapper.getName()));
}
unavailable
=
true
;
}
//
Allocate a servlet instance to process this request
try
{
if
(!
unavailable) {
servlet
=
wrapper.allocate();
}
}
catch
(ServletException e) {
log(sm.getString(
"standardWrapper.allocateException"
,
wrapper.getName()), e);
throwable
=
e;
exception(request, response, e);
servlet
=
null
;
}
catch
(Throwable e) {
log(sm.getString(
"standardWrapper.allocateException"
,
wrapper.getName()), e);
throwable
=
e;
exception(request, response, e);
servlet
=
null
;
}
//
Acknowlege the request
try
{
response.sendAcknowledgement();
}
catch
(IOException e) {
sreq.removeAttribute(Globals.JSP_FILE_ATTR);
log(sm.getString(
"standardWrapper.acknowledgeException"
,
wrapper.getName()), e);
throwable
=
e;
exception(request, response, e);
}
catch
(Throwable e) {
log(sm.getString(
"standardWrapper.acknowledgeException"
,
wrapper.getName()), e);
throwable
=
e;
exception(request, response, e);
servlet
=
null
;
}
//
Create the filter chain for this request
ApplicationFilterChain filterChain =
createFilterChain(request, servlet);
//
Call the filter chain for this request
//
NOTE: This also calls the servlet's service() method
try
{
String jspFile
=
wrapper.getJspFile();
if
(jspFile !=
null
)
sreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
else
sreq.removeAttribute(Globals.JSP_FILE_ATTR);
if
((servlet !=
null
) && (filterChain !=
null
)) {
filterChain.doFilter(sreq, sres);
}
sreq.removeAttribute(Globals.JSP_FILE_ATTR);
}
catch
(IOException e) {
sreq.removeAttribute(Globals.JSP_FILE_ATTR);
log(sm.getString(
"standardWrapper.serviceException"
,
wrapper.getName()), e);
throwable
=
e;
exception(request, response, e);
}
catch
(UnavailableException e) {
sreq.removeAttribute(Globals.JSP_FILE_ATTR);
log(sm.getString(
"standardWrapper.serviceException"
,
wrapper.getName()), e);
//
throwable = e;
//
exception(request, response, e);
wrapper.unavailable(e);
long
available =
wrapper.getAvailable();
if
((available > 0L) && (available <
Long.MAX_VALUE))
hres.setDateHeader(
"Retry-After"
, available);
hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString(
"standardWrapper.isUnavailable"
,
wrapper.getName()));
//
Do not save exception in 'throwable', because we
//
do not want to do exception(request, response, e) processing
}
catch
(ServletException e) {
sreq.removeAttribute(Globals.JSP_FILE_ATTR);
log(sm.getString(
"standardWrapper.serviceException"
,
wrapper.getName()), e);
throwable
=
e;
exception(request, response, e);
}
catch
(Throwable e) {
sreq.removeAttribute(Globals.JSP_FILE_ATTR);
log(sm.getString(
"standardWrapper.serviceException"
,
wrapper.getName()), e);
throwable
=
e;
exception(request, response, e);
}
//
Release the filter chain (if any) for this request
try
{
if
(filterChain !=
null
)
filterChain.release();
}
catch
(Throwable e) {
log(sm.getString(
"standardWrapper.releaseFilters"
,
wrapper.getName()), e);
if
(throwable ==
null
) {
throwable
=
e;
exception(request, response, e);
}
}
//
Deallocate the allocated servlet instance
try
{
if
(servlet !=
null
) {
wrapper.deallocate(servlet);
}
}
catch
(Throwable e) {
log(sm.getString(
"standardWrapper.deallocateException"
,
wrapper.getName()), e);
if
(throwable ==
null
) {
throwable
=
e;
exception(request, response, e);
}
}
//
If this servlet has been marked permanently unavailable,
//
unload it and release this instance
try
{
if
((servlet !=
null
) &&
(wrapper.getAvailable()
==
Long.MAX_VALUE)) {
wrapper.unload();
}
}
catch
(Throwable e) {
log(sm.getString(
"standardWrapper.unloadException"
,
wrapper.getName()), e);
if
(throwable ==
null
) {
throwable
=
e;
exception(request, response, e);
}
}
}
上述創建過濾器鏈的過程本人后來作了補充: How Tomcat Works(十四)補充
---------------------------------------------------------------------------?
本系列How Tomcat Works系本人原創?
轉載請注明出處 博客園 刺猬的溫馴?
本人郵箱: ? chenying998179 # 163.com ( #改為@ )
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

