您的位置:首页 > 运维架构 > Tomcat

How Tomcat works之第十一章 Allocate The Servlet

2014-06-26 21:33 375 查看
如同这章开头提到的那样,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 ;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: