分享Tomcat源码系列三部曲

开发 后端
本文主要和大家分享一下Tomcat的源代码,希望对大家有所帮助。

最近在看Tomcat的源码,下面用博客记下看源码的一些心得。

Tomcat是从org.apache.catalina.startup.Bootstrap#main()开始启动. 大致分为三个步骤,即init、load和start。代码如下:

Java代码

public static void main(String args[]) {      
        try {      
            // Attempt to load JMX class      
            new ObjectName("test:foo=bar");      
        } catch (Throwable t) {      
            System.out.println(JMX_ERROR_MESSAGE);      
            try {      
                // Give users some time to read the message before exiting      
                Thread.sleep(5000);      
            } catch (Exception ex) {      
            }      
            return;      
        }      
        if (daemon == null) {      
            daemon = new Bootstrap();      
            try {      
                daemon.init();   ★1      
            } catch (Throwable t) {      
                t.printStackTrace();      
                return;      
            }      
        }      
        try {      
            String command = "start";      
            if (args.length > 0) {      
                command = args[args.length - 1];      
            }      
            if (command.equals("startd")) {      
                args[0] = "start";      
                daemon.load(args);      
                daemon.start();      
            } else if (command.equals("stopd")) {      
                args[0] = "stop";      
                daemon.stop();      
            } else if (command.equals("start")) {      
                daemon.setAwait(true);      
                daemon.load(args);   ★2      
    
             // 反射调用Catalina的start方法      
                daemon.start();        ★3      
            } else if (command.equals("stop")) {      
                daemon.stopServer(args);      
            }      
        } catch (Throwable t) {      
            t.printStackTrace();      
        }      
    }      
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.

 

从以上可以很清楚的看出tomcat是通过参数的不同进行相应的命令调用。

★1 启动、初始化(加载类)

启动之前要进行相应的init()初始化,进行相应的环境设置以及包的加,以下是init()方法。(org.apache.catalina.startup.Bootstrap.init())

Java代码

 

public void init()      
        throws Exception      
    {      
        setCatalinaHome();//设置Catalina安装目录      
        setCatalinaBase();//设置Catalina工作目录      
        initClassLoaders();//加载jar包      
    
       // 将classload设置进线程,以便我们使用时进行调用            
        Thread.currentThread().      
                      setContextClassLoader(catalinaLoader);      
        SecurityClassLoad.securityClassLoad(catalinaLoader);      
    
        // 加载启动类和调用它的process方法      
        if (log.isDebugEnabled())      
            log.debug("Loading startup class");      
        Class startupClass =      
            catalinaLoader.loadClass      
            ("org.apache.catalina.startup.Catalina");      
        Object startupInstance = startupClass.newInstance();      
    
        // 设置共享扩张类加载器      
        if (log.isDebugEnabled())      
            log.debug("Setting startup class properties");      
        String methodName = "setParentClassLoader";      
        Class paramTypes[] = new Class[1];      
        paramTypes[0] = Class.forName("java.lang.ClassLoader");      
        Object paramValues[] = new Object[1];      
        paramValues[0] = sharedLoader;      
        Method method =      
        startupInstance.getClass().getMethod(methodName,      
                                                          paramTypes);      
        method.invoke(startupInstance, paramValues);      
        catalinaDaemon = startupInstance;      
    }  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

 

在加载jar的时候,需要初始化classloader,代码如下:(org.apache.catalina.startup.Bootstrap)

Java代码

 

private void initClassLoaders() {      
        try {      
            commonLoader = createClassLoader("common"null);      
            catalinaLoader= createClassLoader("server", commonLoader);      
            sharedLoader = createClassLoader("shared", commonLoader);      
        } catch (Throwable t) {      
            log.error("Class loader creation threw exception", t);      
            System.exit(1);      
        }      
    }   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

 

tomcat中的加载方式是:

|-------commonLoader (common)-> System Loader

|-------sharedLoader (shared)-> commonLoader -> System Loader

|-------catalinaLoader(server) -> commonLoader -> System Loader

Common是公共类加载器,负责加载tomcat内部和web应用程序可以看到的类(%CATALINA_HOME%/bin/common下的jar文件),Catalina负责加载的是tomcat内部使用的类(%CATALINA_HOME%/server下的jar文件),这些类对web应用程序不可见。Shared负责加载的是web应用程序之间共享的类(%CATALINA_BASE%/shared下的jar文件),这些类对于tomcat内部是不可见的。如果%CATALINA_HOME%/conf/catalina.Properties中没有指定Common的搜索路径,则用当前的类的类加载器即系统类加载器作为Common。

★2 装载相应的资源

下面主要讲解tomcat的load()方法。下图是Catalina.load方法的时序图。

 

 

(1) 从上面的时序图可以看出首先调用Catalina类的load()方法,具体代码如下:

(org.apache.catalina.startup.Catalina)。

Java代码

 

public void load() {      
        initDirs();      
    
        // Before digester - it may be needed      
        initNaming();      
    
        // Create and execute our Digester      
        Digester digester = createStartDigester();      
            
        try {      
            inputSource.setByteStream(inputStream);      
            digester.push(this);      
            digester.parse(inputSource); //对server.xml进行解析      
            inputStream.close();      
        }      
       ......      
        // Start the new server      
        if (server instanceof Lifecycle) {      
            try {      
                server.initialize();  //server初始化工作      
            } catch (LifecycleException e) {      
                log.error("Catalina.start", e);      
            }      
        }      
        long t2 = System.currentTimeMillis();      
        log.info("Initialization processed in " + (t2 - t1) + " ms");      
    
    }     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

 

(2) 在上面的load()方法中需要进行server的初始化工作,下图为Catalina.initialize的时序图,从图中可以看出server初始化所完成的工作。

 

 

至此,load方法结束,初期化的工作结束,下面开始进入start方法。

★3 容器启动

容器启动时,会调用Catalina.start(),下图为它的时序图。从图中可以看出StandardService的start方法被调用后会分别对Container和Connector进行start方法的调用。

 

 

1. Bootstrap调用Catalina的start方法

Catalina.start()方法(org.apache.catalina.startup.Catalina.start())

Java代码

 

public void start() {      
    // 启动server      
    if (server instanceof Lifecycle) {      
        try {      
            ((Lifecycle) server).start();      
                    ......      
   }     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

 

2. Catalina调用StandardServer的start方法

StandardServer.start() (org.apache.catalina.core.StandardServer.start() )

Java代码

 

public void start() throws LifecycleException {            
        synchronized (services) {      
            for (int i = 0; i < services.length; i++) {      
                if (services[i] instanceof Lifecycle)      
                    ((Lifecycle) services[i]).start();      
            }       
}   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

 

3. StandardServer调用StandardService的start方法

Java代码

 

org.apache.catalina.core.StandardService.start() )      
        public void start() throws LifecycleException {      
                  if (container != null) {      
            synchronized (container) {      
                if (container instanceof Lifecycle) {      
              //  standardEngine的启动      
                    ((Lifecycle) container).start();      
                }      
            }      
       //两个connector的启动,8080和8009        
       synchronized (connectors) {        
           for (int i = 0; i < connectors.length; i++) {        
               if (connectors[i] instanceof Lifecycle)        
                   ((Lifecycle) connectors[i]).start();        
                  }        
       }        
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

 

以上StandardService.start()方法主要实现了两个功能,standardEngine的启动和connector的启动,下面分别来介绍。

#p#

下面是standardEngine的启动和connector的启动

● standardEngine的启动

(1) 首先是StandardEngine.start()被调用

Java代码

 

 

public void start() throws LifecycleException {      
       // Standard container startup        
      //进行logger,manager,cluster,realm,resource的启动        
       super.start();      
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

 

(2) super.start()--->org.apache.catalina.core.ContainerBase#start()

Java代码

 

 

public synchronized void start() throws LifecycleException {      
//(省略)  server.xml中配置应用组件的启动         
//StandardHost容器的启动,        
        Container children[] = findChildren();        
        for (int i = 0; i < children.length; i++) {        
            if (children[i] instanceof Lifecycle)        
                ((Lifecycle) children[i]).start();        
        }          
    
    //StandardPipeline的启动(容器与容器间的管道)        
        if (pipeline instanceof Lifecycle)        
            ((Lifecycle) pipeline).start();       
}   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

 

(3) StandardHost.start()被调用

Java代码

 

 

public synchronized void start() throws LifecycleException {      
//返回到以上的containerBase#start执行pipeline        
      super.start();       
}  
  • 1.
  • 2.
  • 3.
  • 4.

 

(4) StandardPipeline#start

Java代码

 

 

public synchronized void start() throws LifecycleException {      
       // 将会调用HostConfig#start方法        
       lifecycle.fireLifecycleEvent(START_EVENT, null);        
       
       // Notify our interested LifecycleListeners        
       lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);        
}    
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

 

(5) HostConfig#start

Java代码

 

 

public void start() {       
          //部暑webapps        
          deployApps();                 
  }     
  • 1.
  • 2.
  • 3.
  • 4.

 

(6) HostConfig#deployApps

Java代码

 

 

protected void deployApps() {        
    File appBase = appBase();        
    File configBase = configBase();        
    // Deploy XML descriptors from configBase        
    deployDescriptors(configBase, configBase.list());        
    // Deploy WARs, and loop if additional descriptors are found        
    deployWARs(appBase, appBase.list());        
    // Deploy expanded folders        
    deployDirectories(appBase, appBase.list());                  
}    
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

 

(7) deployWARs

Java代码

 

 

protected void deployWARs(File appBase, String[] files) {      
……      
deployWAR(contextPath, dir, file);               
  }  
  • 1.
  • 2.
  • 3.
  • 4.

 

(8) deployWAR

Java代码

 

 

protected void deployWAR(String contextPath, File war, String file) {      
if (context instanceof Lifecycle) {        
  // (省略)      
            Class clazz = Class.forName(host.getConfigClass());        
            LifecycleListener listener =        
                (LifecycleListener) clazz.newInstance();        
            ((Lifecycle) context).addLifecycleListener(listener);        
        }        
        context.setPath(contextPath);        
        context.setDocBase(file);        
        //以下这一步跟进去,,StandardContext的启动        
        host.addChild(context);              
  }   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

 

(9) StandardContext#start

在Context的启动过程中,主要完成了以下任务。

----------------------------------------------------------------------------------------------------------------------

a) 设置web app的具体目录webappResources。

b) postWorkDirectory (),创建临时文件目录。Tomcat下面有一个work目录,用来存放临时文件。

c) 触发START_EVENT事件监听,在这个事件监听里面会启动ContextConfig的start()事件,ContextConfig是用来配置web.xml的。

d) 为context创建welcome files,通常是这三个启动文件:index.html、index.htm、index.jsp

e) 配置filter

f) 启动带有的Servlet。

g) 注册JMX。

----------------------------------------------------------------------------------------------------------------------

至此,Container启动完毕,下面是connector的启动。

● connector的启动

(1) org.apache.catalina.connector.Connector.start()

Java代码

 

 

public void start() throws LifecycleException {      
           // Http11Protocol的启动      
            protocolHandler.start();      
}   
  • 1.
  • 2.
  • 3.
  • 4.

 

(2) Http11Protocol#start

Java代码

 

 

public void start() throws Exception {      
try {        
            //到了终点的启动        
            endpoint.start();        
        } catch (Exception ex) {        
            log.error(sm.getString("http11protocol.endpoint.starterror"), ex);        
            throw ex;        
        }    
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

 

(3) JIoEndPoint#start

Java代码

 

 

public void start()        
        throws Exception {                 
                   
            for (int i = 0; i < acceptorThreadCount; i++) {        
        //这里的acceptor是一个线程,里面是一个serversocket的启动        
                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);        
                acceptorThread.setPriority(threadPriority);        
                acceptorThread.setDaemon(daemon);        
                acceptorThread.start();        
            }        
        }  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

 

(4) Acceptor#run

Java代码

 

 

public void run() {                    
// Accept the next incoming connection from the server socket        
               try {        
          //这里进行了accept(),等待客户端消息,进行接收        
                   Socket socket = serverSocketFactory.acceptSocket(serverSocket);        
                   serverSocketFactory.initSocket(socket);        
                   // Hand this socket off to an appropriate processor        
                   if (!processSocket(socket)) {        
                       // Close socket right away        
                       try {        
                           socket.close();        
                       } catch (IOException e) {        
                           // Ignore        
                       }        
                   }        
               }catch ( IOException x ) {        
                   if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);        
               } catch (Throwable t) {        
                   log.error(sm.getString("endpoint.accept.fail"), t);        
               }        
}    
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

 

至此Connector.start方法调用完毕。整个server启动完毕。

#p#

本次讲解一下Tomcat请求处理的流程,不当之处还请comment。

一. Tomcat 总体结构

Tomcat采用模块化管理,下面是 Tomcat 的总体结构图:

 

 

从上图中可以看出 Tomcat 的核心是两个组件:Connector 和 Container。下面是一些概念的介绍。

① Server

一个server代表了整个catalina servlet容器,在Tomcat里面的Server的用处是启动和监听服务端事件(诸如重启、关闭等命令)。

② Service

Service是由一个或多个Connector与一个Engine的组合。

③ Connector

Connector将在某个指定的端口上监听客户的请求,把从socket传递过来的数据,封装成Request,传递给Engine来处理,并从Engine处获得响应并返回给客户。

Tomcat通常会用到两种Connector:

a) Http Connector 在端口8080处侦听来自客户browser的http请求。

b) AJP Connector 在端口8009处侦听来自其它WebServer(Apache)的servlet/jsp代理请求。

二、请求处理过程解析

1. Connector处理请求

Connector处理请求的流程大致如下:

 

 

Connector组件启动后,会侦听相关的端口的客户端请求。

(1) 接受一个新的连接请求(org.apache.tomcat.util.net.TcpWorkerThread)

Java代码

 

void runIt(Object[] perThrData){      
       Socket s = null;      
            try {      
                s = endpoint.acceptSocket();  //获取一个请求      
            } finally {      
                if (endpoint.isRunning()) {      
                    endpoint.tp.runIt(this);      
  // 此处启动另一个TcpWorkerTread去接受其他请求,此线程处理已接受的请求      
                }      
            }                      
      TcpConnection con = null;      
      con = (TcpConnection) perThrData[0];      
      con.setEndpoint(endpoint);      
      con.setSocket(s);endpoint.getConnectionHandler().processConnection(con,(Object[]) perThrData[1]);          
}    
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

 

(2) 新接收的请求被传到Http11ConnectionHandler中处理。(org.apache.coyote.http11.Http11Protocol.Http11ConnectionHandler)

Java代码

 

void processConnection(TcpConnection connection, Object[] thData){          
    Http11Processor  processor=null;      
    processor=(Http11Processor)thData[Http11Protocol.THREAD_DATA_PROCESSOR];        
    socket=connection.getSocket();                           
    InputStream in = socket.getInputStream();        
    OutputStream out = socket.getOutputStream();      
    processor.setSocket(socket );      
    processor.process(in, out);        
//processor是org.apache.coyote.http11.Http11Processor 的 一个实例      
}    
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

 

(3) 在 Http11Processor 中处理 http11 协议相关的信息(org.apache.coyote.http11.Http11Processor)

Java代码

 

void process(InputStream input, OutputStream output) throws IOException{      
        ~~略~~      
        inputBuffer.setInputStream(input);      
        outputBuffer.setOutputStream(output);      
        inputBuffer.parseHeaders();      
      //http11 协议头在此方法中被取出      
        adapter.service(request, response);         
      //adapter 是org.apache.catalina.connector.CoyoteAdapter 的 一个实例      
}   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

 

接下来的流程交由容器进行处理。

2. 容器处理请求

容器交由Pipeline处理,这个Pipeline里面会放置一些vavle,请求沿着pipeline传递下去并且vavle对其进行相关的处理。比如说日志等,valve还可以自定义,具体需要查看server.xml配置文件。相关类图如下:

 

 

Tomcat的主要处理组件Engine、Host、Context和Wrapper的实现都会实现Pipeline接口,实际对请求的处理是一个Adpater,Tomcat中Adapter的实现是CoyoteAdapter,因此容器请求处理的入口是CoyoteAdapter的service方法。

1. CoyoteAdapter.service

--组装好请求处理链

--StandardEngine. getPipeline().getFirst().invoke(request, response);

--StandardEngineValve.invoke

2. StandardEngineValve.invoke

--Host.getPipeline().getFirst().invoke(request, response);

--StandardHostValve.invoke

3. StandardHostValve.invoke

--Context. getPipeline().getFirst().invoke(request, response);

--StandardContextValve.invoke

4. StandardContextValve.invoke

--ServletRequestListener.requestInitialized

--Wrapper.getPipeline().getFirst().invoke(request, response);

--StandardWrapperValve.invoke

-- ServletRequestListener.requestDestroyed

5. StandardWrapperValve.invoke

--组装Filter+Servlet

--处理请求

(1) Connector传来的请求调用CoyoteAdapter.service()方法。(org.apache.catalina.connector.CoyoteAdapter)

Java代码

 

public void service(org.apache.coyote.Request req,         
                    org.apache.coyote.Response res)        
    throws Exception {        
         ~~略~~       
   if (request == null) {       
        request = (Request) connector.createRequest();      
        request.setCoyoteRequest(req);      
        response = (Response) connector.createResponse();      
     response.setCoyoteResponse(res);      
     //创建request、response对象        
         ~~略~~        
    }              
    try {         
        if (postParseRequest(req, request, res, response)) {        
connector.getContainer().getPipeline().getFirst().invoke(request, response);      
//此处的Container是StandardEngine对象       
           ~~略~~         
    }        
}    
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

 

(2) 默认StandardEngine的Pipeline会有StandardEngineValve处理单元(参照StandardEngine构造函数)。(org.apache.catalina.core.StandardEngineValve)

Java代码

 

public final void invoke(Request request, Response response)        
    throws IOException, ServletException {          
     // Select the Host to be used for this Request        
  Host host = request.getHost();        
    if (host == null) {        
         response.sendError        
             (HttpServletResponse.SC_BAD_REQUEST,        
             sm.getString("standardEngine.noHost",        
                           request.getServerName()));        
         return;        
     }          
     // Ask this Host to process this request        
     host.getPipeline().getFirst().invoke(request, response);        
   }    
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

 

(3) 同样的,StandardHost的Pipeline会有StandardHostValve处理单元。StandardHostValve如何处理请求跟StandardEngineValve类似,接下来请求进入到StandardContextValve.invoke

(4) 同样的,StandardContext的Pipeline会有StandardContextValve处理单元。

Java代码

 

public final void invoke(Request request, Response response)        
        throws IOException, ServletException {         
        // Disallow any direct access to resources under WEB-INF or META-INF         MessageBytes requestPathMB = request.getRequestPathMB();        
        if ((requestPathMB.startsWithIgnoreCase("/META-INF/"0))        
            || (requestPathMB.equalsIgnoreCase("/META-INF"))        
            || (requestPathMB.startsWithIgnoreCase("/WEB-INF/"0))        
           || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {        
            String requestURI = request.getDecodedRequestURI();        
            notFound(requestURI, response);        
            return;        
        }          
        // Wait if we are reloading        
        while (context.getPaused()) {        
            try {        
                Thread.sleep(1000);        
            } catch (InterruptedException e) {        
                ;        
            }        
        }          
        // Select the Wrapper to be used for this Request        
        Wrapper wrapper = request.getWrapper();        
        if (wrapper == null) {        
            String requestURI = request.getDecodedRequestURI();        
            notFound(requestURI, response);        
            return;        
        }          
//ServletRequestListener. requestInitialized        
~~略~~        
       
   wrapper.getPipeline().getFirst().invoke(request, response);        
//ServletRequestListener.requestDestroyed        
~~略~~        
     }   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

 

(5) 同样的,StandardWrapper这个Pipeline会有StandardWrapperValve这个处理单元。在invoke()方法调用Filter的同时,servlet.service()方法也将会被调用。

(org.apache.catalina.core.StandardWrapperValve)

Java代码

 

void invoke(Request request, Response response, ValveContext valveContext)      
                throws IOException, ServletException{      
         Servlet servlet = null;      
          HttpServletRequest hreq = (HttpServletRequest) request.getRequest();           
//org.apache.catalina.Request被封装成javax.servlet.http.HttpServletRequest.      
          HttpServletResponse hres =(HttpServletResponse) response.getResponse();       
// org.apache.catalina.Response被封装成javax.servlet.http.HttpServletResponse.      
         servlet = wrapper.allocate();       // 装载servlet      
        if ((servlet != null) && (filterChain != null)) {      
           filterChain.doFilter(hreq, hres);                   //调用此servlet的filterchain      
       }   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

 

(6) 调用servlet的filterchain 处理 request和response

(org.apache.catalina.core.ApplicationFilterChain)

Java代码

 

void doFilter(ServletRequest request, ServletResponse response) throws      
                  IOException, ServletException{      
             ~~略~~      
           internalDoFilter(request,response);      
             ~~略~~      
       }    
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

 

(7) 调用internalDoFilter()处理请求。(org.apache.catalina.core.ApplicationFilterChain)

Java代码

 

void internalDoFilter(ServletRequest request, ServletResponse response) throws      
                        IOException, ServletException{      
     // 此处省略filter 处理的代码,filter 被一个一个调用。      
     // 如果http请求的是一个jsp页面, 下面的 servlet 会是 org.apache.jasper.servlet.JspServlet 类的一个实例      
     // 若是 html 页面, 下面的 servlet 会是 org.apache.catalina.servlets.DefaultServlet 类的一个实例      
     if ((request instanceof HttpServletRequest) &&      
          (response instanceof HttpServletResponse)) {      
      servlet.service((HttpServletRequest) request, (HttpServletResponse) response);      
        servlet.service(request, response);      
       } else {      
        servlet.service(request, response);      
      }      
    }   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

 

至此,servlet.service()方法被调用。

【编辑推荐】

  1. 配置Tomcat+SQL Server2000连接池流程
  2. 配置Tomcat 4使用SSL
  3. 深入剖析Subversion+Apache配置
  4. Fedora apache配置一个基于IP的虚拟主机
  5. Ubuntu Apache配置测试及安装各种指令
责任编辑:金贺 来源: JavaEye博客
相关推荐

2009-09-14 09:04:17

CCNA考试CCNA

2011-03-25 09:56:40

Nagios 安装

2010-07-17 01:12:31

Telnet服务

2010-09-26 14:39:40

DHCP故障分析

2011-03-09 09:30:52

Mina

2022-10-10 09:10:51

家庭网络网络

2010-09-06 09:22:26

CSS语法

2013-06-28 09:35:04

Hypervisor虚拟化成本

2015-05-12 10:42:53

程序员代码

2012-09-10 16:19:00

云计算公共云

2017-02-07 14:50:39

华为

2017-04-11 09:07:20

互联网

2011-08-29 16:26:50

魔兽世界LUA

2019-03-28 14:45:33

数据安全数据泄露信息安全

2010-05-12 10:51:06

2011-08-03 10:33:05

网络管理网络拓扑管理

2018-03-18 15:51:59

人工智能潜力首席数据官

2018-03-19 09:35:37

人工智能

2010-09-14 16:54:18

2009-10-22 13:09:19

VB.NET动态控件数
点赞
收藏

51CTO技术栈公众号