Tomcat源码解析(一):wrapper

    xiaoxiao2025-04-22  14

    Wrapper的作用

    StandardWrapper同StandardContext一样,继承自ContainerBase,Wrapper表示了一个servlet定义,一个servlet类对应一个wrapper。wrapper的parent container必须是context,而wrapper不能有child container。 定义如下:

    public final class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper { // ----------------------------------------------------------- Constructors /** * Create a new StandardWrapper component with the default basic Valve. */ public StandardWrapper() { super(); pipeline.setBasic(new StandardWrapperValve()); }

    一个servletClass对应一个wrapper。 ContainerBase中用HashMap存储children,以child的名字为键。

    /** * The child Containers belonging to this Container, keyed by name. */ protected HashMap children = new HashMap(); public void addChild(Container child) { if (System.getSecurityManager() != null) { PrivilegedAction dp = new PrivilegedAddChild(child); AccessController.doPrivileged(dp); } else { addChildInternal(child); } } private void addChildInternal(Container child) { synchronized(children) { if (children.get(child.getName()) != null) throw new IllegalArgumentException("addChild: Child name '" + child.getName() + "' is not unique"); child.setParent((Container) this); // May throw IAE //添加的同时,执行start() if (started && (child instanceof Lifecycle)) { try { ((Lifecycle) child).start(); } catch (LifecycleException e) { log("ContainerBase.addChild: start: ", e); throw new IllegalStateException ("ContainerBase.addChild: start: " + e); } } children.put(child.getName(), child); fireContainerEvent(ADD_CHILD_EVENT, child); } }

    主流程

    请求进入后,context利用mapper匹配request url对应的wrapper,交给classloader来load wrapper,然后调用wrapper的invoke()方法。wrapper也是一个pipeline,拿到请求后,交给valve即StandardWrapperValve处理。 StandardWrapperValve.invoke()的主要逻辑如下。

    创建servlet实例。一个wrapper对应一个servletclass。wrapper需要根据servlet是否实现SingleThreadModel进行不同的处理。response.sendAcknowledgement()告知request已收到。具体怎么做的呢??创建一个filterchain过滤器链,filterchain对象中包含成员servlet,表示正在处理的servlet。创建filterchain,就是根据servletname匹配对应的filter,加入到链中。调用filterchain.filter()。filter方法调用完过滤器后,调用servlet.service()方法,执行业务逻辑。 // Allocate a servlet instance to process this request try { if (!unavailable) { servlet = wrapper.allocate(); } } catch (ServletException e) { log(sm.getString("standardWrapper.allocateException", wrapper.getName()), e); throwable = e; exception(request, response, e); servlet = null; } catch (Throwable e) { log(sm.getString("standardWrapper.allocateException", wrapper.getName()), e); throwable = e; exception(request, response, e); servlet = null; } // Acknowlege the request try { response.sendAcknowledgement(); } catch (IOException e) { sreq.removeAttribute(Globals.JSP_FILE_ATTR); log(sm.getString("standardWrapper.acknowledgeException", wrapper.getName()), e); throwable = e; exception(request, response, e); } catch (Throwable e) { log(sm.getString("standardWrapper.acknowledgeException", wrapper.getName()), e); throwable = e; exception(request, response, e); servlet = null; } // Create the filter chain for this request ApplicationFilterChain filterChain = createFilterChain(request, servlet); // Call the filter chain for this request // NOTE: This also calls the servlet's service() method try { String jspFile = wrapper.getJspFile(); 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.removeAttribute(Globals.JSP_FILE_ATTR); } //省略异常处理和调用结束后清理现场的代码

    wrapper创建servlet的代码如下。如果servlet没有实现SingleThreadModel,直接获取并返回servlet单例即可。

    如果实现了SingleThreadModel,用一个实例池来控制servlet对象。也就是,实现了SingleThreadModel的servlet,在tomcat中并不是单例的。countAllocated表示该servletclass当前正在服务的servlet实例个数,servlet服务完成后,deallocate(Servlet servlet)方法将servlet放回池中,并将countAllocated减1。nInstances表示创建的servlet实例总个数,实例池中的实例个数+countAllocated=nInstances。所以countAllocated>=nInstances,表示当前实例池中没有闲着的实例,需要创建新的servlet实例了。

    // 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 { instance = loadServlet(); } catch (ServletException e) { throw e; } catch (Throwable e) { throw new ServletException (sm.getString("standardWrapper.allocate"), e); } } } } if (!singleThreadModel) { if (debug >= 2) log(" Returning non-STM instance"); countAllocated++; return (instance); } } synchronized (instancePool) { while (countAllocated >= 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 (debug >= 2) log(" Returning allocated STM instance"); countAllocated++; return (Servlet) instancePool.pop(); }

    servlet服务完成后,deallocate(Servlet servlet)负责将servlet实例放回到实例池中,同时countAllocated减一。

    public void deallocate(Servlet servlet) throws ServletException { // If not SingleThreadModel, no action is required if (!singleThreadModel) { countAllocated--; return; } // Unlock and free this instance synchronized (instancePool) { countAllocated--; instancePool.push(servlet); instancePool.notify(); } }

    servlet.init()

    StandardWrapper还实现了ServletConfig接口。在load servle中,创建了servlet实例后,会调用servlet.init(ServletConfig)对servlet进行初始化,这时传入的不是StandardWrapper实例本身servlet.init(facade),而是传入它的包装类对象StandardWrapperFacade。facade定义如下:

    private StandardWrapperFacade facade = new StandardWrapperFacade(this);

    StandardWrapperFacade的作用是什么呢?StandardWrapper的public方法较多,大部分都不是servlet中应该用到的,如果传给servlet,可能会被误用。所以用StandardWrapperFacade包装起来,只提供必须的一些方法。

    public final class StandardWrapperFacade implements ServletConfig { /** * Create a new facede around a StandardWrapper. */ public StandardWrapperFacade(StandardWrapper config) { super(); this.config = (ServletConfig) config; } // ----------------------------------------------------- Instance Variables /** * Wrapped config. */ private ServletConfig config = null; // -------------------------------------------------- ServletConfig Methods public String getServletName() { return config.getServletName(); } public ServletContext getServletContext() { ServletContext theContext = config.getServletContext(); if ((theContext != null) && (theContext instanceof ApplicationContext)) theContext = ((ApplicationContext) theContext).getFacade(); return (theContext); }

    filter

    tomcat可以配置过滤器,在servlet调用前对请求进行预处理。我写了个简单的过滤器,试了一下过滤器功能。filter代码如下:

    /** * Created by zhangguixian on 8/1/16 */ public class HelloFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("init HelloFilter"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filter HelloFilter"); //执行完之后,显式调用chain的doFilter方法,执行剩余的过滤器 filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { System.out.println("destroy HelloFilter"); } }

    配置filter的代码:

    Context context = new StandardContext(); // StandardContext's start method adds a default mapper context.setPath("/myApp"); context.setDocBase("myApp"); LifecycleListener listener = new SimpleContextConfig(); ((Lifecycle) context).addLifecycleListener(listener); context.addChild(wrapper1); context.addChild(wrapper2); //加filter FilterMap filterMap = new FilterMap(); filterMap.setFilterName("hello filter"); // filterMap.setServletName("HelloFilter"); filterMap.setURLPattern("/Modern"); FilterDef filterDef = new FilterDef(); filterDef.setFilterName(filterMap.getFilterName()); filterDef.setFilterClass("HelloFilter"); context.addFilterDef(filterDef); context.addFilterMap(filterMap);

    总结

    wrapper功能看起来简单,但结构同context一样,实现了container、pipeline、lifecycle,代码结构非常清晰,非常便于扩展。tomcat看了这么久,还是觉得设计的很美呢~~

    转载请注明原文地址: https://ju.6miu.com/read-1298333.html
    最新回复(0)