设为首页 收藏本站
查看: 308|回复: 0

[经验分享] 17,tomcat中StandardContext

[复制链接]

尚未签到

发表于 2017-1-27 06:31:43 | 显示全部楼层 |阅读模式
  一个上下文容器(Context)代表一个web应用,每一个上下文包括多个包装器(Wrapper),每个包装器代表一个Servlet
上下文还需要其它的一些组件如加载器和管理器
Context接口的标准实现,org.apache.catalina.core.StandardContext类
  StandardContext配置
创建一个StandardContext实例之后,必须调用它的start方法,这样它就能为受到的HTTP请求服务了。一个StandardContext对象可能启动失败,
这时候属性available被设置为false,属性available表示了StandardContext对象的可用性。
在一个Tomcat部署中,StandardContext的配置过程做了以下事情:准备读取和解析%CATALINA_HOME%/conf 目录下面的web.xml,
部署所有应用程序,确保StandardContext实例可以处理应用级别的web.xml。
另外,配置需要添加一个验证器阀门和证书阀门(authenticator valve and a certificate valve)
StandardContext的属性之一是它属性configured,用来表示该StandardContext是否已经配置了
StandardContext使用一个事件监听器来作为它的配置器
当StandardContext实例的start方法被调用的时候,首先触发一个生命周期事件
。该事件唤醒一个监听器来配置该StandardContext实例。配置成功后,该监听器将configured属性设置为true。
否则,StandardContext对象拒绝启动,这样就不能对HTTP请求进行服务了。
  StandardContext构造函数
public StandardContext() {
 super();
 pipeline.setBasic(new StandardContextValve());
 namingResources.setContainer(this);
}
在构造函数中,最重要的事情是在StandardContext的流水线上添加了一个类型为StandardContextValve的基本阀门
  
启动StandardContext
Start方法初始化StandardContext对象并让生命周期监听器配置该StandardContext实例。如果配置成功,
生命周期监听器会将configured属性设置为true。最后start方法,将available属性设置为true或者false。
如果是true的话表示该StandardContext属性配置完毕并且所有相关子容器和组件已经成功启动,
这样就能对HTTP请求进行服务了,如果是false则表示出现了错误。

public synchronized void start() throws LifecycleException {
if (started)
throw new LifecycleException (sm.getString("containerBase.alreadyStarted", logName()));
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
setAvailable(false);
setConfigured(false);
boolean ok = true;
if (getResources() == null) {
try {
if ((docBase != null) && (docBase.endsWith(".war")))
setResources(new WARDirContext());
else
setResources(new FileDirContext());
}catch (IllegalArgumentException e) {
ok = false;
}
}
if (ok && (resources instanceof ProxyDirContext)) {
DirContext dirContext = ((ProxyDirContext) resources).getDirContext();
if ((dirContext != null) && (dirContext instanceof BaseDirContext)) {
((BaseDirContext) dirContext).setDocBase(getBasePath());
((BaseDirContext) dirContext).allocate();
}
}
if (getLoader() == null) {
if (getPrivileged()) {
setLoader(new WebappLoader(this.getClass().getClassLoader()));
}else{
setLoader(new WebappLoader(getParentClassLoader()));
}
}
if (getManager() == null) {
setManager(new StandardManager());
}
// Initialize character set mapper
getCharsetMapper();
// Post work directory
postWorkDirectory();
String useNamingProperty = System.getProperty("catalina.useNaming");
if ((useNamingProperty != null) && (useNamingProperty.equals("false"))) {
useNaming = false;
}
if (ok && isUseNaming()) {
if (namingContextListener == null) {
namingContextListener = new NamingContextListener();
namingContextListener.setDebug(getDebug());
namingContextListener.setName(getNamingContextName()); addLifecycleListener(namingContextListener);
}
}
ClassLoader oldCCL = bindThread();
if (ok) {
try {
addDefaultMapper(this.mapperClass);
started = true;
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start();
if ((logger != null) && (logger instanceof Lifecycle))
((Lifecycle) logger).start();
// Unbinding thread
unbindThread(oldCCL);
oldCCL = bindThread();
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();
Mapper mappers[] = findMappers();
for (int i = 0; i < mappers.length; i++) {
if (mappers instanceof Lifecycle)
((Lifecycle) mappers).start();
}
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children instanceof Lifecycle)
((Lifecycle) children).start();
}
if (pipeline instanceof Lifecycle)
((Lifecycle) pipeline).start();
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(START_EVENT, null);
if ((manager != null) && (manager instanceof Lifecycle))
((Lifecycle) manager).start();
} finally {
// Unbinding thread
unbindThread(oldCCL);
}
}
if (!getConfigured())
ok = false;
if (ok)
getServletContext().setAttribute (Globals.RESOURCES_ATTR, getResources());
if (ok) {
postWelcomeFiles();
}
if (ok) {
if (!listenerStart())
ok = false;
}
if (ok) {
if (!filterStart())
ok = false;
}
// Load and initialize all "load on startup" servlets
if (ok)
loadOnStartup(findChildren());
unbindThread(oldCCL);
if (ok){
setAvailable(true);
}else{
stop();
setAvailable(false);
}
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}

  下面是该方法做的事情:
· 触发BEFORE_START事件
· 设置availability属性为false
· 设置configured属性为false
· 设置源(resources)
· 设置加载器
· 设置管理器
· 初始化属性map
· 启动跟该上下文相关的组件
· 启动子容器(包装器)
· 启动流水线
· 启动管理器
· 触发START事件
。监听器(ContextConfig)会进行一系列配置操作,配置成功后,将StandardContext实例的configured属性设置为true。
· 检查configured属性的值,如果为true:调用postWelcomPages方法,加载子包装器,并将available属性设置为true。
 如果configured属性为false调用stop方法
· 触发AFTER_START事件
  Invoke方法
StandardContext's方法由相关联的连接器调用,
如果该上下文是一个主机(host)的子容器,
有该主机的invoke方法调用。StandardContext的invoke方法首先检查是否正在重加载该应用程序,
是的话,等待知道加载完毕。然后调用它的父类ContainerBase的invoke方法

public void invoke(Request request, Response response){
while (getPaused()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) { ; }
}
if (swallowOutput) {
try {
SystemLogHandler.startCapture();
super.invoke(request, response);
}
}else {
super.invoke(request, response);
}
}
  方法getPaused获得属性paused的值,当应用程序正在加载的时候该属性为ture。
  
StandardContextMapper
对于每一个请求,invoke方法都会调用StandarContext流水线基本阀门的invoke方法。
StandarContext的基本阀门用org.apache.catalina.core.StandardContextValve类表示。
StandardContextValve实例查找包含它的StandardContext。StandardContextValve使用上下文容器的map来查找合适的包装器
StandardContext的父类ContainerBase定义了addDefaultMapper方法来添加

protected void addDefaultMapper(String mapperClass) {
if (mapperClass == null)
return;
if (mappers.size() >= 1)
return;
try {
Class clazz = Class.forName(mapperClass);
Mapper mapper = (Mapper) clazz.newInstance();
mapper.setProtocol("http");
addMapper(mapper);
} catch (Exception e) {}
}
  重加载支持
StandardContext定义了reloadable属性来标识是否支持应用程序的重加载。
当允许重加载的时候,当web.xml或者WEB-INF/classes目录下的文件被改变的时候会重加载。
StandardContext 中Loader接口的标准实现WebappLoader类,有一个单独线程来检查WEB-INF目录下面所有类和JAR文件的时间戳
你需要做的是启动该线程,将 WebappLoader关联到StandardContext,使用setContainer方法即可

public void setContainer(Container container) {
if ((this.container != null) && (this.container instanceof Context))
((Context) this.container).removePropertyChangeListener(this);
Container oldContainer = this.container;
this.container = container;
support.firePropertyChange("container", oldContainer, this.container);
// Register with the new Container (if any)
if ((this.container!=null) && (this.container instanceof Context)) {
setReloadable( ((Context) this.container).getReloadable() );
((Context) this.container).addPropertyChangeListener(this);
}
}
  
注意最后一个if语句块中,如果容器是一个上下文容器,调用setReloadable方法,
也就是说WebappLoader的reloadable属性跟StandardContext的reloadable属性相同。
下面是WebappLoader对setReload方法的实现:

public void setReloadable(boolean reloadable) {
boolean oldReloadable = this.reloadable;
this.reloadable = reloadable;
support.firePropertyChange("reloadable", new Boolean(oldReloadable), new Boolean(this.reloadable));
if (!started)
return;
if (!oldReloadable && this.reloadable)
threadStart();
else if (oldReloadable && !this.reloadable)
threadStop();
}
}
  如果将reloadable属性设置为true,调用threadStart方法。如果从true到false,则调用threadStop方法。
threadStart方法启动一个线程持续的检查WEB-INF目录下面的类文件和JAR文件的时间戳。threadStop方法用于停止该线程。
类的时间戳是由backgroundProcess方法调用
  backgroundProcess方法
一个上下文容器需要其它组件如加载器和管理器的支持。这些组件通常需要一个单独的线程来处理后台过程(background processing)
所有的后台过程都分享同一个线程。如果一个组件或者是容器需要定期的来执行操作,
它需要做的是将这些代码写入到backgroundProcess方法即可。

protected void threadStart() {
if (thread != null)
return;
if (backgroundProcessorDelay <= 0)
return;
threadDone = false;
String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
thread = new Thread(new ContainerBackgroundProcessor(), threadName);
thread.setDaemon(true);
thread.start();
}
  
方法threadStart传递一个ContainerBackgroundProcessor对象创建一个新线程。
ContainerBackgroundProcessor实现了java.lang.Runnable接口

protected class ContainerBackgroundProcessor implements Runnable {
public void run() {
while (!threadDone) {
try {
Thread.sleep(backgroundProcessorDelay * 1000L);
} catch (InterruptedException e) { ; }
if (!threadDone) {
Container parent = (Container) getMappingObject();
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (parent.getLoader() != null) {
cl = parent.getLoader().getClassLoader();
}
processChildren(parent, cl);
}
}
}
protected void processChildren(Container container, ClassLoader cl) {
try {
if (container.getLoader() != null) {
Thread.currentThread().setContextClassLoader (container.getLoader().getClassLoader());
}
container.backgroundProcess();
}catch (Throwable t) {}
finally {
Thread.currentThread().setContextClassLoader(cl);
}
Container[] children = container.findChildren();
for (int i = 0; i < children.length; i++) {
if (children.getBackgroundProcessorDelay() <= 0) {
processChildren(children, cl);
}
}
}
}
  ContainerBackgroundProcessor是ContainerBase的内部类,
在他的run方法里,有一个while循环定期的调用它的processChildren方法

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-333824-1-1.html 上篇帖子: tomcat 类加载机制 —— ClassLoader 下篇帖子: tomcat源码解析--ClassLoader
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表