Tomcat的Host初始化(Context,Listener,Filter,Servlet)

Tomcat的Engine初始化,启动过程:[url]http://donald-draper.iteye.com/blog/2327119[/url]
Tomcat配置文件:
<Engine name="Catalina" defaultHost="localhost">
<!--
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
-->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>

Host作为Engine的子容器,我们来看看Host的初始化与启动
public class StandardHost extends ContainerBase implements Host {
private final Object aliasesLock = new Object();

/**The application root for this Host.默认应用根目录*/
private String appBase = "webapps";
/** The XML root for this Host.*/
private String xmlBase = null;
/** The auto deploy flag for this Host.*/
private boolean autoDeploy = true;
/**
* The Java class name of the default context configuration class
* for deployed web applications.
*/
//上下文配置类
private String configClass =
"org.apache.catalina.startup.ContextConfig";
/**
* The Java class name of the default Context implementation class for
* deployed web applications.
*/
//Host的上下文加载实现类
private String contextClass =
"org.apache.catalina.core.StandardContext";
/** The deploy on startup flag for this Host.*/
private boolean deployOnStartup = true;
/** deploy Context XML config files property. */
private boolean deployXML = !Globals.IS_SECURITY_ENABLED;
/**
* Should XML files be copied to
* $CATALINA_BASE/conf/<engine>/<host> by default when
* a web application is deployed?
*/
private boolean copyXML = false;
/**
* The Java class name of the default error reporter implementation class
* for deployed web applications.
*/
private String errorReportValveClass =
"org.apache.catalina.valves.ErrorReportValve";
//实现类类信息
private static final String info =
"org.apache.catalina.core.StandardHost/1.0";
//是否解压WARs
private boolean unpackWARs = true;
//applications的工作目录
private String workDir = null;
//启动 appBase and xmlBase的时候,是否创建目录
private boolean createDirs = true;
//子ClassLoaderMap,以便检测内存泄漏情况
private Map<ClassLoader, String> childClassLoaders =
new WeakHashMap<ClassLoader, String>();
private Pattern deployIgnore = null;
private boolean undeployOldVersions = false;
private boolean failCtxIfServletStartFails = false;
//构造HOST
public StandardHost() {
super();
pipeline.setBasic(new StandardHostValve());
}
//启动
protected synchronized void startInternal() throws LifecycleException {
String errorValve = getErrorReportValveClass();
if ((errorValve != null) && (!errorValve.equals(""))) {
try {
boolean found = false;
Valve[] valves = getPipeline().getValves();
for (Valve valve : valves) {
if (errorValve.equals(valve.getClass().getName())) {
found = true;
break;
}
}
if(!found) {
Valve valve =
(Valve) Class.forName(errorValve).newInstance();
//添加valve到Pipeline
getPipeline().addValve(valve);
}
}
}
//这个调用的是ContainerBase的startInternal
//这个在上面我们已经说过
super.startInternal();
}
}

//StandardHostValve
final class StandardHostValve extends ValveBase {
static final boolean STRICT_SERVLET_COMPLIANCE;
static final boolean ACCESS_SESSION;
static {
STRICT_SERVLET_COMPLIANCE = Globals.STRICT_SERVLET_COMPLIANCE;
String accessSession = System.getProperty(
"org.apache.catalina.core.StandardHostValve.ACCESS_SESSION");
if (accessSession == null) {
ACCESS_SESSION = STRICT_SERVLET_COMPLIANCE;
} else {
ACCESS_SESSION = Boolean.parseBoolean(accessSession);
}
}
//根据request的URI选择一个child context来处理Request,
//如果没有匹配的上下文,则返回HTTP error
public final void invoke(Request request, Response response)
throws IOException, ServletException {
//获取请求的上下文
Context context = request.getContext();
if (context == null) {
response.sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
sm.getString("standardHost.noContext"));
return;
}
// Bind the context CL to the current thread
if( context.getLoader() != null ) {
// Not started - it should check for availability first
// This should eventually move to Engine, it's generic.
if (Globals.IS_SECURITY_ENABLED) {
PrivilegedAction<!-- <Void> --> pa = new PrivilegedSetTccl(
context.getLoader().getClassLoader());
AccessController.doPrivileged(pa);
} else {
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
}
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(context.getPipeline().isAsyncSupported());
}
boolean asyncAtStart = request.isAsync();
boolean asyncDispatching = request.isAsyncDispatching();
if (asyncAtStart || context.fireRequestInitEvent(request)) {
Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
// Look for (and render if found) an application level error page
if (response.isErrorReportRequired()) {
if (t != null) {
throwable(request, response, t);
} else {
//返回处理状态
status(request, response);
}
}
}
}
//返回处理状态,如果异常发生,返回错误状态码,对应的错误页面(web.xml-errorPage)
private void status(Request request, Response response) {
int statusCode = response.getStatus();

// Handle a custom error page for this status code
Context context = request.getContext();
if (context == null)
return;

/* Only look for error pages when isError() is set.
* isError() is set when response.sendError() is invoked. This
* allows custom error pages without relying on default from
* web.xml.
*/
if (!response.isError())
return;
//从Context中,获取statusCode对应的页面
ErrorPage errorPage = context.findErrorPage(statusCode);
if (errorPage == null) {
// Look for a default error page
errorPage = context.findErrorPage(0);
}
if (errorPage != null && response.isErrorReportRequired()) {
response.setAppCommitted(false);
request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
Integer.valueOf(statusCode));
String message = response.getMessage();
if (message == null)
message = "";
request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
errorPage.getLocation());
request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,
DispatcherType.ERROR);
Wrapper wrapper = request.getWrapper();
if (wrapper != null)
request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,
wrapper.getName());
request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,
request.getRequestURI());
if (custom(request, response, errorPage)) {
response.setErrorReported();
try {
response.finishResponse();
} catch (ClientAbortException e) {
// Ignore
} catch (IOException e) {
container.getLogger().warn("Exception Processing " + errorPage, e);
}
}
}
}

//ErrorPage
public class ErrorPage implements Serializable {
//错误码
private int errorCode = 0;
private String exceptionType = null;
//处理异常的Handler位置(context)
private String location = null;
}

在StandardHost中我们看到有一个StandardContext,
StandardContext也继承了ContainerBase
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
public StandardContext() {
super();
pipeline.setBasic(new StandardContextValve());
broadcaster = new NotificationBroadcasterSupport();
// Set defaults
if (!Globals.STRICT_SERVLET_COMPLIANCE) {
// Strict servlet compliance requires all extension mapped servlets
// to be checked against welcome files
resourceOnlyServlets.add("jsp");
}
//url安全字符集
protected static URLEncoder urlEncoder;
static {
urlEncoder = new URLEncoder();
urlEncoder.addSafeCharacter('~');
urlEncoder.addSafeCharacter('-');
urlEncoder.addSafeCharacter('_');
urlEncoder.addSafeCharacter('.');
urlEncoder.addSafeCharacter('*');
urlEncoder.addSafeCharacter('/');
}
/**
* The set of application listener class names configured for this
* application, in the order they were encountered in the resulting merged
* web.xml file.
*/
private ApplicationListener applicationListeners[] =new ApplicationListener[0];
private final Object applicationListenersLock = new Object();
/** The ordered set of ServletContainerInitializers for this web application.*/
private Map<ServletContainerInitializer,Set<Class<?>>> initializers =
new LinkedHashMap<ServletContainerInitializer,Set<Class<?>>>();
/**The set of application parameters defined for this application.*/
private ApplicationParameter applicationParameters[] =
new ApplicationParameter[0];
private final Object applicationParametersLock = new Object();
/** The Locale to character set mapper for this application.*/
private CharsetMapper charsetMapper = null;
/** The Java class name of the CharsetMapper class to be created.*/
private String charsetMapperClass ="org.apache.catalina.util.CharsetMapper";
/** The URL of the XML descriptor for this context.*/
private URL configFile = null;
/** The "correctly configured" flag for this Context.*/
private boolean configured = false;
/** The security constraints for this web application.*/
private volatile SecurityConstraint constraints[] = new SecurityConstraint[0];
private final Object constraintsLock = new Object();
/** The ServletContext implementation associated with this Context.*/
protected ApplicationContext context = null;
/**
* The wrapped version of the associated ServletContext that is presented
* to listeners that are required to have limited access to ServletContext
* methods. See Servlet 3.0 section 4.4.
*/
private NoPluggabilityServletContext noPluggabilityServletContext = null;
/**Compiler classpath to use.*/
private String compilerClasspath = null;
/**Should we attempt to use cookies for session id communication?*/
private boolean cookies = true;
/**
* Should we allow the <code>ServletContext.getContext()</code> method
* to access the context of other web applications in this server?
*/
private boolean crossContext = false;
/**Encoded path.*/
private String encodedPath = null;
/**Unencoded path for this web application.*/
private String path = null;
/**
* The "follow standard delegation model" flag that will be used to
* configure our ClassLoader.
*/
private boolean delegate = false;
/** The display name of this web application.*/
private String displayName = null;
/** Override the default context xml location.*/
private String defaultContextXml;
/** Override the default web xml location.*/
private String defaultWebXml;
/** The distributable flag for this web application.*/
private boolean distributable = false;
/** The document root for this web application.*/
private String docBase = null;
//异常页面定义映射
private HashMap<String, ErrorPage> exceptionPages =
new HashMap<String, ErrorPage>();
/**
* The set of filter configurations (and associated filter instances) we
* have initialized, keyed by filter name.
*/
private HashMap<String, ApplicationFilterConfig> filterConfigs =
new HashMap<String, ApplicationFilterConfig>();
//filter定义
private HashMap<String, FilterDef> filterDefs =
new HashMap<String, FilterDef>();
//Filter映射
private final ContextFilterMaps filterMaps = new ContextFilterMaps();
/**
* The set of classnames of InstanceListeners that will be added
* to each newly created Wrapper by <code>createWrapper()</code>.
*/
private String instanceListeners[] = new String[0];
private final Object instanceListenersLock = new Object();
/** The login configuration descriptor for this web application. */
private LoginConfig loginConfig = null;
/** The mapper associated with this context.*/
private org.apache.tomcat.util.http.mapper.Mapper mapper =
new org.apache.tomcat.util.http.mapper.Mapper();
/** The naming context listener for this web application.*/
private NamingContextListener namingContextListener = null;
/** The naming resources for this web application. */
private NamingResources namingResources = null;
/** The message destinations for this web application.*/
private HashMap<String, MessageDestination> messageDestinations =
new HashMap<String, MessageDestination>();
/** The MIME mappings for this web application, keyed by extension.*/
private HashMap<String, String> mimeMappings =new HashMap<String, String>();
/** Special case: error page for status 200. */
private ErrorPage okErrorPage = null;
/**
* The context initialization parameters for this web application,keyed by name.
*/
private final ConcurrentMap<String, String> parameters = new ConcurrentHashMap<String, String>();
/** Context level override for default {@link StandardHost#isCopyXML()}.*/
private boolean copyXML = false;
/** The default context override flag for this web application.*/
private boolean override = false;
/** The original document root for this web application.*/
private String originalDocBase = null;
//角色映射
private HashMap<String, String> roleMappings =new HashMap<String, String>();
//安全角色
private String securityRoles[] = new String[0];
private final Object securityRolesLock = new Object();
//SevletMapping
private HashMap<String, String> servletMappings = new HashMap<String, String>();
private final Object servletMappingsLock = new Object();
/** The session timeout (in minutes) for this web application.*/
private int sessionTimeout = 30;
/** The notification sequence number.*/
private AtomicLong sequenceNumber = new AtomicLong(0);
private HashMap<Integer, ErrorPage> statusPages =new HashMap<Integer, ErrorPage>();
//应用欢迎文件
private String welcomeFiles[] = new String[0];
private final Object welcomeFilesLock = new Object();
/** The pathname to the work directory for this context (relative to the server's home if not absolute). */
//工作目录
private String workDir = null;
//Wrapper的实现类Java class name
private String wrapperClassName = StandardWrapper.class.getName();
private Class<?> wrapperClass = null;
/**Caching allowed flag.*/
private boolean cachingAllowed = true;
/** Cache TTL in ms.*/
protected int cacheTTL = 5000;
private String sessionCookieName;
private String sessionCookiePath;
//ApplicationContext#createServlet(Class),Servlet集合
private Set<Servlet> createdServlets = new HashSet<Servlet>();
//初始化
protected void initInternal() throws LifecycleException {
super.initInternal();
if (processTlds) {
//添加tld监听器
this.addLifecycleListener(new TldConfig());
}
// Register the naming resources
if (namingResources != null) {
namingResources.init();
}
// Send j2ee.object.created notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.object.created",
this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
}
protected synchronized void startInternal() throws LifecycleException {
if(log.isDebugEnabled()) log.debug("Starting " + getBaseName());
// Send j2ee.state.starting notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.starting",
this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
setConfigured(false);
boolean ok = true;
if (namingResources != null) {
namingResources.start();//配置namingResources
}
// 初始化Cached,CacheTTL,CacheMaxSize,CacheObjectMaxSize等
if (webappResources == null) { // (1) Required by Loader
try {
String docBase = getDocBase();
if (docBase == null) {
setResources(new EmptyDirContext());
} else if (docBase.endsWith(".war")
&& !(new File(getBasePath())).isDirectory()) {
setResources(new WARDirContext());
} else {
setResources(new FileDirContext());
}
}
}
if (ok) {
if (!resourcesStart()) {
throw new LifecycleException("Error in resourceStart()");
}
}
//配置WebappLoader
if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
// Initialize character set mapper
getCharsetMapper();
// Post work directory
postWorkDirectory();
// Validate required extensions
boolean dependencyCheck = true;
try {
dependencyCheck = ExtensionValidator.validateApplication
(getResources(), this);
}
// 绑定线程ClassLoader
ClassLoader oldCCL = bindThread();
try {
if (ok) {
// Start our subordinate components, if any
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start();

// since the loader just started, the webapp classloader is now
// created.
// By calling unbindThread and bindThread in a row, we setup the
// current Thread CCL to be the webapp classloader
unbindThread(oldCCL);
oldCCL = bindThread();

// Initialize logger again. Other components might have used it
// too early, so it should be reset.
logger = null;
getLogger();
if ((cluster != null) && (cluster instanceof Lifecycle))
((Lifecycle) cluster).start();
Realm realm = getRealmInternal();
if ((realm != null) && (realm instanceof Lifecycle))
((Lifecycle) realm).start();
if ((resources != null) && (resources instanceof Lifecycle))
((Lifecycle) resources).start();
// Notify our interested LifecycleListeners
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
// Start our child containers, if not already started
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
child.start();
}
}
// Start the Valves in our pipeline (including the basic),
// if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
// Acquire clustered manager
Manager contextManager = null;
if (manager == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("standardContext.cluster.noManager",
Boolean.valueOf((getCluster() != null)),
Boolean.valueOf(distributable)));
}
if ( (getCluster() != null) && distributable) {
try {
contextManager = getCluster().createManager(getName());
} catch (Exception ex) {
log.error("standardContext.clusterFail", ex);
ok = false;
}
} else {
contextManager = new StandardManager();
}
}
// Configure default manager if none was specified
if (contextManager != null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("standardContext.manager",
contextManager.getClass().getName()));
}
setManager(contextManager);
}
if (manager!=null && (getCluster() != null) && distributable) {
//let the cluster know that there is a context that is distributable
//and that it has its own manager
getCluster().registerManager(manager);
}
}

} finally {
// Unbinding thread
unbindThread(oldCCL);
}
if (!getConfigured()) {
log.error(sm.getString("standardContext.configurationFail"));
ok = false;
}
// We put the resources into the servlet context
if (ok)
getServletContext().setAttribute
(Globals.RESOURCES_ATTR, getResources());
//初始化欢迎页面
mapper.setContext(getPath(), welcomeFiles, resources);
// Binding thread
oldCCL = bindThread();

if (ok ) {
if (getInstanceManager() == null) {
javax.naming.Context context = null;
if (isUseNaming() && getNamingContextListener() != null) {
context = getNamingContextListener().getEnvContext();
}
Map<String, Map<String, String>> injectionMap = buildInjectionMap(
getIgnoreAnnotations() ? new NamingResources(): getNamingResources());
setInstanceManager(new DefaultInstanceManager(context,
injectionMap, this, this.getClass().getClassLoader()));
getServletContext().setAttribute(
InstanceManager.class.getName(), getInstanceManager());
}
}

try {
// Create context attributes that will be required
if (ok) {
getServletContext().setAttribute(
JarScanner.class.getName(), getJarScanner());
}
//初始化上下文参数
mergeParameters();
// Call ServletContainerInitializers
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
initializers.entrySet()) {
try {
entry.getKey().onStartup(entry.getValue(),
getServletContext());
}
// Configure and call application event listeners
if (ok) {
//初始化应用监听器
if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}
try {
// Start manager
if ((manager != null) && (manager instanceof Lifecycle)) {
((Lifecycle) getManager()).start();
}
}
// Configure and call application filters
if (ok) {
//初始化Fliter
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
// Load and initialize all "load on startup" servlets
if (ok) {
//初始化servlet关键步骤,StandardWrapper为Sevlet的包装类
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
// Start ContainerBackgroundProcessor thread
super.threadStart();
} finally {
// Unbinding thread
unbindThread(oldCCL);
}
startTime=System.currentTimeMillis();
// Send j2ee.state.running notification
if (ok && (this.getObjectName() != null)) {
Notification notification =
new Notification("j2ee.state.running", this.getObjectName(),
sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
// Close all JARs right away to avoid always opening a peak number
// of files on startup
if (getLoader() instanceof WebappLoader) {
((WebappLoader) getLoader()).closeJARs(true);
}
// Reinitializing if something went wrong
if (!ok) {
setState(LifecycleState.FAILED);
} else {
setState(LifecycleState.STARTING);
}
}
//初始化Fliter
public boolean filterStart() {
// Instantiate and record a FilterConfig for each defined filter
boolean ok = true;
synchronized (filterConfigs) {
filterConfigs.clear();
for (Entry<String, FilterDef> entry : filterDefs.entrySet()) {
String name = entry.getKey();
if (getLogger().isDebugEnabled())
getLogger().debug(" Starting filter '" + name + "'");
ApplicationFilterConfig filterConfig = null;
try {
filterConfig =
new ApplicationFilterConfig(this, entry.getValue());
filterConfigs.put(name, filterConfig);
}
}
}
return (ok);
}
//加载StandardWrapper,即Servlet包装类
public boolean loadOnStartup(Container children[]) {
// Collect "load on startup" servlets that need to be initialized
TreeMap<Integer, ArrayList<Wrapper>> map =
new TreeMap<Integer, ArrayList<Wrapper>>();
for (int i = 0; i < children.length; i++) {
Wrapper wrapper = (Wrapper) children[i];
//延迟加载时间
int loadOnStartup = wrapper.getLoadOnStartup();
if (loadOnStartup < 0)
continue;
Integer key = Integer.valueOf(loadOnStartup);
ArrayList<Wrapper> list = map.get(key);
if (list == null) {
list = new ArrayList<Wrapper>();
map.put(key, list);
}
list.add(wrapper);
}
// Load the collected "load on startup" servlets
for (ArrayList<Wrapper> list : map.values()) {
for (Wrapper wrapper : list) {
try {
//关键在这个load中
wrapper.load();
}
}
}
return true;
}
}

从上面可以分析得出StardardContext主要,是根据Web.xml的配置,
初始化欢迎界面,角色权限,初始参数,错误处理,Listener,Filter
和Sevlet等。
来看StandardWrapper从load函数,StandardWrapper同样继承里ContainerBase
,作为一个容器
public class StandardWrapper extends ContainerBase
implements ServletConfig, Wrapper, NotificationEmitter {
protected static final String[] DEFAULT_SERVLET_METHODS = new String[] {
"GET", "HEAD", "POST" };
//为StandardWrapper创建一个默认的StandardWrapperValve
public StandardWrapper() {

super();
swValve=new StandardWrapperValve();
pipeline.setBasic(swValve);
broadcaster = new NotificationBroadcasterSupport();
}
**
* The count of allocations that are currently active (even if they
* are for the same instance, as will be true on a non-STM servlet).
*/
protected AtomicInteger countAllocated = new AtomicInteger(0);
/**
* The load-on-startup order value (negative value means load on
* first call) for this servlet.
*/
protected int loadOnStartup = -1;
protected volatile Servlet instance = null;
/**
* Mappings associated with the wrapper.
*/
protected ArrayList<String> mappings = new ArrayList<String>();
/**
* The initialization parameters for this servlet, keyed by
* parameter name.
*/
protected HashMap<String, String> parameters = new HashMap<String, String>();
/**
* True if this StandardWrapper is for the JspServlet
*/
protected boolean isJspServlet;
/**
* The ObjectName of the JSP monitoring mbean
*/
protected ObjectName jspMonitorON;
//Servlet栈
protected Stack<Servlet> instancePool = null;
//启动Wrapper容器
protected synchronized void startInternal() throws LifecycleException {
// Send j2ee.state.starting notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.starting",
this.getObjectName(),
sequenceNumber++);
broadcaster.sendNotification(notification);
}
// Start up this component
super.startInternal();
setAvailable(0L);
// Send j2ee.state.running notification
if (this.getObjectName() != null) {
Notification notification =
new Notification("j2ee.state.running", this.getObjectName(),
sequenceNumber++);
broadcaster.sendNotification(notification);
}
}
//创建一个Servlet实例
public synchronized void load() throws ServletException {
//加载Servlet实例
instance = loadServlet();
if (!instanceInitialized) {
//初始化Servlet
initServlet(instance);
}
//JSP页面Servlet的处理
if (isJspServlet) {
StringBuilder oname =
new StringBuilder(MBeanUtils.getDomain(getParent()));

oname.append(":type=JspMonitor,name=");
oname.append(getName());
oname.append(getWebModuleKeyProperties());
try {
jspMonitorON = new ObjectName(oname.toString());
Registry.getRegistry(null, null)
.registerComponent(instance, jspMonitorON, null);
}
}
}
//加载Servlet
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 {
//加载servletClass实例
servlet = (Servlet) instanceManager.newInstance(servletClass);
}
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) {
//初始化Servlet栈
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
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 {
[color=red] //初始化Servlet,这个方法大家应该熟悉
servlet.init(facade);[/color]
}
instanceInitialized = true;
instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
servlet);
}
}
}

//StandardWrapperValve,Servlet请求处理包装类
final class StandardWrapperValve
extends ValveBase {
//------------------------------------------------------ Constructor
public StandardWrapperValve() {
super(true);
}
// ----------------------------------------------------- Instance Variables

// Some JMX statistics. This valve is associated with a StandardWrapper.
// We expose the StandardWrapper as JMX ( j2eeType=Servlet ). The fields
// are here for performance.
private volatile long processingTime;
private volatile long maxTime;
private volatile long minTime = Long.MAX_VALUE;
private volatile int requestCount;
private volatile int errorCount;
//处理Http请求,根据Servlet的映射
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Initialize local variables we may need
boolean unavailable = false;
Throwable throwable = null;
// This should be a Request attribute...
long t1=System.currentTimeMillis();
requestCount++;
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
//从StandardWrapper中获取Context
Context context = (Context) wrapper.getParent();

// 检查Context状态是否可利用,否返回错误
if (!context.getState().isAvailable()) {
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardContext.isUnavailable"));
unavailable = true;
}

// Check for the servlet being marked unavailable
if (!unavailable && wrapper.isUnavailable()) {
container.getLogger().info(sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
//设置头部
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
unavailable = true;
}
// Allocate a servlet instance to process this request
try {
if (!unavailable) {
//从StandardWraper中获取Servlet实例,下面我们分析这个函数
servlet = wrapper.allocate();
}
}
// Identify if the request is Comet related now that the servlet has been allocated
boolean comet = false;
if (servlet instanceof CometProcessor && Boolean.TRUE.equals(request.getAttribute(
Globals.COMET_SUPPORTED_ATTR))) {
comet = true;
request.setComet(true);
}

MessageBytes requestPathMB = request.getRequestPathMB();
DispatcherType dispatcherType = DispatcherType.REQUEST;
if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
requestPathMB);
// Create the filter chain for this request
ApplicationFilterFactory factory =
ApplicationFilterFactory.getInstance();
//创建应用Filter链
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 {
//Filter处理
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());
}
}

}
}
// Release the filter chain (if any) for this request
if (filterChain != null) {
if (request.isComet()) {
// If this is a Comet request, then the same chain will be used for the
// processing of all subsequent events.
filterChain.reuse();
} else {
filterChain.release();
}
}

// Deallocate the allocated servlet instance
try {
if (servlet != null) {
wrapper.deallocate(servlet);
}
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(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) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.unloadException",
wrapper.getName()), e);
if (throwable == null) {
throwable = e;
exception(request, response, e);
}
}
long t2=System.currentTimeMillis();

long time=t2-t1;
processingTime += time;
if( time > maxTime) maxTime=time;
if( time < minTime) minTime=time;

}
}

//从StandardWrapperValve可以看出,其通过包装Request,StandardWrapper,Servlet
到ApplicationFilterChain,然后执行Filter的doFilter方法;
下面我们看一ApplicationFilterChain
final class ApplicationFilterChain implements FilterChain, CometFilterChain {

// Used to enforce requirements of SRV.8.2 / SRV.14.2.5.1
//ThreadLocal,线程本地变量,隔离多线程下,每个线程ServletRequest,ServletResponse
//这种思想值得借鉴
private static final ThreadLocal<ServletRequest> lastServicedRequest;
private static final ThreadLocal<ServletResponse> lastServicedResponse;

static {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest = new ThreadLocal<ServletRequest>();
lastServicedResponse = new ThreadLocal<ServletResponse>();
} else {
lastServicedRequest = null;
lastServicedResponse = null;
}
}
/**
* The servlet instance to be executed by this chain.
*/
private Servlet servlet = null;
/**
* Static class array used when the SecurityManager is turned on and
* <code>doFilter</code> is invoked.
*/
private static Class<?>[] classType = new Class[]{ServletRequest.class,
ServletResponse.class,
FilterChain.class};

/**
* Static class array used when the SecurityManager is turned on and
* <code>service</code> is invoked.
*/
private static Class<?>[] classTypeUsedInService = new Class[]{
ServletRequest.class,
ServletResponse.class};
//过滤Servlet
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
@Override
public Void run()
throws ServletException, IOException {
internalDoFilter(req,res);
return null;
}
}
);
}
}
//内部过滤处理
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函数
filter.doFilter(request, response, this);
}
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response);
}
}

// 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};
//如果有权限限制,这检查权限,执行Service()
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
[color=red]//这个方法熟悉了吧,调用Servlet的service函数
servlet.service(request, response);[/color]
}
} else {
servlet.service(request, response);
}
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response);
}
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
//清除线程的本地变量ServletRequest,ServletResponse
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
}
}

上面我们分析 StandardWraper时,他的invove()函数获取Servlet有一个方法
allocate,这个方法的实现在StandardWrape
//从StandardWraper中获取Servlet实例,下面我们分析这个函数
servlet = wrapper.allocate();
public class StandardWrapper extends ContainerBase
implements ServletConfig, Wrapper, NotificationEmitter {
/**
* Stack containing the STM instances.
*/
//servlet实例栈池
protected Stack<Servlet> instancePool = null;
/**
* The count of allocations that are currently active (even if they
* are for the same instance, as will be true on a non-STM servlet).
*/
protected AtomicInteger countAllocated = new AtomicInteger(0);
/**
* Does this servlet implement the SingleThreadModel interface?
*/
protected volatile boolean singleThreadModel = false;
/**
* Maximum number of STM instances.
*/
protected int maxInstances = 20;
/**
* Number of instances currently loaded for a STM servlet.
*/
protected int nInstances = 0;
//如果Servlet是单例模式,则返回instance
//否则创建一个新的Servlet,push到instancePool中
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 {
//实例为null,则加载Servlet
instance = loadServlet();
newInstance = true;
if (!singleThreadModel) {
// For non-STM, increment here to prevent a race
// condition with unload. Bug 43683, test case
// #3
//Servlet实例数据加1
countAllocated.incrementAndGet();
}
}
}
if (!instanceInitialized) {
initServlet(instance);
}
}
}
//如果是多例模式,默认为false
if (singleThreadModel) {
if (newInstance) {
// Have to do this outside of the sync above to prevent a
// possible deadlock
synchronized (instancePool) {
//如果是单例模式,则push到instancePool中
instancePool.push(instance);
nInstances++;
}
}
} else {
//如果是单例模式,直接返回
if (log.isTraceEnabled()) {
log.trace(" Returning non-STM instance");
}
// For new instances, count will have been incremented at the
// time of creation
if (!newInstance) {
countAllocated.incrementAndGet();
}
return instance;
}
}
synchronized (instancePool) {
while (countAllocated.get() >= nInstances) {
// Allocate a new instance if possible, or else wait
if (nInstances < maxInstances) {
//如果目前实例数,小于最大实例数
try {
//加载一个新的Servlet,push到instancePool中
instancePool.push(loadServlet());
nInstances++;
}
} else {
try {
//否则等待
instancePool.wait();
}
}
}
if (log.isTraceEnabled()) {
log.trace(" Returning allocated STM instance");
}
countAllocated.incrementAndGet();
return instancePool.pop();
}
}
}

//从分析StrandardWrapper可以看出,从StrandardWrapper的allocate方法,看出,当Servlet是单线程模型是则直接返回instance,否则加载一个新的Servlet,push到Servlet栈instancePool中。
总结:
[color=blue]从上面可以分析得出每个Host关联一个StardardContext,StardardContext是根据Web.xml的配置,初始化欢迎界面,角色权限,初始参数,错误处理,Listener,Filter和Sevlet等。
在加载Servlet的过程中,通过反射,调用Servlet的init方法。每个StandardContext有多个StandardWrappe子容器,StandardWrappe关联一个StandardPileLine,StandardPileLine中装配这StandardWrappeValve,StandardWrappeValve处理HTTP请求,通过invoke(),在invoke中,同StandardWrappe的allocate方法获取Servlet实例instance,然后将Request,StandardWrapper,Servlet包装到ApplicationFilterChain,然后执行Filter的doFilter方法;在Filter的doFilter中,限制性filter操作,然后通过反射调用servlet的service方法。下篇文章将介绍ContextConfig[/color]
附:
//tld配置
public final class TldConfig  implements LifecycleListener {

private static final String TLD_EXT = ".tld";
private static final String WEB_INF = "/WEB-INF/";
private static final String WEB_INF_LIB = "/WEB-INF/lib/";
//监听事件
public void lifecycleEvent(LifecycleEvent event) {
// Identify the context we are associated with
try {
context = (Context) event.getLifecycle();
}
if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
init();
} else if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
try {
execute();
}
}
//扫描所有的tld,lib,初始化
public void execute() {
long t1=System.currentTimeMillis();
/*
* Priority order of URIs required by spec is:
* 1. J2EE platform taglibs - Tomcat doesn't provide these
* 2. web.xml entries
* 3. JARS in WEB-INF/lib & TLDs under WEB-INF (equal priority)
* 4. Additional entries from the container
*
* Keep processing order in sync with o.a.j.compiler.TldLocationsCache
*/
// Stage 2 - web.xml entries
tldScanWebXml();
// Stage 3a - TLDs under WEB-INF (not lib or classes)
tldScanResourcePaths(WEB_INF);
// Stages 3b & 4
JarScanner jarScanner = context.getJarScanner();
TldJarScannerCallback tldCallBack = new TldJarScannerCallback();
jarScanner.scan(context.getServletContext(), context.getLoader().getClassLoader(),
tldCallBack, noTldJars);
// Now add all the listeners we found to the listeners for this context
String list[] = getTldListeners();
for( int i=0; i<list.length; i++ ) {
context.addApplicationListener(
new ApplicationListener(list[i], true));
}
long t2=System.currentTimeMillis();
if( context instanceof StandardContext ) {
((StandardContext)context).setTldScanTime(t2-t1);
}
}
}

//StandardContextValve
final class StandardContextValve extends ValveBase {
/**
* Select the appropriate child Wrapper to process this request,
* based on the specified request URI. If no matching Wrapper can
* be found, return an appropriate HTTP error.
*/
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {

// Disallow any direct access to resources under WEB-INF or META-INF
MessageBytes requestPathMB = request.getRequestPathMB();
if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/META-INF"))
|| (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// Select the Wrapper to be used for this Request
Wrapper wrapper = request.getWrapper();
if (wrapper == null || wrapper.isUnavailable()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}

// Acknowledge the request
try {
response.sendAcknowledgement();
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
}
wrapper.getPipeline().getFirst().invoke(request, response);
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值