Servlet工作原理解析(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangxiaowei333/article/details/79220646

一:Servlet简介

要介绍Servlet必须从Servlet容器开始说起,Servlet与Servlet容器的关系有点像枪跟子弹的关系,枪是为子弹而生的,而子弹又让枪有了杀伤力。

 以Tomcat容器为例,容器模型图大体是这样的:


从图中可以看出,Tomcate容器分为4个等级,真正管理Servlet的是Context容器,在我们实际开发中,一个web应用对应一

Context容器,一个Wrapper对应一个Servlet,在Tomcat配置文件中很容易可以发现这一点。


 二:Servlet容器的启动过程
 前面已经介绍一个web应用对应一个Context容器,即运行Servlet的Servlet容器。添加一个web应用会创建一个
StandardContext容器,并且给这个容器设置必要的参数,url与path分别代表这个web应用在Tomcat中的访问地址与实际路径,
这两个参数与Tomcat配置中的两个参数是一致的。其中最重要的一个配置是ContextConfig,这个类就会负责整个web配置的解析
工作,稍后会详细介绍这个类。最后将这个Context容器添加到父容器Host中。
 接下来将会调用Tomcat的start方法启动Tomcat。Tomcat的启动逻辑是基于观察者模式设计的,所有的容器都会继承LifeCycle
接口,它管理着各个容器的整个生命周期,每个容器发生修改或状态的改变都会由它去通知已经注册的观察者(Listener)。Tomcat
启动的时序图如下:



当context容器初始状态设置Init时,添加到context容器的listener将会被调用。ContextConfig继承了LifecycleListener接口,它是在调用Tomcat.addWebapp时被加入到StandardContext容器中的。ContextConfig类会负责整个WEB应用的配置文件的解析工作。

ContextConfig的init方法将会主要完成以下工作:

  • 创建用于解析XML配置文件的contextDigester对象
  • 读取默认的context.xml文件,如果存在则解析它
  • 读取默认的Host配置文件,如果存在则解析它
  • 读取默认的Context自身的配置文件,如果存在则解析它
  • 设置Context的DocBase

ContextConfig的init方法完成后,Context容器会执行startInternal方法,这个方法包括如下几个部分:

  • 创建读取资源文件的对象
  • 创建ClassLoader对象
  • 设置应用的工作目录
  • 启动相关的辅助类,如logger,realm,resources等
  • 修改启动状态,通知感兴趣的观察者
  • 子容器的初始化
  • 获取ServletContext并设置必要的参数
  • 初始化“load on startuo”的Servlet
三:Web应用的初始化工作
 WEB应用的初始化工作是在ContextConfig的configureStart方法中实现的,应用的初始化工作主要是解析web.xml文件,这个文件
是一个WEB应用的入口。
 Tomcat首先会找globalWebXml,这个文件的搜索路径是engine的工作目录下的org/apache/catalina/startup/NO-DEFAULT_
XML或conf/web.xml。接着会找hostWebXml,这个文件可能会在System.getProperty("catalina.base")/conf/${EngineName}
/${HostName}/web.xml.default中。接着寻找应用的配置文件examples/WEB-INF/web.xml,web.xml文件中的各个配置项将会
被解析成相应的属性保存在WebXml对象中。
 接下来会讲WebXml对象中的属性设置到context容器中,这里包括创建servlet对象,filter,listerner等,这些在WebXml的
configureContext方法中,下面这段代码可以清晰的看出ContextConfig是如何将Servlet包装成Context容器中的StandardContext的。
for (ServletDef servlet : servlets.values()) {   
            Wrapper wrapper = context.createWrapper();   
            String jspFile = servlet.getJspFile();   
            if (jspFile != null) {   
                wrapper.setJspFile(jspFile);   
            }   
            if (servlet.getLoadOnStartup() != null) {   
                wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());   
            }   
            if (servlet.getEnabled() != null) {   
                wrapper.setEnabled(servlet.getEnabled().booleanValue());   
            }   
            wrapper.setName(servlet.getServletName());   
            Map<String,String> params = servlet.getParameterMap();   
            for (Entry<String, String> entry : params.entrySet()) {   
                wrapper.addInitParameter(entry.getKey(), entry.getValue());   
            }   
            wrapper.setRunAs(servlet.getRunAs());   
            Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();   
            for (SecurityRoleRef roleRef : roleRefs) {   
                wrapper.addSecurityReference(   
                        roleRef.getName(), roleRef.getLink());   
            }   
            wrapper.setServletClass(servlet.getServletClass());   
            MultipartDef multipartdef = servlet.getMultipartDef();   
            if (multipartdef != null) {   
                if (multipartdef.getMaxFileSize() != null &&   
                        multipartdef.getMaxRequestSize()!= null &&   
                        multipartdef.getFileSizeThreshold() != null) {   
                    wrapper.setMultipartConfigElement(new   
 MultipartConfigElement(   
                            multipartdef.getLocation(),   
                            Long.parseLong(multipartdef.getMaxFileSize()),   
                            Long.parseLong(multipartdef.getMaxRequestSize()),   
                            Integer.parseInt(   
                                    multipartdef.getFileSizeThreshold())));   
                } else {   
                    wrapper.setMultipartConfigElement(new   
 MultipartConfigElement(   
                            multipartdef.getLocation()));   
                }   
            }   
            if (servlet.getAsyncSupported() != null) {   
                wrapper.setAsyncSupported(   
                        servlet.getAsyncSupported().booleanValue());   
            }   
            context.addChild(wrapper);   
 }  


 除了将Servlet包装成StandardWrapper并作为子容器添加到Context中外,其他所有的web.xml属性都被解析到Context中。所以说

Context是真正运行Servlet的Servlet容器。一个web应用对应一个Context容器,容器的配置属性由web.xml决定,这样web.xml的作用就

显而易见了。

 到目前为止已经完成了servlet的解析工作,并且被包装成了StandardWrapper添加到Context容器中,但是它仍然不能为我们

工作,因为它还没有被实例化。下一篇文章将介绍Servlet是如何被创建的以及如何被初始化的。



没有更多推荐了,返回首页