我们知道当tomcat收到一个请求会经过一系列的调用过程猜到servlet的执行。那么我们从这中间的standardWrapperValve的invoke开始。
public final void invoke(Request request, Response response) throws IOException, ServletException { ........此处省略部分代码.............. servlet = wrapper.allocate(); ........此处省略部分代码.............. ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance(); ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet); ........此处省略部分代码.............. if (comet) { filterChain.doFilterEvent(request.getEvent()); request.setComet(true); } else { filterChain.doFilter(request.getRequest(), response.getResponse()); } } ........此处省略部分代码.............. if (comet) { request.setComet(true); filterChain.doFilterEvent(request.getEvent()); } else { filterChain.doFilter (request.getRequest(), response.getResponse()); } } ........此处省略部分代码.............. }
可以看出invoke的作用有两个:一个是创建servlet,另外一个是执行过滤器链,其实当执行完过滤器后会去调用servlet的service方法(在ApplicationFilterChain中的doFilter)。
public Servlet allocate() throws ServletException { // If we are currently unloading this servlet, throw an exception if (unloading) throw new ServletException (sm.getString("standardWrapper.unloading", getName())); boolean newInstance = false; // If not SingleThreadedModel, return the same instance every time if (!singleThreadModel) { // Load and initialize our instance if necessary if (instance == null) { synchronized (this) { if (instance == null) { try { if (log.isDebugEnabled()) log.debug("Allocating non-STM instance"); instance = loadServlet(); // For non-STM, increment here to prevent a race // condition with unload. Bug 43683, test case #3 if (!singleThreadModel) { newInstance = true; countAllocated.incrementAndGet(); } } catch (ServletException e) { throw e; } catch (Throwable e) { throw new ServletException (sm.getString("standardWrapper.allocate"), e); } } } } if (!singleThreadModel) { if (log.isTraceEnabled()) log.trace(" Returning non-STM instance"); // For new instances, count will have been incremented at the // time of creation if (!newInstance) { countAllocated.incrementAndGet(); } return (instance); } } synchronized (instancePool) { while (countAllocated.get() >= nInstances) { // Allocate a new instance 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 (log.isTraceEnabled()) log.trace(" Returning allocated STM instance"); countAllocated.incrementAndGet(); return (Servlet) instancePool.pop(); } }
在分析代码之前,先来说说SingleThreadModel接口吧。实现此接口的目的是保证servlet一次只能有一个请求,不会有两个线程同是使用servlet的service方法,该接口不能避免同步而产生的问题,如访问静态类变量或该servlet以外的类或变量。我们把实现了该接口的servlet叫做STM组件。
在allocate中对两种情况都做了实现。如果一个servlet没有实现接口SingleThreadModel,那么该servlet被加载一次,以后每次的请求都返回相同的实例即可。而实现该接口的情况就不同了,如果StandardWrapper只维持了一个STM servlet的实例,那么就需要对这个servlet实例做同步操作,当然为了性能考虑也可以维护一个servlet池。在tomcat中很明显可以看出它是维护了一个实例池。知道了这些,那么理解allocate方法就不难了吧。
虽然StandardWrapperValve从StandardWrapper中的allocate方法获得了servlet实例,但是真正创建该servlet实例的并不是allocate方法,而是loadServlet方法。我们从allocate方法中也可以看出这点。
instance = loadServlet();
instancePool.push(loadServlet());那么继续 看看到底怎么创建的servlet实例的呢?
public synchronized Servlet loadServlet() throws ServletException { // Nothing to do if we already have an instance or an instance pool if (!singleThreadModel && (instance != null)) return instance; PrintStream out = System.out; if (swallowOutput) { SystemLogHandler.startCapture(); } Servlet servlet; try { long t1=System.currentTimeMillis(); // If this "servlet" is really a JSP file, get the right class. // HOLD YOUR NOSE - this is a kludge that avoids having to do special // case Catalina-specific code in Jasper - it also requires that the // servlet path be replaced by the <jsp-file> element content in // order to be completely effective String actualClass = servletClass; if ((actualClass == null) && (jspFile != null)) { Wrapper jspWrapper = (Wrapper) ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME); if (jspWrapper != null) { actualClass = jspWrapper.getServletClass(); // Merge init parameters String paramNames[] = jspWrapper.findInitParameters(); for (int i = 0; i < paramNames.length; i++) { if (parameters.get(paramNames[i]) == null) { parameters.put (paramNames[i], jspWrapper.findInitParameter(paramNames[i])); } } } } // Complain if no servlet class has been specified if (actualClass == null) { unavailable(null); throw new ServletException (sm.getString("standardWrapper.notClass", getName())); } // 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())); } ClassLoader classLoader = loader.getClassLoader(); // Special case class loader for a container provided servlet // if (isContainerProvidedServlet(actualClass) && ! ((Context)getParent()).getPrivileged() ) { // If it is a priviledged context - using its own // class loader will work, since it's a child of the container // loader classLoader = this.getClass().getClassLoader(); } // Load the specified servlet class from the appropriate class loader Class classClass = null; try { if (SecurityUtil.isPackageProtectionEnabled()){ final ClassLoader fclassLoader = classLoader; final String factualClass = actualClass; try{ classClass = (Class)AccessController.doPrivileged( new PrivilegedExceptionAction(){ public Object run() throws Exception{ if (fclassLoader != null) { return fclassLoader.loadClass(factualClass); } else { return Class.forName(factualClass); } } }); } catch(PrivilegedActionException pax){ Exception ex = pax.getException(); if (ex instanceof ClassNotFoundException){ throw (ClassNotFoundException)ex; } else { getServletContext().log( "Error loading " + fclassLoader + " " + factualClass, ex ); } } } else { if (classLoader != null) { classClass = classLoader.loadClass(actualClass); } else { classClass = Class.forName(actualClass); } } } catch (ClassNotFoundException e) { unavailable(null); getServletContext().log( "Error loading " + classLoader + " " + actualClass, e ); throw new ServletException (sm.getString("standardWrapper.missingClass", actualClass), e); } if (classClass == null) { unavailable(null); throw new ServletException (sm.getString("standardWrapper.missingClass", actualClass)); } // Instantiate and initialize an instance of the servlet class itself try { servlet = (Servlet) classClass.newInstance(); // Annotation processing if (!((Context) getParent()).getIgnoreAnnotations()) { if (getParent() instanceof StandardContext) { ((StandardContext)getParent()).getAnnotationProcessor().processAnnotations(servlet); ((StandardContext)getParent()).getAnnotationProcessor().postConstruct(servlet); } } } catch (ClassCastException e) { unavailable(null); // Restore the context ClassLoader throw new ServletException (sm.getString("standardWrapper.notServlet", actualClass), e); } catch (Throwable e) { unavailable(null); // Added extra log statement for Bugzilla 36630: // http://issues.apache.org/bugzilla/show_bug.cgi?id=36630 if(log.isDebugEnabled()) { log.debug(sm.getString("standardWrapper.instantiate", actualClass), e); } // Restore the context ClassLoader throw new ServletException (sm.getString("standardWrapper.instantiate", actualClass), e); } // Check if loading the servlet in this web application should be // allowed if (!isServletAllowed(servlet)) { throw new SecurityException (sm.getString("standardWrapper.privilegedServlet", actualClass)); } // Special handling for ContainerServlet instances if ((servlet instanceof ContainerServlet) && (isContainerProvidedServlet(actualClass) || ((Context)getParent()).getPrivileged() )) { ((ContainerServlet) servlet).setWrapper(this); } classLoadTime=(int) (System.currentTimeMillis() -t1); // Call the initialization method of this servlet try { instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT, servlet); if( Globals.IS_SECURITY_ENABLED) { boolean success = false; try { Object[] args = new Object[]{ facade }; SecurityUtil.doAsPrivilege("init", servlet, classType, args); success = true; } finally { if (!success) { // destroy() will not be called, thus clear the reference now SecurityUtil.remove(servlet); } } } else { servlet.init(facade); } // Invoke jspInit on JSP pages if ((loadOnStartup >= 0) && (jspFile != null)) { // Invoking jspInit DummyRequest req = new DummyRequest(); req.setServletPath(jspFile); req.setQueryString(Constants.PRECOMPILE + "=true"); DummyResponse res = new DummyResponse(); if( Globals.IS_SECURITY_ENABLED) { Object[] args = new Object[]{req, res}; SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args); args = null; } else { servlet.service(req, res); } } instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet); } catch (UnavailableException f) { instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet, f); unavailable(f); throw f; } catch (ServletException f) { instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet, f); // If the servlet wanted to be unavailable it would have // said so, so do not call unavailable(null). throw f; } catch (Throwable f) { getServletContext().log("StandardWrapper.Throwable", f ); instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet, f); // If the servlet wanted to be unavailable it would have // said so, so do not call unavailable(null). throw new ServletException (sm.getString("standardWrapper.initException", getName()), f); } // Register our newly initialized instance singleThreadModel = servlet instanceof SingleThreadModel; if (singleThreadModel) { if (instancePool == null) instancePool = new Stack(); } fireContainerEvent("load", this); loadTime=System.currentTimeMillis() -t1; } finally { if (swallowOutput) { String log = SystemLogHandler.stopCapture(); if (log != null && log.length() > 0) { if (getServletContext() != null) { getServletContext().log(log); } else { out.println(log); } } } } return servlet; }loadServlet方法负责加载servlet类,类名被分配给了servletClass变量,该方法将该值分配给了actualClass。jspWrapper是为了防止该servlet是个jsp页面。接下来就是要得到类加载器。
ClassLoader classLoader = loader.getClassLoader();Catalina提供了特殊的servlet,从属于org.apache.catalina包,这些servlet可以进入servlet容器内部。
就是为了得到这样一种特殊的servlet的类加载器。接下来就可以创建该servlet的一个实例啦。
servlet = (Servlet) classClass.newInstance();
if (!isServletAllowed(servlet)) { throw new SecurityException (sm.getString("standardWrapper.privilegedServlet", actualClass)); }在该实例进行初始化之前,需要判断该servlet是不是可以访问的isServletAllowed。如果loadonStartup变量的值大于0并且servlet是一个jsp页面,调用该servlet的service方法。
如果你足够细心的话,你会发现我没有对其中的很重要的一行代码进行解释:那就是
servlet.init(facade);这是对servlet进行初始化。
protected StandardWrapperFacade facade = new StandardWrapperFacade(this);其实facade是对StandardWrapper的一个封装类的实例,而且它也实现了ServletConfig接口,其实servlet.init()中可以传入本身的servlet.init(this),为何要有这个封装类呢?
StandardWrapperFacade其实是非常简单的。只有几个方法而已,而且还是通过它封装的StandardWrapper来实现的。为了防止开发人员用到不必要的public方法tomcat采用了这个封装类。
以上就是如何得到一个servlet实例的具体情况。也许你已经忘了standardWrapperValve的invoke方法中的第二个作用了:执行过滤器链。
filterChain.doFilter(request.getRequest(), response.getResponse());
先来看看ApplicationFilterChain的内部基础结构:
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];这就是过滤器数组,里面保存的是每个过滤器,也许你会觉得ApplicationFilterConfig[0]很奇怪,那么来看看如何加入过滤器,这样你肯定不奇怪啦!
void addFilter(ApplicationFilterConfig filterConfig) { if (n == filters.length) { ApplicationFilterConfig[] newFilters = new ApplicationFilterConfig[n + INCREMENT]; System.arraycopy(filters, 0, newFilters, 0, n); filters = newFilters; } filters[n++] = filterConfig; }其实在tomcat内部很多地方都是这样的。
毫无疑问过滤器是ApplicationFilterConfig类型的,
ApplicationFilterConfig(Context context, FilterDef filterDef)org.apache.catalina.deploy.FilterDef内部在这里就不分析了,它就是一个过滤器的定义。准备工作做完了,接下来就是doFilter方法了。
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; try { java.security.AccessController.doPrivileged( new java.security.PrivilegedExceptionAction() { public Object run() throws ServletException, IOException { internalDoFilter(req,res); return null; } } ); } catch( PrivilegedActionException pe) { Exception e = pe.getException(); if (e instanceof ServletException) throw (ServletException) e; else if (e instanceof IOException) throw (IOException) e; else if (e instanceof RuntimeException) throw (RuntimeException) e; else throw new ServletException(e.getMessage(), e); } } else { internalDoFilter(request,response); } }
........此处省略部分代码.............. private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // Call the next filter if there is one if (pos < n) { ApplicationFilterConfig filterConfig = filters[pos++]; Filter filter = null; try { filter = filterConfig.getFilter(); support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT, filter, request, response); if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res, this}; SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal); args = null; } else { filter.doFilter(request, response, this); } support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response); } ........此处省略部分代码.............. return; } // We fell off the end of the chain -- call the servlet instance try { if (Globals.STRICT_SERVLET_COMPLIANCE) { lastServicedRequest.set(request); lastServicedResponse.set(response); } support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT, servlet, request, response); if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) { if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res}; SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal); args = null; } else { servlet.service((HttpServletRequest) request, (HttpServletResponse) response); } } else { servlet.service(request, response); } support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response); } ........此处省略部分代码.............. }
过滤器链在调用完了过滤器的doFilter之后就会调用servlet的service方法。在每个过滤器的doFilter中我们可以这样实现:
public void doFilter(ServletRequest request, ServletResponse response ,FilterChain chain) { //...do something..... chain.doFilter(request,response,chain); }现在servlet的创建和执行过程都分析完成了。我用一个图来概括这个过程: