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看了这么久,还是觉得设计的很美呢~~