Tomcat学习笔记
Posted 92#
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat学习笔记相关的知识,希望对你有一定的参考价值。
StandardWrapper容器
Context容器包含一个或者多个Wrapper实例,每个Wrapper实例表示一个具体的servlet定义。
方法调用序列
具体过程
(1)连接器创建request和response对象
(2)连接器调用StandardContext实例的invoke()方法
(3)接着,StandardContext实例的invoke方法调用其管道对象的invoke方法。StandardContext中管道对象的基础阀是StandContextValve类的实例,因此,StandardContext的管道对象会调用StandardContextValve实例的invoke方法。
(4)StandardContextValve实例的invoke方法获取相应的Wrapper实例处理HTTP请求,调用Wrapper实例的invoke的方法。
(5)StandardWrapper类是Wrapper接口的标准实现,StandardWrapper实例的invoke方法会调用其管道对象的invoke方法。
(6)StandardWrapper流水线的基本阀门时StandardWrapperValve。因此StandardWrapperValve的invoke方法会被调用。StandardWrapperValve的invoke方法会调用包装器的allocate方法获得一个servlet的实例。
(7)当一个servlet需要被加载的时候,方法allocate调用方法load来加载一个servlet。
(8)方法load会调用servlet的init方法。
(9)StandardWrapperValve调用servlet实例的service方法。
1. StandardWrapper对象主要任务加载servlet类,并进行实例。但是StandardWrapper并不调用servlet的service方法。StandardWrapperValve对象通过调用allocate()方法从StandardWrapper实例中获取servlet实例。在获取得了servlet实例后,StandardWrapperValve实例就会调用servlet实例的service方法。
粗略流程如下:
分配Servlet实例
StandardWrapperValve实例的invoke方法调用Wrapper实例的allocate方法获取请求的servlet的一个实例,因此StandardWrapper类的实现allocate方法。
StandardWrapperValve#invoke
public final void invoke(Request request, Response response) throws IOException, ServletException { //...省略 // Allocate a servlet instance to process this request try { if (!unavailable) { servlet = wrapper.allocate(); } } catch (UnavailableException e) { //...省略
StandardWrapper#allocate
/** * Stack containing the STM instances. */ protected Stack<Servlet> instancePool = null; 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();//加载servlet实例 if (!singleThreadModel) { // For non-STM, increment here to prevent a race // condition with unload. Bug 43683, test case // #3 newInstance = true; //分配计数 countAllocated.incrementAndGet(); } } catch (ServletException e) { throw e; } catch (Throwable e) { ExceptionUtils.handleThrowable(e); throw new ServletException (sm.getString("standardWrapper.allocate"), e); } } } } if (!instanceInitialized) { initServlet(instance);//初始化servlet->servlet.init() } if (singleThreadModel) { if (newInstance) { // Have to do this outside of the sync above to prevent a // possible deadlock synchronized (instancePool) { instancePool.push(instance); nInstances++; } } } else { 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) { ExceptionUtils.handleThrowable(e); throw new ServletException (sm.getString("standardWrapper.allocate"), e); } } else { try { instancePool.wait(); } catch (InterruptedException e) { // Ignore } } } if (log.isTraceEnabled()) log.trace(" Returning allocated STM instance"); countAllocated.incrementAndGet(); return instancePool.pop();//弹出栈顶servlet } }
加载Servlet
在StandardWrapper#loadServlet()方法,先检查是否已经加载过servlet
if (!singleThreadModel && (instance != null)) return instance;
StandardContext获取实例管理器,并通过实例管理器获取servlet实例
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
servlet = (Servlet) instanceManager.newInstance(servletClass);
初始化servlet,并触发容器加载监听事件。
initServlet(servlet); fireContainerEvent("load", this);
最后,返回servlet
return servlet;
2.ServletConfig类
在servlet调用init方法,要传入ServletConfig参数,ServletConfig参数的来源,由于StandardWrapper不仅实现了Wrapper接口,还实现了javax.servlet.ServletConfig接口,在ServletConfig接口中的方法如下:
public interface ServletConfig { public String getServletName();//返回servlet名字 public ServletContext getServletContext();//返回servlet容器 public String getInitParameter(String name);//返回初始化参数 public Enumeration<String> getInitParameterNames(); }
StandardWrapper类并不是将自身传递给servlet#init方法,它会在一个StandardWrapperFacade实例中包装自生,将其大部分的公共方法对servlet程序员隐藏。
/** * The facade associated with this wrapper. */ protected StandardWrapperFacade facade = new StandardWrapperFacade(this);
在StandardWrapper中方法实现
public ServletContext getServletContext() { if (parent == null) return (null); else if (!(parent instanceof Context)) return (null); else return (((Context) parent).getServletContext()); } protected HashMap<String, String> parameters = new HashMap<String, String>(); /** * Return the name of this servlet. */ @Override public String getServletName() { return (getName()); } public String getInitParameter(String name) { return (findInitParameter(name)); } /** * Return the set of initialization parameter names defined for this * servlet. If none are defined, an empty Enumeration is returned. */ @Override public Enumeration<String> getInitParameterNames() { try { parametersLock.readLock().lock(); return Collections.enumeration(parameters.keySet()); } finally { parametersLock.readLock().unlock(); } } //初始化参数 public void addInitParameter(String name, String value) { try { parametersLock.writeLock().lock(); parameters.put(name, value); } finally { parametersLock.writeLock().unlock(); } fireContainerEvent("addInitParameter", name); }
3.Servlet容器的父子关系。
Wrapper实例代表一个servlet实例,因此Wrapper实例不在有子容器,不应该在调用其addChild方法,否则抛出IllegalStateException异常。下面是addChild方法。
public void addChild(Container child) { throw new IllegalStateException (sm.getString("standardWrapper.notChild")); }
Wrapper的父容器只能是Context类的实现,若是在调用Wrapper实例的setParent方法时,传入了一个非Context类型的容器,则会抛出IllegalStateException异常。
public void setParent(Container container) { if ((container != null) && !(container instanceof Context)) throw new IllegalArgumentException (sm.getString("standardWrapper.notContext")); if (container instanceof StandardContext) { swallowOutput = ((StandardContext)container).getSwallowOutput(); unloadDelay = ((StandardContext)container).getUnloadDelay(); } super.setParent(container); }
4.StandardWrapperFacade类
StandardWrapper实例会调用它所载入的servlet类的实例的init()方法。init()方法需要一个javax.servlet.ServletConfig实例,而StandardWrapper类本身实现了javax.servlet.ServletConfig接口,所以,理论上StandardWrapper对象可以将自己传入init()方法。但是StandardWrapper需要将其大部分公共方法对servlet程序员隐藏起来。为了实现在这个目的,StandWrapper类将自身包装成StandardWrapperFacade类的一个实例。关系如下。
(1)在StandardWrapper类中,将自身包装成StandardWrapperFacade。
/** * The facade associated with this wrapper. */ protected StandardWrapperFacade facade = new StandardWrapperFacade(this);
(2)StandardWrapperFacade中的构造方法。
public StandardWrapperFacade(StandardWrapper config) { super(); this.config = config; } //通过包装类型调用方法 public String getInitParameter(String name) { return config.getInitParameter(name); } public ServletContext getServletContext() { if (context == null) { context = config.getServletContext(); if ((context != null) && (context instanceof ApplicationContext)) context = ((ApplicationContext) context).getFacade(); } return (context); } public String getServletName() { return config.getServletName(); }
这里采用装饰模式。
5.StandardWrapperValve类
StandardWrapperValve是StandardWrapper中的基础阀门,主要完成2个操作。
(1)执行与该servlet实例关联的全部过滤器,
(2)调用servlet实例的service方法。
在StandardWrapperValve中invoke方法执行的步骤。
(1)调用StandardWrapper实例的allocate方法获取该StandardWrapper实例所表示的servlet的实例;
(2)调用私有方法createFilterChain,创建过滤器链;
(3)调用过滤链的doFilter方法,其中包括调用servlet实例的service方法;
(4)释放过滤链;
(5)调用Wrapper实例的deallocate方法;
(6)若该servlet类再也不会用到,将会调用Wrapper实例的unload方法。
6.ApplicationFilterChain类
ApplicationFilterChain类实现了FilterChain接口,StandardWrapperValve类的invoke方法会创建ApplicationFilterChain类的一个实例,并调用其doFilter方法,doFilter方法会调用第一个过滤器的doFilter方法。直到最后一个过滤器,则会调用被请求的servlet类的service方法。
总结:
通过StandWrapper的学习,大致的了解了Servlet的从加载,初始化,service方法,到卸载的整个流程。另外还有就是单例模式,监听事件,装饰模式的设计思想的了解。
以上是关于Tomcat学习笔记的主要内容,如果未能解决你的问题,请参考以下文章
在Tomcat的安装目录下conf目录下的server.xml文件中增加一个xml代码片段,该代码片段中每个属性的含义与用途