tomcat7 --servlet

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

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、付费专栏及课程。

余额充值