wrapper是什么
wrapper是包装了一个应用实现Servlet类的容器的包装类,管理了这个Servlet的实例化和,初始化,调用及销毁。
介绍Wrapper之前我要先说明一下我要过滤掉的几个比较重要的内容
1、SingleThreadMode
实现了这个类的Servlet,tomcat保证不会有两个线程同时调用同一个该servlet实例,现在已经弃用,因其有误导性,导致很多人以为这个是线程安全的,
wrapper中涉及这一块的我会去掉
2、Jsp
这块的处理目前也不会在今天的文章中讲解,我也会处理掉
wrapper怎么工作
我们假设wrapper完全正常启动,我们根据之前管道的内容,我们需要找到StandardWrapper中的基础阀StandardWrapperValve的invoke
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// 这个变量类型是volatile,这样子使用在并发编程中是不安全的不是原子操作,在之后的
//版本中改为atomicLong,当然这个在这里不重要,与我们的流程无关
requestCount++;
//获取Wrapper容器
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
//获取父Context容器
Context context = (Context) wrapper.getParent();
// Allocate a servlet instance to process this request
try {
if (!unavailable) {
// 获取Servlet实例,实例化过一次之后,保存在Wrapper的instance中
servlet = wrapper.allocate();
}
}
// Acknowledge the request
try {
response.sendAcknowledgement();
}
request.setAttribute
(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
ApplicationFilterFactory.REQUEST_INTEGER);
request.setAttribute
(ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
requestPathMB);
// Create the filter chain for this request
ApplicationFilterFactory factory =
ApplicationFilterFactory.getInstance();
// 创建过滤器
ApplicationFilterChain filterChain =
factory.createFilterChain(request, wrapper, servlet);
// Reset comet flag value after creating the filter chain
request.setComet(false);
// 调用过滤器和servlet
filterChain.doFilter
(request.getRequest(), response.getResponse()
// Release the filter chain (if any) for this request
if (filterChain != null) {
if (request.isComet()) {
// If this is a Comet request, then the same chain will be used for the
// processing of all subsequent events.
filterChain.reuse();
} else {
filterChain.release();
}
}
// 这里其实没有做啥
try {
if (servlet != null) {
wrapper.deallocate(servlet);
}
}
}
这个阀主要做了两个我们需要关注的重要工作
1、 创建容器实例
2、 调用实例的service,在这里是放在过滤器中处理的,先执行所有过滤,再调用service。
我们是否应该有个问题,Servlet的实例类并不在tomcat这个系统中,tomcat是怎么加载的
我们首先想到的是不是反射,我们来看看tomcat是怎么加载Servlet实现类的
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;
// 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 (!newInstance) {
countAllocated.incrementAndGet();
}
return (instance);
}
}
}
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 {
// 获取容器中servletClass全限定名
String actualClass = servletClass;
// 获取该容器的加载器,整个容器的加载器会找一篇来讲解
Loader loader = getLoader();
// 获取类加载器
ClassLoader classLoader = loader.getClassLoader();
Class classClass = null;
//加载类对象
if (classLoader != null) {
classClass = classLoader.loadClass(actualClass);
} else {
classClass = Class.forName(actualClass);
}
//实例化servlet实例
servlet = (Servlet) classClass.newInstance();
// 第一次创建servlet的时候会执行一次init,所以servlet的初始化仅执行一次
servlet.init(facade);
return servlet;
}
这块代码我们知道为什么servlet的init为什么只执行一次,也就是servlet的类实例化之后是保存在wrapper容器中的。
也了解到我们tomcat的一个Servlet类,是单例的,所以存在并发安全问题。
这里基本上就是wrapper容器的功能了,下一篇讲解的容器是context