tomcat7 --servlet

本文详细介绍了Tomcat7中Servlet的管理过程,从servlet规范到Tomcat的context容器,深入剖析了servlet的初始化、加载过程,包括web.xml的解析、Wrapper的创建以及Servlet的实例化。重点讲解了StandardContext、StandardWrapper以及WebappClassLoader的角色,揭示了类加载器的层次结构,最后通过实例展示了servlet如何协同Spring MVC进行初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

servlet 是一套规范, javax.servlet 包中提供了接口 或帮助类,具体的实现由servlet 容器提供商实现。

tomcat 包含的servlet 容器为 context 容器,context 容器初始化时会从 web.xml里读取servlet 元素及其他servlet相关配置。

tomcat 启动时将 servlet配置 包装成 wapper,wapper由 context管理。

一个srevlet 包装成一个wapper ,一个web 应用 WAR 包对应一个 context。


CONTEXT  --servlet容器


context  实例创建时,默认的是standardcontext,会绑定一个监听者ContextConfig

context 启动调用 startInternal() 时,会触发一个启动事件,ContextConfig 监听到该事件进行配置初始化。

                // Notify our interested LifecycleListeners
                fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

CONFIGURE_START_EVENT事件ContextConfig做的事情如下:

调用 webConfig() 方法解析 context 级别的web.xml。

standardContext 触发事件时把自己传给了 ContextConfig 监听者,ContextConfig 在解析后将设置  standardcontext 的相关属性

1.  解析 webapp应用里的 web.xml, 调 WebXmlParser.parseWebXml() 方法,  解析生成配置的对象实例。

2.  ContextConfig 接着调用 configureContext() 方法,顾名思义就是设置context ,设置 standardcontext 的相关属性 。

     将上一步解析的对象包装成wapper,wapper 其实就是 servlet 的 tomcat 版的实现,Wrapper 作为 Context 的子容器。

---2.1  ContextConfig  会调用传过来的 standardcontext 容器的 createWrapper() 方法,为每一个解析过的 servlet 封装成 standardWapper 对象。

---2.2 然后将这个standardWapper 对象添加到 standardcontext  容器,这里就可以发现 standardContext 确实是 servlet 的管理容器。

---2.3 其他的配置如 servlet 的url-mapping 也会保存到 standardContext 容器。


这里 那么Context 触发的 ContextConfig 监听者的动作就完成了,接下来Context 继续启动,启动子容器,也就是Wrapper 容器。

Wrapper 容器负责具体的servlet 的实例化和初始化。

下面看看具体的servlet 初始化动作:


---------关于 servlet 体系------------------------

说到 servlet 容器 standardcontext , 是和 servlet 体系不可分的。

servlet 体系的核心是 Servlet 接口 ,位于 javax.servlet 包。

servlet规范中有这么一句话:

对于未托管在分布式环境中(默认)的servlet而言,servlet容器对于每一个Servlet声明必须且只能产生一个实例;

servlet 可以实现 SingleThreadModel 接口,这样容器就会产生多个servlet 实例

但是这个接口并不保证其他会话属性的线程安全性。该接口的说明如下:

 * Note that SingleThreadModel does not solve all thread safety issues. For
 * example, session attributes and static variables can still be accessed by
 * multiple requests on multiple threads at the same time, even when
 * SingleThreadModel servlets are used. It is recommended that a developer take
 * other means to resolve those issues instead of implementing this interface,
 * such as avoiding the usage of an instance variable or synchronizing the block
 * of the code accessing those resources. This interface is deprecated in
 * Servlet API version 2.4.


(那么开发者必须自己保证线程的安全性。Spring MVC 的 servletDispatcher 的前端控制器模式可以研究一下。)

通常是实现 HttpServlet 这个抽象类。

servlet 作为一个web 组件,是有生命周期的。

srevlet接口的生命周期分为 :

1. init()

实际调用的是 GenericServlet.init(ServletConfig config) 方法。

这里 ServletConfig 也是servlet 体系的一个接口。获取web.xml里解析后的servlet相关配置。

该方法由容器初始化servlet实例时调用:

也就是上文提到的wapper 进行初始化servlet 实例时调用,实际是standardWrapper 调用的。

最后一行的servlet.init(facade) 方法就是初始化调用。


standardWrapper传递给init() 的对象其实是 :

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

(这里就又用到了facade模式,先带过。)

这个StandardWrapperFacade  其实就是 servletConfig 接口的实现,封装了会话接口专门给 standardWrapper 初始化servlet时调用。

那么 StandardWrapperFacade   的配置对象this其实就是 standardWrapper ,因为standardWrapper 包装了 servlet的解析配置。

那么 StandardWrapper 必然实现了 ServletConfig 接口


由上图可以发现, standardContext 启动过程中当web.xml的配置读取封装完成之后,包括之前提到的servlet 的配置读取和封装实例化wrapper 之后,就会loadOnstartup 开始实例化和初始化servlet了。具体的实例化和初始化就由 StandardWrapper 自己完成。

 实例化servlet代码如下:



这里使用了standardContext 容器绑定的类加载器加载;具体的是


这个classLoader 是初始化的时候由standardContext 传递的;


就是这个WebappLoader,这个loader是tomcat 自己定义的。实现了Loader接口,这个是tomcat 自定义的一个接口,这个接口提供了这个方法


这个WebappLoader 持有一个 classLoader 实例 ,这个实例就是 WebappClassLoader的实例。


因此,我们知道了Servlet是由 Tomcat 自定义的 WebappClassLoader 加载的。

那么 WebappClassLoader 的父类加载器是什么?,下面是webappClassLoader实例化的代码


使用反射来实例化,反射时调用了指定父类加载器的构造方法,这个父类加载器就是容器绑定的父类加载器。如果该容器没有,则寻找父容器绑定的类加载器,

最终找到了Engine容器的 父类加载器。


最终发现是这个类加载器:


是Catalina 类的加载器,下面是Bootstrap 启动类 加载Catalina的代码

发现是自定义的catalinaLoader 加载的


这个catalinaLoader是怎么回事呢?


默认情况下,commomLoader 定义的classPath 如下,加载Tomcat 下的lib文件

common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar

现在整理一下:

   systemClassLoader (appClassLoader)

             |

commonLoader(加载Tomcat 的lib) 

             |

serverLoader,catalinaLoader(默认情况下未指定加载类路径,就是commonLoader)

             |

各容器的classLoader (同上)

             |

WebappClassLoader (加载WAR包)


那么Engine 的父类加载器就是 commonLoader 了,容器是Tomcat 利用Digester 组件实例化的,

Digester实例化容器的classLoader是什么呢?catalina  设置了Digester 使用线程上下文的ClassLoader,而这个线程上下文的classLoader是在Bootstrap里设置的,就是

catalinaLoader,至此,真相大白,加载容器的classLoader也是CatalinaLoader,也就是 commonLoader。

所以,默认情况下commonLoader加载 Tomcat 下的Lib,和 容器组件。

WebappClassLoader 加载用户程序代码 ,也包括 web-inf/lib 。

因为 WebappClassLoader 的父类加载器是commonLoader,

Bootstrap 类创建 commonLoader的方法是  createClassLoader()没有父类加载器,那么是默认的父类加载器,调用这个工厂类

ClassLoaderFactory.createClassLoader(repositories, parent); 其中parent =null


当parent 为null时, new URLClassLoader(),默认指定systemClassLoader作为其父类加载器。也就是加载启动Tomcat时 在 setClassPath.bat 脚本里指定的 JAVA_OPTIONS  -classPath="..." 。这些类和JAR的appClassLoader。



回到之前的servlet 的实例化问题上来,servlet 实例化是由webappLoader 持有的WebappClassLoader 加载和实例化的。


看看实际项目中的例子:

web.xml  中,spring MVC的配置如下:

<servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
	    	/WEB-INF/web-context.xml
	    </param-value>
        </init-param>
	<init-param>
		<param-name>publishContext</param-name>
		<param-value>true</param-value>
	</init-param>
        <load-on-startup>1</load-on-startup>
</servlet>

这里 DispatcherServlet 初始化的时候,也就是之前提到的servlet的 init() 方法,在它的父类 FrameServlet 中默认创建一个IOC容器 ,XmlWebApplicationContext,这个容器由 DispatcherServlet 持有。


这个容器创建之后,调用 refresh() 初始化,由contextConfigLocation 指定bean 定义文件。

XmlWebApplicationContext 实现的 loadBeanDefinitions 方法如下:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
     throws BeansException, IOException
   {
     XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
 
     beanDefinitionReader.setEnvironment(getEnvironment());
     beanDefinitionReader.setResourceLoader(this);
     beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
 
     initBeanDefinitionReader(beanDefinitionReader);
     loadBeanDefinitions(beanDefinitionReader);
   }
默认从classPath读取xml资源:classLoader.getResourceAsStream(this.path);   或者 Class.getResourceAsStream(this.path);

第一步是创建解析过bean 定义的 beanFactory,调用 XmlbeanDefinitionReader 解析:


第二步然后根据解析过的bean定义 创建bean实例:


直接调用beanFacotory 的 getBean () 方法创建一个单例bean。

 具体见 DefaultSingletonBeanRegistry

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值