How Tomcat works之第十一章 Allocate The Servlet

来源:互联网 发布:康师傅 解散 知乎 编辑:程序博客网 时间:2024/06/02 17:42

 如同这章开头提到的那样,StandardWrapperValue的invoke方法调用wrapper的allocate方法获取一个请求servlet的实例。StandardWrapper类必须有这个方法的实现。Allocate方法的签名如下:

 

Publicjavax.servlet.Serlvet  allocate() throwsServletException

 

注意allocate方法返回请求servlet的实例

 

为了支持STM servlets 使得allocate方法有点小复杂。实际上,allocate方法分成两部分,一部分满足非STM servlets需求,其他的满足STM Servlets需要。第一部分有如下框架:

If(!singleThreadModel){

       //return a non-STM servlet instance

}

 

singleThreadModel是一个Boolean值,指明由StandardWrapper代表的Servlet是否是一个STM Servlet。虽然singleThreadModel初始值是false,但是loadServlet方法测试它加载的Servlet并设置这个Boolean ,如果这个Servlet是一个STM Servlet。

 

如果simpleThreadModel是true,allocate方法的第二部分就被执行。大致骨架如下:

synchronized(instancepool) {

     //returns an instance of the servlet from the pool

}

 

现在我们看看第一部分和第二部分

 

对于非STMServlet,StandardWrapper定义了一个命名为instance 类型为javax.servlet.servlet的变量。

 

Private Servletinstance = null;

 

Allocate方法检查实例是否为null。如果为null,allocate方法调用loadServlet方法加载Servlet。之后使countAllocated变量加一并返回Servlet。

代码如下:

if(!singleThreadModel) { // Load and initialize our instance if necessary

 if (instance == null) {

     synchronized (this) { if (instance ==null) {

         try { instance = loadServlet(); }

              catch (ServletException e) {throw e; }

 catch (Throwable e) {

throw newServletException

(sm.getString("standardWrapper.allocate"),e); } } } }

 

if(!singleThreadModel) {

     if (debug >= 2)

            log(" Returninq non-STMinstance");

            countAllocated++;

            return (instance);

    }

 }

 

如果Servlet是一个STM Servlet,allocate方法尝试从池中获取一个实例。InstancePool变量是java.util.Stack类型,与STM Servlet实例的栈关联。

代码如下:

  Private Stack  instancePool = null;

 

这个变量在loadServlet方法内部实例化,这将在下一章节讨论。

 

Allocate方法将分配STM Servlet的一个实例,只要实例的数量没有达到指定的最大值。maxInstances最大值默认为20.

 

Private  int maxInstance  = 20;

为获取STM 实例的当前数量,StandardWrapper类使用nInstances变量。

 

这里是allocate方法的第二部分。

 

 

synchronized(instancePool) { while (countAllocated >= nInstances) { // Allocate a newinstance if possible, or else wait if (nInstances < maxInstances) { try {instancePool.push(loadServlet()); nInstances++; } catch (ServletException e) {throw e; } catch (Throwable e) { throw new ServletException(sm.getString("StandardWrapper.allocate"), e); } } else { try {instancePool.wait(); } catch (InterruptedException e) {

 

; } } } if (debug>= 2) log(" Returning allocated STM instance"); countAllocated++;return (Servlet) instancePool.pop();

}

 

 

上路代码使用了一个while循环,直到nInstances少于或者等于countAllocated的值。在while循环内部,allcoate方法检查nInstances的值,如果低于maxInstances,allocate方法将调用loadServlet方法并将新的实例添加到pool中并使nInstances加一。如果nInstances的值与maxInstance相等或者更大,它就通过调用instancePool的stack的wait方法等待,等待一个实例返回到stack中。

 

Loading theServlet

 

StandardWrapper类实现了wrapper接口的load方法。load方法调用loadServlet方法(加载这个Servlet)并调用它的init方法,传递一个javax.servlet.ServletConfig实例。这里是loadServlet方法如何工作的。

 

loadServlet方法以检查当前StandardWrapper代表的是否是一个STM Servlet开始。如果不是并且变量实例不为null(意味着Servlet之前已经加载过了),就简单的返回实例。

 

// Nothing to doif we already have an instance or an instance pool if (!singleThreadModel&& (instance != null)) return instance;

 

如果实例为null或者是一个STM实例,就运行方法的剩余部分。

 

首先,获取System.out和System.err的输出,这样在使用了ServletContext的log方法后可以记录任何信息。

 

PrintStream out =System.out; SystemLogHandler.startCapture();

 

之后定义类型为Servlet的变量。这代表一个需要加载的实例,由loadServlet方法返回。

Servlet   servlet = null;

 

loadServlet方法负责加载一个servlet类。这个类的名字应该已经被指定给servletClass 的类变量。这个方法指定这个变量的值给String类型的actualClass变量。

 

String  actualClass  =  servletClass


然而,由于Catalina也是一个jsp容器,loadServlet方法必须也找出请求的Servlet是否是一个jsp页面。如果是的,loadServlet方法试着获取JSP页面真正的类。


if ((actualClass == null) && (jspFile != null)) {
       Wrapper jspWrapper = (Wrapper)
        ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
       if (jspWrapper != null)
          actualClass = jspWrapper.getServletClass();
    }

如果JSP页面的Servlet的名字没能发现,Servletclass变量的值将被使用。然而,如果servletClass还没有被StandardWrapper类的setServletClass方法调用,将抛出一个异常并且方法的剩余部分也不会执行。


// Complain if no servlet class has been specified
     if (actualClass == null) {
        unavailable(null);
        throw new ServletException
           (sm.getString("StandardWrapper.notClass", getName()));
     }


在这个阶段,Servlet 类名还没有被指定,这样loadServlet方法获取加载器。如果没有加载器发现,就抛出一个异常并停止方法。


// Acquire an instance of the class loader to be used
     Loader loader = getLoader();
     if (loader == null) {
        unavailable(null);
        throw new ServletException
        (sm.getString("StandardWrapper.missingLoader", getName()));
     }

如果没有发现加载器,loadServlet方法调用它的getClassLoader方法去获取一个ClassLoader

      ClassLoader classLoader = loader.getClassLoader().


Catalina提供了特殊的Servlets ,这些都在org.apache.catalian包中。如果Servlet是一个特殊的Servlet,即如果isContainerProvidedServlet方法返回true,classLoader变量由另一个ClassLoader实例指定,这样访问Catalina的内部成为可能。


// Acquire an instance of the class loader to be used
   // Special case class loader for a container provided servlet
     if (isContainerProvidedServlet(actualClass)) {
        ClassLoader = this.getClass().getClassLoader();
        log(sm.getString
           ("standardWrapper.containerServlet", getName()));
     }


有了类加载器和要加载的servlet 名字,loadServlet方法现在能加载这个servlet了。


// Load the specified servlet class from the appropriate class
     // loader
     Class classClass = null;
     try {
        if (ClassLoader != null) {

 System.out.println("Using classLoader.loadClass");
            classClass = classLoader.loadClass(actualClass);
        }
        else {
            System.out.println("Using forName");
            classClass = Class.forName(actualClass);
        }
     }
     catch (ClassNotFoundException e) {
        unavailable(null);
        throw new ServletException
            (sm.getstring("standardWrapper.missingClass", actualClass), e);
     }
     if (classClass == null) {
        unavailable(null);
        throw new ServletException
            (sm.getString("standardWrapper.missingClass", actualClass));
     }

稍后,它能实例化这个servlet:


 // Instantiate and initialize an instance of the servlet class
     // itself
     try {
        servlet = (Servlet) classClass.newInstance();
     }
     catch (ClassCastException e) {
        unavailable(null);
        // Restore the context ClassLoader
        throw new ServletException
            (sm.getString("standardWrapper.notServlet", actualClass), e);
     }
     catch (Throwable e) {
        unavailable(null);
        // Restore the context ClassLoader
        throw new ServletException
            (sm.getstring("standardWrapper.instantiate", actualClass), e);

     }

然而,在loadServlet方法实例化servlet之前,检查加载这个servlet是否被isServletAllowed方法允许。


if (!isServletAllowed(servlet)) {
        throw new SecurityException
           (sm.getString("standardWrapper.privilegedServlet",
              actualClass));
     }

如果安全校验通过,它检查servlet是否是一个ContainerServlet。其实现了ContainerServlet接口并能访问Catalina内部功能。如果Servlet是一个ContainerServlet,loadServlet方法调用ContainerServlet的SetWrapper方法,传递这个StandardWrapper实例。



     // Special handling for ContainerServlet instances
     if ((servlet instanceof ContainerServlet) &&
        isContainerProvidedServlet(actualClass)) {
        ((ContainerServlet) servlet).setWrapper(this);
     }

下一步,loadServlet方法触发了BEFORE_INIT_EVENT并调用sender的init方法。


try {
        instanceSupport.fireInstanceEvent(
           InstanceEvent.BEFORE_INIT_EVENT, servlet);
        servlet.init(facade);

注意,init方法传递了门面变量,其与ServletConfig对象关联。


如果loadOnStartup变量被指定为一个整形值并且Servlet是一个JSP页面,就调用这个Servlet的service方法。


 // Invoke jspInit on JSP pages
     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);
     }


下一步,loadServlet方法触发了AFTER_INIT_EVENT事件。


instanceSupport.firelnstanceEvent (InstanceEvent.AFTER_INIT_EVENT,
        servlet);

如果被StandardWrapper对象表示的Servlet是一个STM Servlet,这个Servlet实例被添加到实例池中。因此,如果这个instancePool变量仍就为空,就被赋值一个Stack对象。


 // Register our newly initialized instance
       singleThreadModel = servlet instanceof SingleThreadModel;
       if (singleThreadModel) {
          if (instancePool == null)
             instancePool = new Stack();
       }
       fireContainerEvent("load", this);
   }


在finally块中,loadServlet方法停止输出System.out和System.err并记录在加载过程中任何发往到ServletContext的log方法。


finally {
       String log = SystemLogHandler.stopCapture();
       if (log != null && log.length() > 0) {
          if (getServletContext() != null) {
             getServletContext().log(log);
          }
          else {
             out.println(log);
          }
       }
   }

并且,最后返回loadServlet方法返回Servlet的实例。


     return servlet ;



0 0
原创粉丝点击