文档章节

StandardContext分析-tomcat6.x源码阅读

douglaswei
 douglaswei
发布于 2013/10/20 00:46
字数 4289
阅读 340
收藏 6

2013-10-06

StandardContext 是什么

是org.apache.catalina.Context的标准实现,继承自ContainerBase基容器,具备容器的功能,在tomcat结构层次图中位于Host内部,包含ServletWrapper容器,它的角色是管理在其内部的ServletWrapper,从Host那里接收请求信息,加载实例化Servlet,然后选择一个合适的ServletWrapper处理,同一个Context内部中Servlet数据共享,不同Context之间数据隔离。在Context层面上,Filter的作用就出来了,Filter是用来过滤Servlet的组件,Context负责在每个Servlet上面调用Filter过滤,Context与开发Servlet息息相关,跟前面的几个组件相比,Context能够提供更多的信息给Servlet。一个webapp有一个Context,Context负责管理在其内部的组件:Servlet,Cookie,Session等等。

Context
tomcat定义的Servlet上下文接口,也即一个webapp的应用环境。tomcat定义了一个Context所必须具备的功能,定义对Cookie,Session,ServletWrapper等组件的管理,为一个请求选项合适的Servlet方法,他要解决的问题是为请求选择Servlet,Servlet之间的数据共享问题。

ContainerBase
容器基类,StandardContext继承该类,具备了基容器的功能和方法。

StandardContext()
默认构造器,设定basicValve, 初始化NotificationBroadcasterSupport,用户支持j2ee企业功能。

StandardContextValve
Context的标准BasicValve,作用连接Context和ServletWrapper的工具。StandardContextValve主要完成的功能是:

  • WEB-INF和META-INF目录的访问选项判断,tomcat禁止直接访问这两个目录
  • 选定Wrapper处理请求

URLEncoder
跟URL相关,具有对URL编码的功能,替换特殊字符功能。

charsetMapper : CharsetMapper
Context多支持的字符集,跟本地化有关,负责处理本地字符问题。

context : ApplicationContext
Servlet的上下文,一个wepapp中提供一个全局的servlet上下,提供了一些列的访问资源的接口,servlet通过ApplicationContext获取servlet的运行环境信息,加载资源,获取资源。与StandardContext的作用是相辅相成的关系,两种的角色不同,ApplicationContext负责的是servlet运行环境上下信息,不关心session管理,cookie管理,servlet的加载,servlet的选择问题,请求信息,一句话就是:他是servlet管家。StandardContext需要负责管理session,Cookie,Servlet的加载和卸载,负责请求信息的处理,掌握控制权,他是servlet的爹妈。

exceptionPages : HashMap
错误页面信息匹配,对于404,403等HTTP错误码,tomcat通过映射页面来定制提示信息。

filterConfigs : HashMap
k/v的方式保存Filter与Fifter配置信息,在过滤请求信息是使用到。

filterDefs : HashMap
k/v的方式保存Filter与Filter的定义信息。

filterMaps : FilterMap[]
webapp中所有的Filter集合。别名映射关系。

mapper : Mapper
Servlet与访问url匹配集合。

namingContextListener : NamingContextListener
是JNDI中的内容,不了解,比较复杂,只知道是负责监听javax.naming.Context资源事件。NamingContextListener实现了LifecycleListener、ContainerListener、PropertyChangeListener3个接口,具备监听Lifecycle组件,Container组件、PropertyChange的事件能力。

namingResources : NamingResources
是JNDI中的内容,不了解,比较复杂,知道它管理命名资源,将要加载的资源封装成对象,可以直接从NamingResources获取对象了。

servletMappings : HashMap
访问url匹配与Servlet类映射。

wrapperClassName : String
用来处理Servlet包装的Wrapper类类名,负责加载管理Servlet。

wrapperClass : Class
用来处理Servlet包装的Wrapper类,负责加载管理Servlet。

addServletMapping(String, String, boolean)
添加servlet和url模式映射关系。从web.xml或者注解中获取Servlet与url的关系,然后保存到Context中。

createWrapper()
实例化Wrapper类,创建一个容纳Servlet的容器,并在Wrapper上注册监听器。主要完成以下步骤:

  • 创建Wrapper类实例
  • 在Wrapper类实例上注册组件实例化监听
  • 在Wrapper类实例上注册组件生命周期监听
  • 在Wrapper类实例上注册容器事件期监听

	/**
	 * Factory method to create and return a new Wrapper instance, of the Java
	 * implementation class appropriate for this Context implementation. The
	 * constructor of the instantiated Wrapper will have been called, but no
	 * properties will have been set.
	 */
	public Wrapper createWrapper() {

		Wrapper wrapper = null;
		if (wrapperClass != null) {
			try {
				wrapper = (Wrapper) wrapperClass.newInstance();
			} catch (Throwable t) {
				log.error("createWrapper", t);
				return (null);
			}
		} else {
			wrapper = new StandardWrapper();
		}

		synchronized (instanceListenersLock) {
			for (int i = 0; i < instanceListeners.length; i++) {
				try {
					Class clazz = Class.forName(instanceListeners[i]);
					InstanceListener listener = (InstanceListener) clazz
							.newInstance();
					wrapper.addInstanceListener(listener);
				} catch (Throwable t) {
					log.error("createWrapper", t);
					return (null);
				}
			}
		}

		synchronized (wrapperLifecyclesLock) {
			for (int i = 0; i < wrapperLifecycles.length; i++) {
				try {
					Class clazz = Class.forName(wrapperLifecycles[i]);
					LifecycleListener listener = (LifecycleListener) clazz
							.newInstance();
					if (wrapper instanceof Lifecycle)
						((Lifecycle) wrapper).addLifecycleListener(listener);
				} catch (Throwable t) {
					log.error("createWrapper", t);
					return (null);
				}
			}
		}

		synchronized (wrapperListenersLock) {
			for (int i = 0; i < wrapperListeners.length; i++) {
				try {
					Class clazz = Class.forName(wrapperListeners[i]);
					ContainerListener listener = (ContainerListener) clazz
							.newInstance();
					wrapper.addContainerListener(listener);
				} catch (Throwable t) {
					log.error("createWrapper", t);
					return (null);
				}
			}
		}

		return (wrapper);

	}

reload()
重新加载数据,用于当类或者配置文件发送变化时重新加载,从代码中可以看出来,tomcat是通过重启来完成的,主要有几个步骤

  • 设置暂停标记
  • 停止Context
  • 启动Context
  • 清楚暂停标记

filterStart()
配置和初始化过滤Servlet的Filter集合。

filterStop()
释放和清除Filter集合配置。

listenerStart()
配置并实例化注册在Context上的监听器集合,用于监听在Context上面触发的事件,监听器用于监听Conext事件和Servlet事件。分下面几个步骤:

  • 获取类加载器,缘由:出于安全考虑,不同Context使用不同类加载器,此外获取类加载器用于实例化监听器
  • 获取监听器类列表,在第一步取得的类加载器中实例化
  • 区分监听器类型(分ContextEvent监听器和生命周期监听器),注册在Context组件上,分别在不同的监听器集合列表中
  • 获取ContextEvent监听器,触发Context实例化事件

	/**
	 * Configure the set of instantiated application event listeners for this
	 * Context. Return <code>true</code> if all listeners wre initialized
	 * successfully, or <code>false</code> otherwise.
	 */
	public boolean listenerStart() {

		if (log.isDebugEnabled())
			log.debug("Configuring application event listeners");

		// Instantiate the required listeners
		//取当前Context的类加载器,用于实例化监听器需要。不同Context的类加载可能不同,出于安全考虑
		ClassLoader loader = getLoader().getClassLoader();
		String listeners[] = findApplicationListeners();
		Object results[] = new Object[listeners.length];
		boolean ok = true;
		//遍历所有监听器,并实例化到results列表中
		for (int i = 0; i < results.length; i++) {
			if (getLogger().isDebugEnabled())
				getLogger().debug(
						" Configuring event listener class '" + listeners[i]
								+ "'");
			try {
				Class clazz = loader.loadClass(listeners[i]);
				results[i] = clazz.newInstance();
				// Annotation processing
				if (!getIgnoreAnnotations()) {
					getAnnotationProcessor().processAnnotations(results[i]);
					getAnnotationProcessor().postConstruct(results[i]);
				}
			} catch (Throwable t) {
				getLogger().error(
						sm.getString("standardContext.applicationListener",
								listeners[i]), t);
				ok = false;
			}
		}
		if (!ok) {
			getLogger().error(
					sm.getString("standardContext.applicationSkipped"));
			return (false);
		}

		// Sort listeners in two arrays
		ArrayList eventListeners = new ArrayList();
		ArrayList lifecycleListeners = new ArrayList();
		//遍历监听器,区分ContextEvent监听器和LifecycleEvent监听器
		for (int i = 0; i < results.length; i++) {
			if ((results[i] instanceof ServletContextAttributeListener)
					|| (results[i] instanceof ServletRequestAttributeListener)
					|| (results[i] instanceof ServletRequestListener)
					|| (results[i] instanceof HttpSessionAttributeListener)) {
				eventListeners.add(results[i]);
			}
			if ((results[i] instanceof ServletContextListener)
					|| (results[i] instanceof HttpSessionListener)) {
				lifecycleListeners.add(results[i]);
			}
		}

		//注册监听器
		setApplicationEventListeners(eventListeners.toArray());
		//注册监听器
		setApplicationLifecycleListeners(lifecycleListeners.toArray());

		// Send application start events

		if (getLogger().isDebugEnabled())
			getLogger().debug("Sending application start events");

		Object instances[] = getApplicationLifecycleListeners();
		if (instances == null)
			return (ok);
		ServletContextEvent event = new ServletContextEvent(getServletContext());
		//触发Context实例化事件,通知监听
		for (int i = 0; i < instances.length; i++) {
			if (instances[i] == null)
				continue;
			if (!(instances[i] instanceof ServletContextListener))
				continue;
			ServletContextListener listener = (ServletContextListener) instances[i];
			try {
				fireContainerEvent("beforeContextInitialized", listener);
				listener.contextInitialized(event);
				fireContainerEvent("afterContextInitialized", listener);
			} catch (Throwable t) {
				fireContainerEvent("afterContextInitialized", listener);
				getLogger().error(
						sm.getString("standardContext.listenerStart",
								instances[i].getClass().getName()), t);
				ok = false;
			}
		}
		return (ok);

	}

listenerStop()
在Conext上停止监听器的监听,步骤与启动时相反,首先触发停止监听事件,然后再清除资源。

resourcesStart()
分配Context的目录资源,这里涉及到目录服务。

resourcesStop()
停止Context的目录资源,这里涉及到目录服务。

loadOnStartup(Container[])
这个方法是用来处理在web.xml中配置的loadonstartup Servlet,需要在Context启动时加载实例化,而不是要等待请求触发才实例化。特殊servlet提前实例化加快了第一次访问速度。主要有两个步骤:

  • 扫描Servlet,标记loadonstartup 大于0的Servlet
  • 加载并实例化Servlet,标记为可用

	/**
	 * Load and initialize all servlets marked "load on startup" in the web
	 * application deployment descriptor.
	 * 
	 * @param children
	 *            Array of wrappers for all currently defined servlets
	 *            (including those not declared load on startup)
	 */
	public void loadOnStartup(Container children[]) {

		// Collect "load on startup" servlets that need to be initialized
		TreeMap map = new TreeMap();
		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 list = (ArrayList) map.get(key);
			if (list == null) {
				list = new ArrayList();
				map.put(key, list);
			}
			list.add(wrapper);
		}

		// Load the collected "load on startup" servlets
		Iterator keys = map.keySet().iterator();
		while (keys.hasNext()) {
			Integer key = (Integer) keys.next();
			ArrayList list = (ArrayList) map.get(key);
			Iterator wrappers = list.iterator();
			while (wrappers.hasNext()) {
				Wrapper wrapper = (Wrapper) wrappers.next();
				try {
					wrapper.load();
				} catch (ServletException e) {
					getLogger()
							.error(sm.getString(
									"standardWrapper.loadException", getName()),
									StandardWrapper.getRootCause(e));
					// NOTE: load errors (including a servlet that throws
					// UnavailableException from tht init() method) are NOT
					// fatal to application startup
				}
			}
		}

	}

init()
负责初始化Context,为Context启动做准备工作。在方法中完成了Context配置类的实例化,并将其注册到生命周期监听器集合中,调用父类初始化函数super.init(),然后出发初始化事件。

start()
负责启动Context,启动web app应用,在启动方法中要完成很多步骤:

  • 判定是否已经启动,是否初始化
  • 注册JMX,触发BEFORE_START_EVENT事件
  • 标记状态(不可用和未配置)
  • 配置Context的目录上下文,用于监控应用文件
  • 启动Context关联的目录服务,监控文件
  • 配置realm权限控制
  • 设置类加载器
  • 设置字符集,工作目录
  • 校验目录文件合法性
  • 注册命名服务监听器
  • 线程绑定,为CL和JNDI在启动,重新加载,停止时提供支持
  • 启动loader类加载器
  • 启动Context中的组件
  • 触发启动事件
  • 解绑线程
  • 配置mapper(serlvet与url匹配规则)
  • 绑定线程
  • 处理欢迎页面
  • 合并Context参数
  • 触发AFTER_START_EVENT事件
  • 启动manager session管理器
  • 启动后台任务处理线程
  • 开启Filter过滤功能
  • 加载实例化标记为loadOnStartup的Servlet
  • 解绑线程
  • 判断是否启动成功,设置标记
  • 注册JMX
  • 释放jars包
  • 判断是否启动成功

	/**
	 * Start this Context component.
	 * 
	 * @exception LifecycleException
	 *                if a startup error occurs
	 */
	public synchronized void start() throws LifecycleException {
		// if (lazy ) return;
		if (started) {
			if (log.isInfoEnabled())
				log.info(sm
						.getString("containerBase.alreadyStarted", logName()));
			return;
		}
		if (!initialized) {
			try {
				init();
			} catch (Exception ex) {
				throw new LifecycleException("Error initializaing ", ex);
			}
		}
		if (log.isDebugEnabled())
			log.debug("Starting " + ("".equals(getName()) ? "ROOT" : getName()));

		// Set JMX object name for proper pipeline registration
		preRegisterJMX();

		if ((oname != null)
				&& (Registry.getRegistry(null, null).getMBeanServer()
						.isRegistered(oname))) {
			// As things depend on the JMX registration, the context
			// must be reregistered again once properly initialized
			Registry.getRegistry(null, null).unregisterComponent(oname);
		}

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);

		setAvailable(false);
		setConfigured(false);
		boolean ok = true;

		// Add missing components as necessary
		if (webappResources == null) { // (1) Required by Loader
			if (log.isDebugEnabled())
				log.debug("Configuring default Resources");
			try {
				if ((docBase != null) && (docBase.endsWith(".war"))
						&& (!(new File(getBasePath())).isDirectory()))
					setResources(new WARDirContext());
				else
					setResources(new FileDirContext());
			} catch (IllegalArgumentException e) {
				log.error("Error initializing resources: " + e.getMessage());
				ok = false;
			}
		}
		if (ok) {
			if (!resourcesStart()) {
				log.error("Error in resourceStart()");
				ok = false;
			}
		}

		// Look for a realm - that may have been configured earlier.
		// If the realm is added after context - it'll set itself.
		// TODO: what is the use case for this ?
		if (realm == null && mserver != null) {
			ObjectName realmName = null;
			try {
				realmName = new ObjectName(getEngineName()
						+ ":type=Realm,host=" + getHostname() + ",path="
						+ getPath());
				if (mserver.isRegistered(realmName)) {
					mserver.invoke(realmName, "init", new Object[] {},
							new String[] {});
				}
			} catch (Throwable t) {
				if (log.isDebugEnabled())
					log.debug("No realm for this host " + realmName);
			}
		}

		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);
		} 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 (namingContextListener == null) {
				namingContextListener = new NamingContextListener();
				namingContextListener.setName(getNamingContextName());
				addLifecycleListener(namingContextListener);
			}
		}

		// Standard container startup
		if (log.isDebugEnabled())
			log.debug("Processing standard container startup");

		// Binding thread
		ClassLoader oldCCL = bindThread();

		boolean mainOk = false;

		try {

			if (ok) {

				started = true;

				// 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 ((logger != null) && (logger instanceof Lifecycle))
					((Lifecycle) logger).start();

				if ((cluster != null) && (cluster instanceof Lifecycle))
					((Lifecycle) cluster).start();
				if ((realm != null) && (realm instanceof Lifecycle))
					((Lifecycle) realm).start();
				if ((resources != null) && (resources instanceof Lifecycle))
					((Lifecycle) resources).start();

				// Start our child containers, if any
				Container children[] = findChildren();
				for (int i = 0; i < children.length; i++) {
					if (children[i] instanceof Lifecycle)
						((Lifecycle) children[i]).start();
				}

				// Start the Valves in our pipeline (including the basic),
				// if any
				if (pipeline instanceof Lifecycle) {
					((Lifecycle) pipeline).start();
				}

				// Notify our interested LifecycleListeners
				lifecycle.fireLifecycleEvent(START_EVENT, null);

				// Acquire clustered manager
				Manager contextManager = null;
				if (manager == null) {
					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) {
					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);
				}

				mainOk = true;

			}

		} finally {
			// Unbinding thread
			unbindThread(oldCCL);
			if (!mainOk) {
				// An exception occurred
				// Register with JMX anyway, to allow management
				registerJMX();
			}
		}

		if (!getConfigured()) {
			log.error("Error getConfigured");
			ok = false;
		}

		// We put the resources into the servlet context
		if (ok)
			getServletContext().setAttribute(Globals.RESOURCES_ATTR,
					getResources());

		// Initialize associated mapper
		mapper.setContext(getPath(), welcomeFiles, resources);

		// Binding thread
		oldCCL = bindThread();

		// Set annotation processing parameter for Jasper (unfortunately, since
		// this can be configured in many places and not just in
		// /WEB-INF/web.xml,
		// there are not many solutions)
		// Initialize annotation processor
		if (ok && !getIgnoreAnnotations()) {
			if (annotationProcessor == null) {
				if (isUseNaming() && namingContextListener != null) {
					annotationProcessor = new DefaultAnnotationProcessor(
							namingContextListener.getEnvContext());
				} else {
					annotationProcessor = new DefaultAnnotationProcessor(null);
				}
			}
			getServletContext().setAttribute(
					AnnotationProcessor.class.getName(), annotationProcessor);
		}

		try {

			// Create context attributes that will be required
			if (ok) {
				postWelcomeFiles();
			}

			// Set up the context init params
			mergeParameters();

			if (ok) {
				// Notify our interested LifecycleListeners
				lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
			}

			// Configure and call application event listeners
			if (ok) {
				if (!listenerStart()) {
					log.error("Error listenerStart");
					ok = false;
				}
			}

			try {
				// Start manager
				if ((manager != null) && (manager instanceof Lifecycle)) {
					((Lifecycle) getManager()).start();
				}

				// Start ContainerBackgroundProcessor thread
				super.threadStart();
			} 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());
			}

		} finally {
			// Unbinding thread
			unbindThread(oldCCL);
		}

		// Set available status depending upon startup success
		if (ok) {
			if (log.isDebugEnabled())
				log.debug("Starting completed");
			setAvailable(true);
		} else {
			log.error(sm.getString("standardContext.startFailed", getName()));
			try {
				stop();
			} catch (Throwable t) {
				log.error(sm.getString("standardContext.startCleanup"), t);
			}
			setAvailable(false);
		}

		// JMX registration
		registerJMX();

		startTime = System.currentTimeMillis();

		// Send j2ee.state.running notification
		if (ok && (this.getObjectName() != null)) {
			Notification notification = new Notification("j2ee.state.running",
					this.getObjectName(), sequenceNumber++);
			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 && started) {
			stop();
		}

		// cacheContext();
	}

cacheContext()
缓存Context对象,将对象序列化到本地文件中。

mergeParameters()
合并Context初始化参数配置

stop()
负责停止Context,清理Context中组件的状态,以启动Context的顺序到这来:

  • 触发BEFORE_STOP_EVENT事件
  • 标记不可用
  • 绑定线程
  • 停止子容器
  • 停止Filter过滤
  • 停止后台处理线程
  • 停止监听器
  • 清除Context字符集
  • 触发STOP_EVENT事件,标记未启动
  • 停止pipeline数据传输线
  • 清除Context属性
  • 停止资源服务
  • 停止Conext上的组件
  • 解绑线程
  • 重置Conext,触发AFTER_STOP_EVENT事件

	/**
	 * Stop this Context component.
	 * 
	 * @exception LifecycleException
	 *                if a shutdown error occurs
	 */
	public synchronized void stop() throws LifecycleException {

		// Validate and update our current component state
		if (!started) {
			if (log.isInfoEnabled())
				log.info(sm.getString("containerBase.notStarted", logName()));
			return;
		}

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);

		// Send j2ee.state.stopping notification
		if (this.getObjectName() != null) {
			Notification notification = new Notification("j2ee.state.stopping",
					this.getObjectName(), sequenceNumber++);
			broadcaster.sendNotification(notification);
		}

		// Mark this application as unavailable while we shut down
		setAvailable(false);

		// Binding thread
		ClassLoader oldCCL = bindThread();

		try {

			// Stop our child containers, if any
			Container[] children = findChildren();
			for (int i = 0; i < children.length; i++) {
				if (children[i] instanceof Lifecycle)
					((Lifecycle) children[i]).stop();
			}

			// Stop our filters
			filterStop();

			// Stop ContainerBackgroundProcessor thread
			super.threadStop();

			if ((manager != null) && (manager instanceof Lifecycle)) {
				((Lifecycle) manager).stop();
			}

			// Stop our application listeners
			listenerStop();

			// Finalize our character set mapper
			setCharsetMapper(null);

			// Normal container shutdown processing
			if (log.isDebugEnabled())
				log.debug("Processing standard container shutdown");
			// Notify our interested LifecycleListeners
			lifecycle.fireLifecycleEvent(STOP_EVENT, null);
			started = false;

			// Stop the Valves in our pipeline (including the basic), if any
			if (pipeline instanceof Lifecycle) {
				((Lifecycle) pipeline).stop();
			}

			// Clear all application-originated servlet context attributes
			if (context != null)
				context.clearAttributes();

			// Stop resources
			resourcesStop();

			if ((realm != null) && (realm instanceof Lifecycle)) {
				((Lifecycle) realm).stop();
			}
			if ((cluster != null) && (cluster instanceof Lifecycle)) {
				((Lifecycle) cluster).stop();
			}
			if ((logger != null) && (logger instanceof Lifecycle)) {
				((Lifecycle) logger).stop();
			}
			if ((loader != null) && (loader instanceof Lifecycle)) {
				((Lifecycle) loader).stop();
			}

		} finally {

			// Unbinding thread
			unbindThread(oldCCL);

		}

		// Send j2ee.state.stopped notification
		if (this.getObjectName() != null) {
			Notification notification = new Notification("j2ee.state.stopped",
					this.getObjectName(), sequenceNumber++);
			broadcaster.sendNotification(notification);
		}

		// Reset application context
		context = null;

		// This object will no longer be visible or used.
		try {
			resetContext();
		} catch (Exception ex) {
			log.error("Error reseting context " + this + " " + ex, ex);
		}

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);

		if (log.isDebugEnabled())
			log.debug("Stopping complete");

	}

destroy()
负责销毁Context任务,在方法中主要是调用基类的destroy()方法销毁Context。


	/**
	 * Destroy needs to clean up the context completely.
	 * 
	 * The problem is that undoing all the config in start() and restoring a
	 * 'fresh' state is impossible. After stop()/destroy()/init()/start() we
	 * should have the same state as if a fresh start was done - i.e read
	 * modified web.xml, etc. This can only be done by completely removing the
	 * context object and remapping a new one, or by cleaning up everything.
	 * 
	 * XXX Should this be done in stop() ?
	 * 
	 */
	public void destroy() throws Exception {
		if (oname != null) {
			// Send j2ee.object.deleted notification
			Notification notification = new Notification("j2ee.object.deleted",
					this.getObjectName(), sequenceNumber++);
			broadcaster.sendNotification(notification);
		}
		super.destroy();

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(DESTROY_EVENT, null);

		synchronized (instanceListenersLock) {
			instanceListeners = new String[0];
		}

	}

resetContext()
负责重置Context容器,将恢复至原始状态。

adjustURLPattern(String)
负责将URL规范化。


	/**
	 * Adjust the URL pattern to begin with a leading slash, if appropriate
	 * (i.e. we are running a servlet 2.2 application). Otherwise, return the
	 * specified URL pattern unchanged.
	 * 
	 * @param urlPattern
	 *            The URL pattern to be adjusted (if needed) and returned
	 */
	protected String adjustURLPattern(String urlPattern) {

		if (urlPattern == null)
			return (urlPattern);
		if (urlPattern.startsWith("/") || urlPattern.startsWith("*."))
			return (urlPattern);
		if (!isServlet22())
			return (urlPattern);
		if (log.isDebugEnabled())
			log.debug(sm.getString("standardContext.urlPattern.patternWarning",
					urlPattern));
		return ("/" + urlPattern);

	}

bindThread()
绑定线程类加载器,保存现有线程的类加载器,然后将Context的类加载器绑定至当前线程中,将Context资源服务绑定至当前线程,绑定命名服务类加载器,这样可以确保在Context中的资源在同一个类加载器上面。


	/**
	 * Bind current thread, both for CL purposes and for JNDI ENC support during
	 * : startup, shutdown and realoading of the context.
	 * 
	 * @return the previous context class loader
	 */
	private ClassLoader bindThread() {

		ClassLoader oldContextClassLoader = Thread.currentThread()
				.getContextClassLoader();

		if (getResources() == null)
			return oldContextClassLoader;

		if (getLoader().getClassLoader() != null) {
			Thread.currentThread().setContextClassLoader(
					getLoader().getClassLoader());
		}

		DirContextURLStreamHandler.bindThread(getResources());

		if (isUseNaming()) {
			try {
				ContextBindings.bindThread(this, this);
			} catch (NamingException e) {
				// Silent catch, as this is a normal case during the early
				// startup stages
			}
		}

		return oldContextClassLoader;

	}

unbindThread(ClassLoader)
解绑线程类加载器,与bindThrea()方法的功能相反,还原线程类加载器。


	/**
	 * Unbind thread.
	 */
	private void unbindThread(ClassLoader oldContextClassLoader) {

		if (isUseNaming()) {
			ContextBindings.unbindThread(this, this);
		}

		DirContextURLStreamHandler.unbindThread();

		Thread.currentThread().setContextClassLoader(oldContextClassLoader);
	}

validateURLPattern(String)
验证访问URL是否合法。


	/**
	 * Validate the syntax of a proposed <code>&lt;url-pattern&gt;</code> for
	 * conformance with specification requirements.
	 * 
	 * @param urlPattern
	 *            URL pattern to be validated
	 */
	private boolean validateURLPattern(String urlPattern) {

		if (urlPattern == null)
			return (false);
		if (urlPattern.indexOf('\n') >= 0 || urlPattern.indexOf('\r') >= 0) {
			return (false);
		}
		if (urlPattern.startsWith("*.")) {
			if (urlPattern.indexOf('/') < 0) {
				checkUnusualURLPattern(urlPattern);
				return (true);
			} else
				return (false);
		}
		if ((urlPattern.startsWith("/")) && (urlPattern.indexOf("*.") < 0)) {
			checkUnusualURLPattern(urlPattern);
			return (true);
		} else
			return (false);

	}

getServlets()
返回Context中所有的Servlet。

Context作为web app的上下文,负责维护和管理在Context中的组件的生命周期,Session管理,Filter过滤,加载Servlet,接收响应请求,应用所需资源加载和维护,Context的数据共享和数据安全隔离等一个webapp所需要的全部功能,上面讲到的是Context中的部分内容,其他内容还请自己查看。

有不足之处请斧正,欢迎吐槽...

坚持,骚年,你可以的

© 著作权归作者所有

共有 人打赏支持
douglaswei
粉丝 9
博文 59
码字总数 39760
作品 0
珠海
架构师
私信 提问
谈谈 Tomcat 架构及启动过程 [ 含部署 ]

(点击上方公众号,可快速关注) 来源:Rainstorm, github.com/c-rainstorm/blog/blob/master/tomcat/谈谈%20Tomcat%20架构及启动过程%5B含部署%5D.md 这个题目命的其实是很大的,写的时候还...

ImportNew
2018/01/10
0
0
Tomcat7.0源码分析——Session管理分析(上)

前言   对于广大java开发者而言,就J2EE规范中的Session应该并不陌生,我们可以使用Session管理用户的会话信息,最常见的就是拿Session来存放用户登录、身份、权限及状态等信息。对于使用T...

beliefer
2016/09/23
0
0
2011-07-26 java.lang.NoClassDefFoundError: org...

我在eclipse里直接启动tomcat总是报告这个错误,错误的内容如下: java.lang.NoClassDefFoundError: org/apache/tomcat/startup/Main Caused by: java.lang.ClassNotFoundException: org.ap......

小吕
2011/07/27
0
0
StandardWrapperValve分析-tomcat6.x源码阅读

2013-11-10 StandardWrapperValve是StandardWrapper容器的BasicValve,tomcat使用容器的BasicValve来控制处理请求,StandardWrapperValve的作用是负责为请求选择Wrapper,调用Servlet处理请求...

douglaswei
2013/11/19
0
0
StandardWrapper分析-tomcat6.x源码阅读

2013-10-20 StandardWrapper是什么StandardWrapper是负责对Servlet的封装,在tomcat的结构层次中属于最内层,跟Servlet最接近的组件,是装载Servlet的容器,StandardWrapper没有子容器,因为...

douglaswei
2013/11/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

matlab-自控原理 step、impulse 阶跃、脉冲响应 已知传递函数

  matlab : R2018a 64bit     OS : Windows 10 x64 typesetting : Markdown    blog : my.oschina.net/zhichengjiu    gitee : gitee.com/zhichengjiu   已知传递函数,求其阶跃响应......

志成就
26分钟前
1
0
JDBC事务操作

事务特点:ACID 原子性(Atomicity):事务管理的系列操作必须全部完成,否则就算失败(类似操作系统的PV原语、信号量) 一致性(Consistency):同一个事务只要起始条件一致最终执行的结果一致 隔...

ZeroneLove
29分钟前
0
0
【scala】3.数组相关操作

简介 在本章中,我们将会学到如何在scala中操作数组。 1、定长数组 // 初始化长度为10的定长数组,每一个元素的值为0val nums = new Array[Int](10)// nums: Array[Int] = Array(0, 0, 0, ...

Areya
29分钟前
2
0
教你零基础如何快速入门大数据技巧

现在是大数据时代,很多人都想要学习大数据,因为不管是就业前景还是薪资都非常的不错,不少人纷纷从其他行业转型到大数据行业,那么零基础的人也想要学习大数据怎么办呢?下面一起探讨下零基...

董黎明
38分钟前
1
0
Nginx 配置 root目录、虚拟目录alias

Nginx是通过 alias 设置虚拟目录,在Nginx的配置中,alias目录和root目录是有区别的。 alias指定的目录是准确的,即location匹配访问的path目录下的文件直接是在alias目录下查找的; root指定...

Yue_Chen
55分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部