`
haitaoandroid
  • 浏览: 27505 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Tomcat源码分析(四)--容器处理链接之责任链模式

 
阅读更多

目标:在这篇文章希望搞明白connector.getContainer().invoke(request,response);调用容器的invoke后是怎么传递到 servlet或者jsp的?

由上篇文章Tomcat源码分析(三)--连接器是如何与容器关联的?可知,connector.getContainer()得到的容器应该是StandardEngine(其实应该是由server.xml文件配置得到的,这里先假定是StandardEngine),StandardEngine没有invoke方法,它继承与ContainerBase(事实上所有的容器都继承于ContainerBase,在ContainerBase类有一些容器的公用方法和属性),抽象类ContainerBase的invoke方法如下:

 protected Pipeline pipeline = new StandardPipeline(this);//标准管道的实现StandardPipeline
 public void invoke(Request request, Response response)
        throws IOException, ServletException {
        pipeline.invoke(request, response);//调用管道里的invoke
    }


由代码可知ContainerBase的invoke方法是传递到Pipeline,调用了Pipeline的invoke方法。这里要说一下Pipeline这个类,这是一个管道类,每一个管道类Pipeline包含数个阀类,阀类是实现了Valve接口的类,Valve接口声明了invoke方法。管道和阀的概念跟servlet编程里面的过滤器机制非常像,管道就像过滤器链,阀就好比是过滤器。不过管道中还有一个基础阀的概念,所谓基础阀就是在管道中当管道把所有的普通阀都调用完成后再调用的。不管是普通阀还是基础阀,都实现了Value接口,也都继承于抽象类ValveBase。在tomcat中,当调用了管道的invoke方法,管道则会顺序调用它里面的阀的invoke方法。先看看管道StandardPipeline的invoke方法:

 

public void invoke(Request request, Response response)
        throws IOException, ServletException {
        // Invoke the first Valve in this pipeline for this request
        (new StandardPipelineValveContext()).invokeNext(request, response);
    }


其中StandardPipelineValveContext是管道里的一个内部类,内部类的作用是帮助管道顺序调用阀Value的invoke方法,下面看它的定义代码:

 

   protected class StandardPipelineValveContext
        implements ValveContext {
        protected int stage = 0;
        public String getInfo() {
            return info;
        }
        public void invokeNext(Request request, Response response)
            throws IOException, ServletException {
            int subscript = stage;//阀的访问变量
            stage = stage + 1;//当前访问到第几个阀
            // Invoke the requested Valve for the current request thread
            if (subscript < valves.length) {
                valves[subscript].invoke(request, response, this);//管道的阀数组
            } else if ((subscript == valves.length) && (basic != null)) {
                basic.invoke(request, response, this);//当基础阀调用完成后,调用管道的基础阀的invoke阀
            } else {
                throw new ServletException
                    (sm.getString("standardPipeline.noValve"));
            }
        }
    }

内部类StandardPipelineValveContext的invokeNext方法通过使用局部变量来访问下一个管道数组,管道类的变量stage保存当前访问到第几个阀,valves保存管道的所有阀,在调用普通阀的invoke方法是,会把内部类StandardPipelineValveContext本身传进去,这样在普通阀中就能调用invokeNext方法以便访问下一个阀的invoke方法,下面看一个普通阀的invoke方法:

 

  public void invoke(Request request, Response response, ValveContext valveContext)
    throws IOException, ServletException {
    // Pass this request on to the next valve in our pipeline
    valveContext.invokeNext(request, response);//使用调用下一个阀的invoke方法
    System.out.println("Client IP Logger Valve");
    ServletRequest sreq = request.getRequest();
    System.out.println(sreq.getRemoteAddr());
    System.out.println("------------------------------------");
  }

这个阀的invoke方法,通过传进来到StandardPipelineValveContext(实现了ValveContext接口)的invokeNext方法来实现调用下一个阀的invoke方法。然后简单的打印了请求的ip地址。

 

最后再看StandardPipelineValveContext的invokeNext方法,调用完普通阀数组valves的阀后,开始调用基础阀basic的invoke方法,这里先说基础阀的初始化,在每一个容器的构造函数类就已经初始化了基础阀,看容器StandardEngine的构造函数:

 public StandardEngine() {
        super();
        pipeline.setBasic(new StandardEngineValve());//容器StandardEngine的基础阀StandardEngineValve
    }

即在容器构造的时候就已经把基础阀添加进管道pipeline中,这样在StandardPipelineValveContext中的invokeNext方法里就能调用基础阀的invoke了,当basic.invoke(request, response, this);进入基础阀StandardEngineValve,看基础阀StandardEngineValve的invoke方法:

 

 

   public void invoke(Request request, Response response,
                       ValveContext valveContext)
        throws IOException, ServletException {
       ...........................

        // Ask this Host to process this request
        host.invoke(request, response);

    }

 

 

这里省略了很多代码,主要是为了更加理解调用逻辑,在StandardEngine的基础阀StandardEngineValve里,调用了子容器invoke方法(这里子容器就是StandardHost),还记得一开始connector.invoke(request, response)(即StandardEngine的invoke方法)现在顺利的传递到子容器StandardHost的invoke方法,变成了StandardHost.invoke(request, response)。由此可以猜测StandardHost也会传递给它的子容器,最后传递到最小的容器StandardWrapper的invoke方法,然后调用StandardWrapper的基础阀StandardWrapperValue的invoke方法,由于StandardWrapper是最小的容器了,不能再传递到其他容器的invoke方法了,那它的invoke方法做了什么?主要做了两件事, 1:创建一个过滤器链并 2:分配一个servlet或者jsp,主要代码如下:

StandardWrapperValue的invoke方法
            servlet = wrapper.allocate();  //分配一个servlet
....................................................................
         // Create the filter chain for this request
            ApplicationFilterChain filterChain =
            createFilterChain(request, servlet);
.........................................................
            String jspFile = wrapper.getJspFile();//分配一个jsp
            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和sres是request和response的包装类,在这里面会调用servlet的services方法
            }

 

这里先不关注jsp,只关注一下servlet,通过servlet = wrapper.allocate(); 进入StandardWrapper的allocate方法,allocate主要就是调用了loadServlet方法,在loadServlet方法类用tomcat自己的类加载器实例化了一个servlet对象,并调用了该servlet的init和service方法:

 

StandardWrapper的loadServlet方法(这里省略了很多其他的代码)
 Servlet servlet = null;
String actualClass = servletClass;//servlet的字节码字符串
 Loader loader = getLoader();
ClassLoader classLoader = loader.getClassLoader();//得到类加载器
 Class classClass = null;
                if (classLoader != null) {
                    System.out.println("Using classLoader.loadClass");
                    classClass = classLoader.loadClass(actualClass);//通过类加载器实例化servlet
                } else {
                    System.out.println("Using forName");
                    classClass = Class.forName(actualClass);//通过反射实例化servlet
                }
 servlet = (Servlet) classClass.newInstance();//实例化servlet
  servlet.init(facade);//调用servlet的init
     if ((loadOnStartup > 0) && (jspFile != null)) {
                    // Invoking jspInit
                    HttpRequestBase req = new HttpRequestBase();
                    HttpResponseBase res = new HttpResponseBase();
                    req.setServletPath(jspFile);
                    req.setQueryString("jsp_precompile=true");
                    servlet.service(req, res);
                };//调用jsp的service方法,jsp会被编译成servlet,所以也会有service方法


至此已经把请求传递到servlet的service(或者jsp的service)方法,整个处理请求到这里就结束了,剩下的就是返回客户端了。这里提出几个问题。1:那么多的servlet,tomcat是怎么知道要请求到哪个servlet? 这个问题留待下篇博客再来讲吧。

 

 


分享到:
评论

相关推荐

    tomcat 源码分析系列文档

    4. "Tomcat源码分析(4)容器处理链接之责任链模式.doc":分析了Tomcat如何利用责任链模式来处理请求,使得请求可以被多个处理器(如过滤器)有序处理。 5. "tomcat加载类的顺序.doc":详细说明了Tomcat加载类的具体...

    web容器---servlet

    7. **源码分析**:“源码”可能是指对Servlet的工作原理或Web容器内部机制的深入理解,包括Servlet的加载、请求处理流程、线程模型等。 8. **实战应用**:Servlet广泛应用于各种Web应用程序,包括登录验证、文件...

    BBS-CS V3.0 虚拟社区系统正式版(Tomcat4)

    - **Tomcat 4**:Tomcat是Apache软件基金会的项目,是一个开源的Servlet容器,支持JSP和Servlet,用于运行Java Web应用程序。Tomcat 4版本相对较旧,但仍然能够运行基于JSP的BBS系统。 - **JDBC(Java Database ...

    tomcat Apache Tomcat Directory Host Appbase Authentication Bypass Vulnerability

    Apache Tomcat是广泛使用的开源Java Servlet容器,它实现了Java EE(现在称为Jakarta EE)的Web应用程序规范。这个漏洞可能允许攻击者绕过身份验证,从而对受保护的目录和应用程序进行未经授权的访问。 描述中提到...

    基于jsp+servlet+layui教师科研论文管理系统源码案例设计.zip

    - JSP页面会被容器(如Tomcat)转换成Servlet并编译,运行时执行生成的Servlet代码,实现动态内容的生成。 - 在这个案例中,JSP可能被用来展示科研论文的信息,如作者、题目、发表时间等,同时处理用户提交的查询...

    基于jsp的学生档案管理系统源码数据库.doc

    - Apache Tomcat是一款开源的Servlet容器,用于部署Java Web应用程序。 - 支持JSP和Servlet标准,是Java Web开发的常用工具之一。 **3.5 BS编程简介** - BS编程特指在B/S架构下的Web应用开发。 - 使用HTML、CSS、...

    尚硅谷JavaWEB 项目实战(图书商城)视频下载链接地址

    这个项目涵盖了从需求分析到系统设计,再到编码实现和测试的完整流程,对于初学者来说,是一次难得的实践机会。 1. **JavaWeb基础** - **Servlet**: 作为JavaWeb应用的核心,Servlet是处理HTTP请求和响应的组件,...

    [搜索链接]Mysoo站内搜索 v1.0 Peview_mysoo-1.0-preview.war.zip

    源码分析是提升编程技能的重要途径。通过对【Mysoo站内搜索 v1.0 Preview】的源码学习,学生可以深入理解以下知识点: 1. **JSP基础**:学习JSP语法,包括脚本元素、表达式、声明和动作标签的使用。 2. **Servlet和...

    java servlet 博客源码

    10. **部署与配置**:将博客应用部署到服务器,配置Web容器(如Tomcat)和数据库连接参数,是实现博客系统运行的最后步骤。 这个博客源码提供了一个学习和实践Java Web开发的实例,涵盖了从基础的Servlet编程到更...

    [搜索链接]要广告分类系统 v2.0_yad20.zip

    6. **Web容器**:为了运行Java Web应用,需要一个Web容器,如Tomcat或Jetty。学生会学习如何配置和部署应用程序到这些容器中。 7. **版本控制**:虽然未直接提及,但源码可能使用了版本控制系统,如Git,便于团队...

    JSP和Servlet基础知识点

    **JSP(JavaServer Pages)和Servlet是Java Web开发中的两个核心技术,...综上所述,JSP和Servlet是Java Web开发的核心,它们结合使用能实现强大的功能,同时借助合适的工具和源码分析,可以提高开发效率和代码质量。

    struts2资料

    源码分析是学习Struts2的重要部分。通过阅读Struts2的源代码,我们可以深入理解其工作原理,例如Action的执行流程、结果映射、拦截器的调用链等。这有助于我们在遇到问题时进行调试,或者根据需求自定义拦截器和扩展...

    Expert One-on-One J2EE Design & Development

    - 部署和容器:讨论如何将应用部署到像Tomcat、WebLogic或JBoss这样的应用服务器,以及容器如何管理J2EE组件的生命周期。 - 安全性:涵盖J2EE的安全特性,如角色基础的访问控制(RBAC)、HTTPS、JAAS(Java ...

    JSP BLOG等源码.......

    源码分析可以帮助我们了解如何使用JSP处理HTTP请求,存储和检索数据,以及构建用户界面。 3. **MyBlog**:这个文件名可能指的是一个使用JSP实现的个人博客项目。它可能包含了数据库连接、用户认证、文章管理、评论...

    [博客空间]JSPWiki ALPHA release v2.3.92_jspwiki-2.3.92-alpha-src.zip

    8. **Web容器**:JSPWiki需要部署在像Tomcat这样的Web容器中运行,理解容器如何工作以及如何配置部署也是重要的学习环节。 总的来说,JSPWiki源码提供了一个全面的Java Web应用示例,涵盖了从前端到后端的多种技术...

    jsp源码网上订餐系统(struts+spring+hibernate)

    - **Web容器**:如Tomcat 8.x 或更高版本 - **数据库**:MySQL 5.7 或更高版本 - **开发工具**:Eclipse/IntelliJ IDEA等 #### 六、学习与实践建议 对于想要深入学习该项目的学生或开发者来说,建议从以下几个方面...

    基于ssm+jsp网络游戏公司官方平台源码数据库.doc

    - **Apache Tomcat**:一个免费开源的Servlet容器,用于运行基于Java的应用程序,如JSP和Servlet。 #### 三、系统设计 ##### 3.1 系统架构 系统采用了经典的MVC(Model-View-Controller)架构模式,将模型层...

    2012java开发工程师必备精品资料

    1. **源码分析**:对于Java开发者来说,阅读和理解源码是提升技术能力的重要途径。这份资料可能提供了对知名开源项目的源码解析,如Spring、Hibernate、Struts等,帮助开发者深入理解框架的工作原理。 2. **开发...

    jsp高校科研项目管理系统Java源码

    Java源码的提供意味着我们可以深入研究系统的设计模式、数据库交互、业务逻辑处理等多个方面,这对于学习和理解Java Web开发有极大的帮助。 【标签】"java"暗示了这个系统的后端开发主要依赖Java语言,可能包括了...

    EJB_BookStore.rar_DEMO_bookstore_bookstore e_ejb_网上书店 源码

    7. **部署和容器**:EJB组件需要在兼容的Java EE服务器(如Tomcat、WildFly、WebLogic等)中部署,服务器负责管理和调度bean的生命周期。 8. **设计模式**:在EJB开发中,可能会用到各种设计模式,如工厂模式(用于...

Global site tag (gtag.js) - Google Analytics