StandardWrapper容器

  Context容器包含一个或者多个Wrapper实例,每个Wrapper实例表示一个具体的servlet定义。

方法调用序列

  

  具体过程

  (1)连接器创建request和response对象 
  (2)连接器调用StandardContext实例的invoke()方法 
  (3)接着,StandardContext实例的invoke方法调用其管道对象的invoke方法。StandardContext中管道对象的基础阀是StandContextValve类的实例,因此,StandardContext的管道对象会调用StandardContextValve实例的invoke方法。 
  (4)StandardContextValve实例的invoke方法获取相应的Wrapper实例处理HTTP请求,调用Wrapper实例的invoke的方法。 
  (5)StandardWrapper类是Wrapper接口的标准实现,StandardWrapper实例的invoke方法会调用其管道对象的invoke方法。 
  (6)StandardWrapper流水线的基本阀门时StandardWrapperValve。因此StandardWrapperValve的invoke方法会被调用。StandardWrapperValve的invoke方法会调用包装器的allocate方法获得一个servlet的实例。 
  (7)当一个servlet需要被加载的时候,方法allocate调用方法load来加载一个servlet。 
  (8)方法load会调用servlet的init方法。 
  (9)StandardWrapperValve调用servlet实例的service方法。

1. StandardWrapper对象主要任务加载servlet类,并进行实例。但是StandardWrapper并不调用servlet的service方法。StandardWrapperValve对象通过调用allocate()方法从StandardWrapper实例中获取servlet实例。在获取得了servlet实例后,StandardWrapperValve实例就会调用servlet实例的service方法。 
粗略流程如下:

  分配Servlet实例 
  StandardWrapperValve实例的invoke方法调用Wrapper实例的allocate方法获取请求的servlet的一个实例,因此StandardWrapper类的实现allocate方法。 
StandardWrapperValve#invoke

 public final void invoke(Request request, Response response)
throws IOException, ServletException {
//...省略
// Allocate a servlet instance to process this request
try {
if (!unavailable) {
servlet = wrapper.allocate();
}
} catch (UnavailableException e) {
//...省略

StandardWrapper#allocate

 /**
* Stack containing the STM instances.
*/
protected Stack<Servlet> instancePool = null; 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) {
synchronized (this) {
if (instance == null) {
try {
if (log.isDebugEnabled())
log.debug("Allocating non-STM instance"); instance = loadServlet();//加载servlet实例
if (!singleThreadModel) {
// For non-STM, increment here to prevent a race
// condition with unload. Bug 43683, test case
// #3
newInstance = true;
//分配计数
countAllocated.incrementAndGet();
}
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException
(sm.getString("standardWrapper.allocate"), e);
}
}
}
} if (!instanceInitialized) {
initServlet(instance);//初始化servlet->servlet.init()
} if (singleThreadModel) {
if (newInstance) {
// Have to do this outside of the sync above to prevent a
// possible deadlock
synchronized (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 {
instancePool.push(loadServlet());//放入栈中
nInstances++;
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException
(sm.getString("standardWrapper.allocate"), e);
}
} else {
try {
instancePool.wait();
} catch (InterruptedException e) {
// Ignore
}
}
}
if (log.isTraceEnabled())
log.trace(" Returning allocated STM instance");
countAllocated.incrementAndGet();
return instancePool.pop();//弹出栈顶servlet } }

加载Servlet 
在StandardWrapper#loadServlet()方法,先检查是否已经加载过servlet

if (!singleThreadModel && (instance != null))
return instance;

StandardContext获取实例管理器,并通过实例管理器获取servlet实例

InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
servlet = (Servlet) instanceManager.newInstance(servletClass);

初始化servlet,并触发容器加载监听事件。

initServlet(servlet);
fireContainerEvent("load", this);

最后,返回servlet

return servlet;

2.ServletConfig类 
在servlet调用init方法,要传入ServletConfig参数,ServletConfig参数的来源,由于StandardWrapper不仅实现了Wrapper接口,还实现了javax.servlet.ServletConfig接口,在ServletConfig接口中的方法如下:

public interface ServletConfig {
public String getServletName();//返回servlet名字
public ServletContext getServletContext();//返回servlet容器
public String getInitParameter(String name);//返回初始化参数
public Enumeration<String> getInitParameterNames();
}

StandardWrapper类并不是将自身传递给servlet#init方法,它会在一个StandardWrapperFacade实例中包装自生,将其大部分的公共方法对servlet程序员隐藏。

/**
* The facade associated with this wrapper.
*/
protected StandardWrapperFacade facade = new StandardWrapperFacade(this);

在StandardWrapper中方法实现

public ServletContext getServletContext() {
if (parent == null)
return (null);
else if (!(parent instanceof Context))
return (null);
else
return (((Context) parent).getServletContext());
} protected HashMap<String, String> parameters = new HashMap<String, String>();
/**
* Return the name of this servlet.
*/
@Override
public String getServletName() {
return (getName());
}
public String getInitParameter(String name) {
return (findInitParameter(name));
}
/**
* Return the set of initialization parameter names defined for this
* servlet. If none are defined, an empty Enumeration is returned.
*/
@Override
public Enumeration<String> getInitParameterNames() {
try {
parametersLock.readLock().lock();
return Collections.enumeration(parameters.keySet());
} finally {
parametersLock.readLock().unlock();
}
}
//初始化参数
public void addInitParameter(String name, String value) {
try {
parametersLock.writeLock().lock();
parameters.put(name, value);
} finally {
parametersLock.writeLock().unlock();
}
fireContainerEvent("addInitParameter", name);
}

3.Servlet容器的父子关系。 
Wrapper实例代表一个servlet实例,因此Wrapper实例不在有子容器,不应该在调用其addChild方法,否则抛出IllegalStateException异常。下面是addChild方法。

public void addChild(Container child) {
throw new IllegalStateException
(sm.getString("standardWrapper.notChild"));
}

Wrapper的父容器只能是Context类的实现,若是在调用Wrapper实例的setParent方法时,传入了一个非Context类型的容器,则会抛出IllegalStateException异常。

public void setParent(Container container) {

        if ((container != null) &&
!(container instanceof Context))
throw new IllegalArgumentException
(sm.getString("standardWrapper.notContext"));
if (container instanceof StandardContext) {
swallowOutput = ((StandardContext)container).getSwallowOutput();
unloadDelay = ((StandardContext)container).getUnloadDelay();
}
super.setParent(container); }

4.StandardWrapperFacade类 
StandardWrapper实例会调用它所载入的servlet类的实例的init()方法。init()方法需要一个javax.servlet.ServletConfig实例,而StandardWrapper类本身实现了javax.servlet.ServletConfig接口,所以,理论上StandardWrapper对象可以将自己传入init()方法。但是StandardWrapper需要将其大部分公共方法对servlet程序员隐藏起来。为了实现在这个目的,StandWrapper类将自身包装成StandardWrapperFacade类的一个实例。关系如下。

(1)在StandardWrapper类中,将自身包装成StandardWrapperFacade。

/**
* The facade associated with this wrapper.
*/
protected StandardWrapperFacade facade = new StandardWrapperFacade(this);

(2)StandardWrapperFacade中的构造方法。

public StandardWrapperFacade(StandardWrapper config) {
super();
this.config = config;
}
//通过包装类型调用方法
public String getInitParameter(String name) {
return config.getInitParameter(name);
}
public ServletContext getServletContext() {
if (context == null) {
context = config.getServletContext();
if ((context != null) && (context instanceof ApplicationContext))
context = ((ApplicationContext) context).getFacade();
}
return (context);
}
public String getServletName() {
return config.getServletName();
}

这里采用装饰模式。 
5.StandardWrapperValve类 
StandardWrapperValve是StandardWrapper中的基础阀门,主要完成2个操作。 
(1)执行与该servlet实例关联的全部过滤器, 
(2)调用servlet实例的service方法。 
在StandardWrapperValve中invoke方法执行的步骤。 
(1)调用StandardWrapper实例的allocate方法获取该StandardWrapper实例所表示的servlet的实例; 
(2)调用私有方法createFilterChain,创建过滤器链; 
(3)调用过滤链的doFilter方法,其中包括调用servlet实例的service方法; 
(4)释放过滤链; 
(5)调用Wrapper实例的deallocate方法; 
(6)若该servlet类再也不会用到,将会调用Wrapper实例的unload方法。 
6.ApplicationFilterChain类 
ApplicationFilterChain类实现了FilterChain接口,StandardWrapperValve类的invoke方法会创建ApplicationFilterChain类的一个实例,并调用其doFilter方法,doFilter方法会调用第一个过滤器的doFilter方法。直到最后一个过滤器,则会调用被请求的servlet类的service方法。

总结:

  通过StandWrapper的学习,大致的了解了Servlet的从加载,初始化,service方法,到卸载的整个流程。另外还有就是单例模式,监听事件,装饰模式的设计思想的了解。

Tomcat学习笔记(十)的更多相关文章

  1. Tomcat学习笔记(十二)

    Host和Engine容器 Context容器的父容器通常是Host容器. Engine容器表示Catalina的整个servlet引擎.如果使用Engine容器,那么它总是处于容器层级的最顶层.默认 ...

  2. python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例

    python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...

  3. Learning ROS for Robotics Programming Second Edition学习笔记(十) indigo Gazebo rviz slam navigation

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 moveit是书的最后一章,由于对机械臂完全不知,看不懂 ...

  4. python3.4学习笔记(十八) pycharm 安装使用、注册码、显示行号和字体大小等常用设置

    python3.4学习笔记(十八) pycharm 安装使用.注册码.显示行号和字体大小等常用设置Download JetBrains Python IDE :: PyCharmhttp://www. ...

  5. python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法

    python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法 同一台机器同时安装 python2.7 和 python3.4不会冲突.安装在不同目录,然 ...

  6. python3.4学习笔记(十六) windows下面安装easy_install和pip教程

    python3.4学习笔记(十六) windows下面安装easy_install和pip教程 easy_install和pip都是用来下载安装Python一个公共资源库PyPI的相关资源包的 首先安 ...

  7. python3.4学习笔记(十五) 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)

    python3.4学习笔记(十五) 字符串操作(string替换.删除.截取.复制.连接.比较.查找.包含.大小写转换.分割等) python print 不换行(在后面加上,end=''),prin ...

  8. python3.4学习笔记(十二) python正则表达式的使用,使用pyspider匹配输出带.html结尾的URL

    python3.4学习笔记(十二) python正则表达式的使用,使用pyspider匹配输出带.html结尾的URL实战例子:使用pyspider匹配输出带.html结尾的URL:@config(a ...

  9. python3.4学习笔记(十) 常用操作符,条件分支和循环实例

    python3.4学习笔记(十) 常用操作符,条件分支和循环实例 #Pyhon常用操作符 c = d = 10 d /= 8 #3.x真正的除法 print(d) #1.25 c //= 8 #用两个 ...

随机推荐

  1. dubbo 与Spring Cloud 对比

    链接:https://www.zhihu.com/question/45413135/answer/242224410 近期也看到一些分享Spring Cloud的相关实施经验,这对于最近正在整理Sp ...

  2. 用 js 写一个获取随机颜色的程序

    function getColor(){ var color="#"; for(var i=0;i<6;i++){ color+=(Math.random()*16 | 0) ...

  3. 【js】【跨域问题】前后端分离的跨域问题

    最近在研究nodejs,php的前后端分离相关东西,在调用接口的时候碰到一些跨域的问题,经过一段时间的摸索,总结出来的一些东西 php采用的是yii框架,登录的机制或者调用接口都需要前端传递cooki ...

  4. PLC状态机编程第一篇-状态机介绍

    状态机的一般概念 过去我在学习PLC编程时,看的大部分书仅仅停留在软件的操作上,没有真正讲述如何组织程序,这里我们讲一个通用的描述控制算法的方法,就是状态机,和SFC很类似,其实SFC也是源于状态机啦 ...

  5. Linux基础知识与命令1(su passwd)

    一.Linux的基本原则 1.linux由一个个目的单一的小程序组成,我们一般需要组合小程序来完成复杂的任务 2.Linux的一切都是文件(文件类似于一棵树,包括外设,接口) 3.Linux尽量避免捕 ...

  6. C语言进阶——goto 和 void 的分析08

    遭人遗弃的goto: 高手潜规则:禁止使用goto 项目经验:程序质量与goto的出现次数成反比 最后的判决:将goto打入冷宫 程序示例1:(goto副作用分析) #include <stdi ...

  7. tar命令,vi编辑器

    一.将用户信息数据库文件和组信息数据库文件纵向合并为一个文件/1.txt(覆盖): [root@localhost /]# cat /etc/passwd /etc/group > 1.txt ...

  8. 初见spark-02(RDD及其简单算子)

    今天,我们来进入spark学习的第二章,发现有很多事都已经开始变化,生活没有简单的朝自己想去的方向,但是还是需要努力呀,不说鸡汤之类的话了, 开始我们今天的spark的旅程 一.RDD是什么 rdd的 ...

  9. TouTiao开源项目 分析笔记20 问答详情

    1.效果预览 1.1.效果预览,从问答列表开始 前面实现了从列表到内容. 这里主要讲解从内容到详情. 点击每一个回答内容,进入回答详情页面. 1.2.触发的点击事件 在WendaContentView ...

  10. 6.bootstrap 将文本内容关联一个动作(手机端导航适配)&在超小尺寸下显示,屏幕变大后消失

    1.情景:这是出现在手机端导航适配的,点击文本MENU可以出现下拉的list 解决方法: 1.首先要想到,MENU只有两个状态,因此可以用checkbox实现 2.将MENU放在label标签里面,l ...