`
韩悠悠
  • 浏览: 842473 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

16,tomcat中StandardWrapper实现

 
阅读更多

Wrapper接口在Catalina中的标准实现StandardWrapper类的详细实现过程。
一共有四种容器:engine(引擎),host(主机),context(上下文)和wrapper(包装器)
一个上下文一般包括一个或者多个包装器,每一个包装器表示一个servlet
方法调用序列Sequence of Methods Invocation
对于每一个连接,连接器都会调用关联容器的invoke方法。接下来容器调用它的所有子容器的invoke方法
如果一个连接器跟一个StadardContext实例相关联,那么连接器会调用StandardContext实例的invoke方法,
该方法会调用所有它的子容器的invoke方法

 


 

过程
1,连接器创建请求和响应对象
2,连接器调用StandardContext的invoke方法
3,StandardContext的invoke方法必须调用该上下文容器的流水线的invoke方法,
所以StandardContext的流水线会调用StandardContextValve的invoke方法
4,StandardContextValve的invoke方法得到合适的包装器来对请求进行服务并调用包装器的invoke方法
5,StandardWrapper是包装器的标准实现,StandardWrapper对象的invoke方法调用流水线的invoke方法。
6,StandardWrapper流水线的基本阀门时StandardWrapperValve。因此StandardWrapperValve的invoke方法会被调用。
StandardWrapperValve的invoke方法会调用包装器的allocate方法获得一个servlet的实例。
7,当一个servlet需要被加载的时候,方法allocate调用方法load来加载一个servlet
8,方法load会调用servlet的init方法

 

我们主要关注的是一个servlet被调用的时候发生的细节。因此我们需要自习看StandardWrapper和StandarWrapperValve类

javax.servlet.SingleThreadModel
一个servlet可以实现javax.servlet.SingleThreadModel接口,实现此接口的一个servlet通俗称为SingleThreadModel(STM)的程序组件。
根据Servlet规范,实现此接口的目的是保证servlet一次只能有一个请求。
StandardWrapper
一个StandardWrapper对象的主要职责是:加载它表示的servlet并分配它的一个实例。该StandardWrapper不会调用servlet的service方法
这个任务留给StandardWrapperValve对象,在StandardWrapper实例的基本阀门管道。
StandardWrapperValve对象通过调用StandardWrapper的allocate方法获得Servlet实例。
在获得Servlet实例之后的StandardWrapperValve调用servlet的service方法

在servlet第一次被请求的时候,StandardWrapper加载servlet类。它是动态的加载servlet,所以需要知道servlet类的完全限定名称。
通过StandardWrapper类的setServletClass方法将servlet的类名传递给StandardWrapper。
必须考虑一个servlet是否实现了SingleThreadModel接口。 如果一个servlet没有实现SingleThreadModel接口,
StandardWrapper加载该servlet一次,对于以后的请求返回相同的实例即可。

对于一个STM servlet,情况就有所不同了。StandardWrapper必须保证不能同时有两个线程提交STM servlet的service方法。

Servlet instance = <get an instance of the servlet>;
if ((servlet implementing SingleThreadModel>) {
	synchronized (instance) {
		instance.service(request, response);
	}
}else{
	instance.service(request, response);
}

 

Allocating the Servlet
StandardWrapperValve的invoke方法调用了包装器的allocate方法来获得一个请求servlet的实例
因此StandardWrapper类必须实现该接口
public javax.servlet.Servlet allocate() throws ServletException;
由于要支持STM servlet,这使得该方法更复杂了一点。实际上,该方法有两部分组成,
一部分负责非STM servlet的工作,另一部分负责STM servlet。

第一部分的结构如下
if (!singleThreadModel) {
 // returns a non-STM servlet instance
}
布尔变量singleThreadModel负责标志一个servlet是否是STM servlet。
它的初始值是false,loadServlet方法会检测加载的servlet是否是STM的,如果是则将它的值该为true
第二部分处理singleThreadModel为true的情况
synchronized (instancepool) {
 // returns an instance of the servlet from the pool
}
对于非STM servlet,StandardWrapper定义一个java.servlet.Servlet类型的实例
private Servlet instance = null;
方法allocate检查该实例是否为null,如果是调用loadServlet方法来加载servlet。然后增加contAllocated整型并返回该实例。

 

if (!singleThreadModel) {
	if (instance == null) {
	synchronized (this) {
		if (instance == null) {
			try { 
				instance = loadServlet();
			}catch (ServletException e) { 
				throw e;
			}
		}
	}
}
if (!singleThreadModel) {
	countAllocated++;
	return (instance);
}

 

 

Loading the Servlet
StandardWrapper实现了Wrapper接口的load方法,load方法调用loadServlet方法来加载一个servlet类,
并调用该servlet的init方法,传递一个javax.servlet.ServletConfig实例
方法loadServlet首先检查StandardWrapper是否表示一个STM servlet。
如果不是并且该实例不是null(即以前已经加载过),直接返回该实例:
if (!singleThreadModel && (instance != null))
 return instance;
如果该实例是null或者是一个STM servlet,继续该方法的其它部分

 

ServletConfig对象
StandardWrapper的loadServlet方法在加载了loaded方法之后调用的发送者的init方法
init方法传递一个javax.servlet.ServletConfig实例
StandardWrapper实现javax.servlet.ServletConfig接口和Wrapper接口。
ServletConfig接口有以下四个方法getServletContext, getServletName, getInitParameter, 和getInitParameterNames

getServletContext
public ServletContext getServletContext()
一个StandardWrapper实例必须是一个StandardContext容器的子容器。也就是说,StandardWrapper的父容器是StandardContext
StandardWrapper中方法getServletContext的实现

public ServletContext getServletContext() {
	if (parent == null)
		return (null);
	else if (!(parent instanceof Context))
		return (null);
	else 
		return (((Context) parent).getServletContext());
}

 现在你知道不能单独部署一个包装器来表示一个Servlet,包装器必须从属于一个上下文容器,
这样才能使用ServletConfig对象使用getServletContext方法获得一个ServletContext实例。

getServletName
该方法返回Servlet的名字
public java.lang.String getServletName()
getServletName方法在StandardWrapper类中实现
public String getServletName() {
 return (getName());
}
它简单的调用StandardWrapper 的父类ContainerBase类的getName方法
在ContainerBase中如下实现
public String getName() {
 return (name);
}
使用setName方法来设置name的值。回忆是如何调用StandardWrapper实例的setName方法来传递Servlet的name的

getInitParameter
该方法返回指定参数的值
public java.lang.String getInitParameter(java.lang.String name)
在StandardWrapper中,初始化参数被存放在一个名为parameters的HashMap中

private HashMap parameters = new HashMap();
StandardWrapper类的addInitParameter方法来填充parameters。传递参数的名字和值。
public void addInitParameter(String name, String value) {
	synchronized (parameters) {
		parameters.put(name, value); 
	} 
	fireContainerEvent("addInitParameter", name); 
}
StandardWrapper对getInitParameter的实现: 
public String getInitParameter(String name) { 
	return (findInitParameter(name));
}

 

getInitParameterNames
该方法返回所有初始化参数名字的枚举(Enumeration),
public java.util.Enumeration getInitParameterNames()
StandardWrapper类中getInitParameterNames的实现:
public Enumeration getInitParameterNames() {
 synchronized (parameters) {
  return (new Enumerator(parameters.keyset()));
 }
}

 

 

Parent and Children
一个包装器表示一个独立Servlet的容器。这样,包装器就不能再有子容器,因此不可以调用它的addChild方法,
如果调用了会得到一个java.langIllegalStateException
一个包装器的父容器只能是一个上下文容器。如果传递的参数不是一个上下文容器,
它的setParent方法会抛出java.lang.IllegalArgumentException

public void setParent(Container container) { 
	if ((container != null) && !(container instanceof Context)) 
		throw new IllegalArgumentException (
			sm.getString("standardWrapper.notContext")); 
	super.setParent(container); 
}

 

StandardWrapperFacade
StandardWrapper调用它价值的Servlet的init方法。该方法需要一个javax.servlet.ServletConfig的参数,
而StandardWrapper类自己就实现了ServletConfig接口。所以,理论上StandardWrapper可以将它自己作为参数传递给init方法。
但是StandardWrapper需要对Servlet隐藏他的大多数public方法。
为了实现这一点,StandardWraper将它自己包装的一个StandardWrapperFacade实例中



 

 

StandardWrapperValve
StandardWrapperValve是StandardWrapper实例上的基本阀门,主要工作是
1,提交Servlet的所有相关过滤器
2,调用发送者的service方法
要实现这些内容,下面是StandardWrapperValve在他的invoke方法要实现的
· 调用StandardWrapper的allocate的方法来获得一个servlet实例
· 调用它的private createFilterChain方法获得过滤链
· 调用过滤器链的doFilter方法。包括调用servlet的service方法
· 释放过滤器链
· 调用包装器的deallocate方法
· 如果Servlet无法使用了,调用包装器的unload方法
接下来是invoke方法

try { 
	if (!unavailable) { 
		servlet = wrapper.allocate(); 
	}
}
.........
try { 
	response.sendAcknowledgement(); 
}
........
ApplicationFilterChain filterChain = createFilterChain(request, servlet);
try { 
	String jspFile = wrapper.getJspFile();
	if (jspFile != null)
		sreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
	else
		sreq.removeAttribute(Globals.JSP_FILE_ATTR);
	if ((servlet != null) && (filterChain != null)) {
		filterChain.doFilter(sreq, sres);
	}
	sreq.removeAttribute(Globals.JSP_FILE_ATTR);
	....
}
...........
try { 
	if (filterChain != null) 
		filterChain.release(); 
}
...
try { 
	if (servlet != null) { 
		wrapper.deallocate(servlet);
	}
}
........
try { 
	if ((servlet != null) && (wrapper.getAvailable() == Long.MAX_VALUE)) { 
		wrapper.unload(); 
	}
}

 最重要的方法是createFilterChain方法并调用过滤器链的doFilter方法。
方法createFilterChain创建了一个ApplicationFilterChain实例,并将所有的过滤器添加到上面

FilterDef
org.apache.catalina.deploy.FilterDef表示一个过滤器定义,就像是在部署文件中定义一个过滤器元素那样

 

public final class FilterDef { 
	/** * The description of this filter. */ 
	private String description = null;
	private String displayName = null;
	private String filterClass = null;
	private String filterName = null;
	private String largeIcon = null;
	private String smallIcon = null;
	private Map parameters = new HashMap();

	public String toString() { 
		StringBuffer sb = new StringBuffer("FilterDef["); 
		sb.append("filterName=");
		sb.append(this.filterName); 
		sb.append(", filterClass=");
		sb.append(this.filterClass); 
		sb.append("]"); 
		return (sb.toString());
	}

	public void addInitParameter(String name, String value) {
		parameters.put(name, value);
	}
}

 

FilterDef类中的每一个属性都代表一个可以在过滤器中出现的子元素。该类包括一个Map类型的变量表示一个包含所有初始参数的Map。
方法addInitParameer添加一个name/value对到该Map。


ApplicationFilterConfig
ApplicationFilterConfig实现了javax.servlet.FilterConfig接口。
ApplicationFilterConfig负责管理web应用程序启动的时候创建的过滤器实例。
传递一个org.apache.catalina.Context对象和FilterDef对象给ApplicationFilterConfig的构造来创建一个ApplicationFilterConfig实例:
public ApplicationFilterConfig(Context context, FilterDef filterDef)
 throws ClassCastException, ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException
Context对象表示一个一个web应用而FilterDef表示一个过滤器定义。

Filter getFilter() 
throws ClassCastException, ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException {
	if (this.filter != null) 
		return (this.filter);
	String filterClass = filterDef.getFilterClass();
	ClassLoader classLoader = null;
	if (filterClass.startsWith("org.apache.catalina.")) 
		classLoader = this.getClass().getClassLoader();
	else 
		classLoader = context.getLoader().getClassLoader();
	
	ClassLoader oldCtxClassLoader = Thread.currentthread().getContextClassLoader();

	Class clazz = classLoader.loadClass(filterClass);
	this.filter = (Filter) clazz.newInstance(); 
	filter.init(this); 
	return (this.filter);
}

 

 

ApplicationFilterChain
ApplicationFilterChain类是实现了javax.servlet.FilterChain接口。
StandardWrapperValve类中的invoke方法创建一个该类的实例并且调用它的doFilter方法。
ApplicationFilterChain类的doFilter的调用该链中第一个过滤器的doFilter方法。
Filter接口中doFilter方法的签名如下:
public void doFilter(ServletRaquest request, ServletResponse response, FilterChain chain)
在他的doFilter方法中,一个过滤器可以调用另一个过滤器链的doFilter来唤醒另一个过来出去

 

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
	// do something here 
	... 
	chain.doFilter(request, response); 
}

 

在doFilter方法最好一行,它调用过滤链的doFilter方法。如果该过滤器是过滤链的最后一个过滤器,
它叫调用请求的Servlet的service方法。如果过滤器没有调用chain.doFilter,下一个过滤器就不会被调用。

  • 大小: 32 KB
  • 大小: 23.9 KB
分享到:
评论

相关推荐

    WEB服务器工作机制由浅至深(5):【How Tomcat Works】第11章StandardWrapper翻译分析

    **WEB服务器工作机制由浅至深:Tomcat StandardWrapper翻译分析** 在深入理解Web服务器的工作机制时,Tomcat作为Apache组织的开源Java Servlet容器,扮演着关键角色。本篇将聚焦于Tomcat的内部组件——...

    Spring Boot启动过程(六)之内嵌Tomcat中StandardHost、StandardContext和StandardWrapper的启动教程详解

    StandardContext是Tomcat中的上下文实现, StandardWrapper是Tomcat中的Wrapper实现。 在StandardHost的启动过程中,首先会初始化startStopExecutor线程池,然后启动所有的子容器,包括StandardContext和...

    Tomcat源代码学习研究

    - **工厂模式**:Tomcat使用工厂模式创建Servlet实例,如StandardWrapper类中的createServlet方法。 - **责任链模式**:请求在不同级别的Container之间传递,每个Container都有机会处理请求,直到找到合适的...

    apache-tomcat-9.0.1源码

    Tomcat源码中实现了Servlet的生命周期管理,包括加载、初始化、服务、销毁等阶段。`org.apache.catalina.core.StandardWrapper`类负责管理单个Servlet实例的生命周期。 3. **请求处理**: `org.apache.coyote`包...

    Tomcat8.0底层源码

    通过对Tomcat 8.0源码的学习,开发者能够更深入地理解Web服务器的工作机制,从而更好地优化应用性能,解决复杂问题,甚至参与到Tomcat的贡献和维护中。这是一条通往Java Web高级开发者之路的必经之路。

    Tomcat6.0.35源码JAVA应用.zip

    在`org.apache.catalina.core.StandardWrapper`类中,可以看到这些过程的详细实现。 5. **安全性与权限控制** Tomcat提供了基于角色的访问控制(RBAC),以及SSL/TLS支持来增强安全性。`conf/tomcat-users.xml`...

    tomcat 7.0

    8. **内存泄漏防护**:Tomcat 7包含了一些防止内存泄漏的机制,比如在`Context`销毁时清理静态变量,以及在`StandardWrapper`中跟踪未关闭的`ServletOutputStream`和`ServletInputStream`。 9. **更好的日志系统**...

    tomcat-catalina-7.0.27.jar.zip

    Catalina是Tomcat的核心组件,负责处理Servlet和JSP的请求,而`tomcat-catalina-7.0.27.jar`则是Catalina模块在Tomcat 7.0.27版本中的实现文件。 1. **Catalina架构** Catalina遵循Java Servlet规范,提供了一个...

    《深入剖析Tomcat(中文版+英文版)》.rar

    《深入剖析Tomcat》从最基本的HTTP请求开始,直至使用JMX技术管理Tomcat中的应用程序,逐一剖析Tomcat的基本功能模块,并配以示例代码,使读者可以逐步实现自己的Web服务器。 目录 第1章 一个简单的web服务器 1.1...

    Tomcat 8源代码 Servlet源代码

    【标题】"Tomcat 8源代码 Servlet源代码" 涉及到的是Apache Tomcat服务器的源码分析,特别是Servlet容器的相关实现。Tomcat是一个开源的轻量级Web应用服务器,广泛用于部署Java Servlet和JavaServer Pages (JSP)应用...

    apache-tomcat-7.0.99-src.zip

    5. **部署与加载**:`StandardContext`和`StandardWrapper`是容器的实现,它们负责加载和管理Web应用及其Servlet。`WEB-INF/web.xml`是应用的部署描述符,定义了应用的结构和行为。 6. **安全性**:Tomcat的安全性...

    tomcat-servlet源码

    在Tomcat中,`org.apache.catalina.core.StandardWrapper`类扮演了ServletConfig的角色,存储了Servlet的配置信息。 4. **Request与Response处理** 当请求到达Tomcat时,会通过`org.apache.coyote.Request`对象...

    Tomcat请求处理UML序列图

    通过对Tomcat请求处理流程中各个组件的解析,我们可以更深入地理解Tomcat的工作原理。这种基于UML序列图的描述方式不仅能够清晰地展现各个组件之间的交互关系,还能够帮助开发者更好地理解Tomcat的架构设计思想。这...

    tomcat原理

    Tomcat6是Tomcat系列中的一个稳定版本,广泛应用于各种规模的应用开发中。 ##### 架构组成: 1. **Connector**:负责接收客户端请求并将它们分发到相应的处理组件。 2. **Container**:主要负责管理应用程序生命...

    tomcat开发技术

    - **StandardWrapper**: 实现了Wrapper接口,用于管理Servlet实例。 通过阅读这些类的实现,可以更深入地理解Tomcat的工作机制和技术细节。 #### 六、总结 本文介绍了Tomcat的基本概念、工作原理、架构设计、容器...

    Tomcat 10.0.12的源码

    `StandardWrapper`实现了`Wrapper`接口,每个Servlet实例都有一个对应的`Wrapper`对象,用于管理Servlet的生命周期。 **5. 安全性** Tomcat的安全性体现在多个层面,如`Realm`接口及其实现,如`MemoryRealm`和`...

    tomcat7.070 源码及转成eclipse

    7. **线程模型**:Tomcat采用多线程模型处理请求,`Executor`接口及其实现允许自定义线程池,以优化并发性能。 8. **连接器与协议处理器**:Tomcat支持多种连接器,如基于NIO的` CoyoteNioProtocol`,基于APR的`...

    Tomcat7 核心包 catalina包源码

    在Tomcat7版本中,Catalina是核心组件之一,负责处理HTTP请求和响应,实现了Servlet和JSP规范。本篇文章将对Catalina包的源码进行深度剖析,帮助读者理解其内部工作机制。 首先,我们来看Catalina的核心架构。...

    tomcat源码

    Apache Tomcat是一款开源的Java Servlet容器,它实现了Java EE中的Web应用服务器规范,特别是Servlet和JSP标准。源代码分析是深入理解Tomcat工作原理、性能优化和自定义配置的关键。下面将对Tomcat源码进行详细的...

Global site tag (gtag.js) - Google Analytics