在前面的文章中,知道在HostConfig对象中,会在host启动的时候开始部署当前host下面的web应用程序,一般情况下就是扫描webapps文件夹,然后为每个web应用程序创建Context对象,一般情况下也就是StandardContext。。
这里其实一个Context也就代表了一个web应用程序,它具体来维护应用程序定义的servlet,filter,资源。。。
但是这里由于Context接口实在比较大,不好挨个挨个字段都贴出来,这里就先来看看StandardContext的创建与启动的过程吧,先来看看构造函数:
public StandardContext() {
super();
pipeline.setBasic(new StandardContextValve()); //在pipeline上添加basic的valve
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");
}
}
这里主要是在当前pipeline上面添加了一个basic格式的valve对象。。。还有其他的一些事情。。。接下来来看看初始化吧:
protected void initInternal() throws LifecycleException {
super.initInternal();
// Register the naming resources
if (namingResources != null) {
namingResources.init();
}
if (resources != null) {
resources.start(); //启动本地资源
}
// Send j2ee.object.created notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.object.created",
this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
}
这里主要还是进行资源的初始化吧。。。那么接下来来看看启动的方法吧:
//启动context,这里可以理解为启动一个应用程序
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); //还没有开始config
boolean ok = true; //必须保证每一步都ok
// Currently this is effectively a NO-OP but needs to be called to
// ensure the NamingResources follows the correct lifecycle
if (namingResources != null) {
namingResources.start();
}
//resource部分
// Add missing components as necessary
if (getResources() == null) { // (1) Required by Loader //可以理解为当前应用程序的资源引用
if (log.isDebugEnabled())
log.debug("Configuring default Resources");
try {
setResources(new StandardRoot(this)); //创建资源引用
} catch (IllegalArgumentException e) {
log.error("Error initializing resources: " + e.getMessage());
ok = false;
}
}
if (ok) {
resourcesStart();
}
//loader部分
if (getLoader() == null) { //这一步用于创建loader对象
WebappLoader webappLoader = new WebappLoader(getParentClassLoader()); //这里用于创建当前context用的classLoader,这里会将parent设置为sharedclassLoader
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader); //保存创建的loader
}
// Initialize character set mapper
getCharsetMapper(); //charsetmapper
// Post work directory
postWorkDirectory();
// Validate required extensions
boolean dependencyCheck = true;
try { //扩展名检查
dependencyCheck = ExtensionValidator.validateApplication
(getResources(), this);
} catch (IOException ioe) {
log.error("Error in dependencyCheck", ioe);
dependencyCheck = false;
}
if (!dependencyCheck) {
// do not make application available if depency check fails
ok = false;
}
// Reading the "catalina.useNaming" environment variable
String useNamingProperty = System.getProperty("catalina.useNaming");
if ((useNamingProperty != null)
&& (useNamingProperty.equals("false"))) {
useNaming = false;
}
if (ok && isUseNaming()) {
if (getNamingContextListener() == null) {
NamingContextListener ncl = new NamingContextListener(); //创建一个NamingContextListener,并将其加入到listeneres里面去
ncl.setName(getNamingContextName());
ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
addLifecycleListener(ncl);
setNamingContextListener(ncl);
}
}
// Standard container startup
if (log.isDebugEnabled())
log.debug("Processing standard container startup");
// Binding thread
ClassLoader oldCCL = bindThread(); //这个是干啥的。。?
try {
if (ok) {
// Start our subordinate components, if any
Loader loader = getLoader(); //获取loader
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start(); //启动当前web应用程序的loader
// since the loader just started, the webapp classloader is now
// created.
//设置loader的一些参数
setClassLoaderProperty("clearReferencesStatic",
getClearReferencesStatic());
setClassLoaderProperty("clearReferencesStopThreads",
getClearReferencesStopThreads());
setClassLoaderProperty("clearReferencesStopTimerThreads",
getClearReferencesStopTimerThreads());
setClassLoaderProperty("clearReferencesHttpClientKeepAliveThread",
getClearReferencesHttpClientKeepAliveThread());
// 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();
Cluster cluster = getClusterInternal();
if ((cluster != null) && (cluster instanceof Lifecycle))
((Lifecycle) cluster).start();
Realm realm = getRealmInternal();
if ((realm != null) && (realm instanceof Lifecycle))
((Lifecycle) realm).start();
// Notify our interested LifecycleListeners
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); //通知监听,激活config启动的事件,这里会在contextConfig里面执行xml文件的解析等工作
// Start our child containers, if not already started
for (Container child : findChildren()) { //启动所有的child,其实这里主要是启动wrapper对象
if (!child.getState().isAvailable()) {
child.start();
}
}
// Start the Valves in our pipeline (including the basic),
// if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start(); //启动pipeline
}
// Acquire clustered manager
Manager contextManager = null;
Manager manager = getManager(); //创建manager对象,这个主要是跟session相关的吧
if (manager == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("standardContext.cluster.noManager",
Boolean.valueOf((getCluster() != null)),
Boolean.valueOf(distributable)));
}
if ( (getCluster() != null) && distributable) { //这里要根据是否有集群的配置来设置manager对象
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);
}
}
if (!getConfigured()) {
log.error( "Error getConfigured");
ok = false;
}
// We put the resources into the servlet context
if (ok)
getServletContext().setAttribute
(Globals.RESOURCES_ATTR, getResources()); //在servletcontext里面保存整个web应用程序的rootresource,可以用于加载文件什么的
if (ok ) { //接下来主要是创建instancemanager,它用于负责管理当前web应用程序对象的实例化
if (getInstanceManager() == null) {
javax.naming.Context context = null;
if (isUseNaming() && getNamingContextListener() != null) {
context = getNamingContextListener().getEnvContext();
}
Map<String, Map<String, String>> injectionMap = buildInjectionMap(
getIgnoreAnnotations() ? new NamingResourcesImpl(): getNamingResources());
setInstanceManager(new DefaultInstanceManager(context, //创建instancemanager
injectionMap, this, this.getClass().getClassLoader()));
getServletContext().setAttribute(
InstanceManager.class.getName(), getInstanceManager());
}
}
// Create context attributes that will be required
if (ok) { //设置jar包的扫描
getServletContext().setAttribute(
JarScanner.class.getName(), getJarScanner());
}
// Set up the context init params
mergeParameters(); //合并context的一些初始化参数
// Call ServletContainerInitializers
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
initializers.entrySet()) {
try {
entry.getKey().onStartup(entry.getValue(),
getServletContext());
} catch (ServletException e) {
log.error(sm.getString("standardContext.sciFail"), e);
ok = false;
break;
}
}
// Configure and call application event listeners
if (ok) {
if (!listenerStart()) { //启动contextListener啥的
log.error( "Error listenerStart");
ok = false;
}
}
// Check constraints for uncovered HTTP methods
// Needs to be after SCIs and listeners as they may programatically
// change constraints
if (ok) {
checkConstraintsForUncoveredMethods(findConstraints());
}
try {
// Start manager
Manager manager = getManager();
if ((manager != null) && (manager instanceof Lifecycle)) {
((Lifecycle) getManager()).start(); //启动manager对象
}
} catch(Exception e) {
log.error("Error manager.start()", e);
ok = false;
}
// Configure and call application filters
if (ok) {
if (!filterStart()) {
log.error("Error filterStart");
ok = false;
}
}
// Load and initialize all "load on startup" servlets
if (ok) {
loadOnStartup(findChildren()); //如果有load on startup的话,需要启动
}
// Start ContainerBackgroundProcessor thread
super.threadStart(); //启动后台线程,用于执行一些周期事物
} finally {
// Unbinding thread
unbindThread(oldCCL);
}
// Set available status depending upon startup success
if (ok) {
if (log.isDebugEnabled())
log.debug("Starting completed");
} else {
log.error(sm.getString("standardContext.startFailed", getName()));
}
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);
}
// Reinitializing if something went wrong
if (!ok) {
setState(LifecycleState.FAILED);
} else {
setState(LifecycleState.STARTING);
}
}
这部分代码够长的吧,这里将要做的工作归纳如下:
(1)设置当前web应用程序的资源引用,StandardRoot对象,可以用它来载入当前应用程序的资源。例如文件什么的。
(2)创建WebappLoader对象,内部还会创建WebAppClassLoader对象,具体这个是干嘛的,应该从名字猜也能猜出来吧,并启动
(3)激活CONFIGURE_START_EVENT事件,前面的文章已经交代过在context创建时会附带给其加一个listener,这个listener就是ContextConfig,它会响应CONFIGURE_START_EVENT事件,其实就是开始读取配置,包括servlet的定义什么,并将读取到的信息加入到当前context对象中。。
(4)启动刚刚通过处理配置文件得到的对象。例如wrapper,用于包装servlet。
(5)创建manager并启动,它用于处理session相关的工作
(6)在servletContext对象中设置一些属性
(7)初始化各种listener,例如定义的contextListener啥的
上面就是Context的启动要做的主要的事情。。。
好像这里说的比较粗糙吧,因为这部分涉及到的东西实在太多了。。以后再来一点一点的分析吧。。。