1、关于Mapper的补充
对于Mapper,其有一个默认实现为StandardContextMapper
image.png
在StandardContext的start()方法中,这个默认的Mapper通过addDefaultMapper()方法会被初始化,然后通过Container的addMapper()方法加入到Container中。
private String mapperClass =
"org.apache.catalina.core.StandardContextMapper";
// StandardContext.addDefaultMapper
protected void addDefaultMapper(String mapperClass) {
super.addDefaultMapper(this.mapperClass);
}
// ContainerBase.addDefaultMapper
protected void addDefaultMapper(String mapperClass) {
// Do we need a default Mapper?
if (mapperClass == null)
return;
if (mappers.size() >= 1)
return;
// Instantiate and add a default Mapper
try {
Class clazz = Class.forName(mapperClass);
Mapper mapper = (Mapper) clazz.newInstance();
// 默认Mapper协议为HTTP
mapper.setProtocol("http");
addMapper(mapper);
} catch (Exception e) {
log(sm.getString("containerBase.addDefaultMapper", mapperClass),
e);
}
}
2、关于SingleThreadModel
对于一个实现了SingleThreadModel接口的servlet,其目的是保证servlet 一次只能有一个请求,也即保证不会有两个线程同是使用 servlet
的 service 方法,该接口并不能避免同步而产生的问题,如访问静态
类变量或该 servlet 以外的类或变量。
3、StandardWrapper处理过程
3、1 Context把请求交给Wrapper过程
在tomcat中,Wrapper容器的父容器为Context,Context的默认实现为StandardContext,其Pipeline的basic Valve为StandardContextValve,当一个请求被Context的invoke方法接收到的时候,根据Pipeline的处理流程,最终交给了StandardContextValve的invoke(Request request, Response response,ValveContext valveContext)方法来处理,而StandardContextValve是通过Context.map()方法来找到对应的Wrapper,Context如何找Wrapper这里略去,比较简单,主要看看Wrapper处理请求的过程,在tomcat中,Wrapper的默认实现为StandardWrapper,其Pipeline的basic Valve为StandardWrapperValve,这里就从这里开始分析:
public void invoke(Request request, Response response,
ValveContext valveContext){
StandardWrapper wrapper = (StandardWrapper) getContainer();
ServletRequest sreq = request.getRequest();
ServletResponse sres = response.getResponse();
try {
if (!unavailable) {
// 这里通过allocate()方法获取到一个Servlet实例
servlet = wrapper.allocate();
}
} catch (ServletException e) {
}
//......
}
3、2 StandardWrapper.allocate()
public Servlet allocate() throws ServletException {
// If not SingleThreadedModel, return the same instance every time
if (!singleThreadModel) {
// 如果Servlet没有实现SingleThreadModel,返回的是Servlet单例
// 对于非SingleThreadModel的Servlet,在整个tomcat中只会有一个实例来处理客户端的请求,
// 也就是说多个线程共享一个Servlet实例,这个要考虑资源共享的问题
// 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);
}
}
// 而对于SingleThreadModel,tomcat为了提高并发问题,
// 创建了多个Servlet实例来处理请求,实例保存在一个Stack中。
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) {
;
}
}
}
countAllocated++;
return (Servlet) instancePool.pop();
}
}
不管是SingleThreadModel的Servlet还是非SingleThreadModel的Servlet,其创建的方法都为loadServlet()
3、3 StandardWrapper.loadServlet()
public synchronized Servlet loadServlet() throws ServletException {
// 因为jsp最终是被tomcat编译成class文件的,tomcat也需要能处理JSP文件
String actualClass = servletClass;
if ((actualClass == null) && (jspFile != null)) {
Wrapper jspWrapper = (Wrapper)
((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
if (jspWrapper != null)
actualClass = jspWrapper.getServletClass();
}
// tomcat有自己的一套类加载机制,主要原因一是安全机制,
// tomcat只允许访问WEB-INF/classes下的servlet,
//二是tomcat要能够热加载servlet,即所谓热加载、动态部署
Loader loader = getLoader();// 得到一个Loader
// 再从loader中取得ClassLoader
ClassLoader classLoader = loader.getClassLoader();
// Special case class loader for a container provided servlet
if (isContainerProvidedServlet(actualClass)) {
// 查看目标是否为org.apache.catalina包下的Servlet或者是ContainerServlet子接口或实现
classLoader = this.getClass().getClassLoader();
log(sm.getString
("standardWrapper.containerServlet", getName()));
}
Class classClass = null;
if (classLoader != null) {
System.out.println("Using classLoader.loadClass");
classClass = classLoader.loadClass(actualClass);
} else {
System.out.println("Using forName");
classClass = Class.forName(actualClass);
}
servlet = (Servlet) classClass.newInstance();
// 初始化操作
// class StandardWrapperFacade implements ServletConfig
// StandardWrapperFacade facade = new StandardWrapperFacade(this);
// public StandardWrapperFacade(StandardWrapper config){}
// 这里传的是包装类,使得servlet只能访问ServletConfig中的方法
servlet.init(facade);
// Invoke jspInit on JSP pages
if ((loadOnStartup > 0) && (jspFile != null)) {
// 如果是JSP文件,并且loadOnStartup 大于0,则立马就调用了service方法
// Invoking jspInit
HttpRequestBase req = new HttpRequestBase();
HttpResponseBase res = new HttpResponseBase();
req.setServletPath(jspFile);
req.setQueryString("jsp_precompile=true");
servlet.service(req, res);
}
// 如果是SingleThreadModel,则初始化一个Stack,用来存放servlet实例
singleThreadModel = servlet instanceof SingleThreadModel;
if (singleThreadModel) {
if (instancePool == null){
instancePool = new Stack();
}
}
}
3、4 ServletConfig对象
在servlet初始化的时候,其需要一个参数为ServletConfig,先看下类图:
image.png
StandardWrapperFacade是一个包装类,内部对于ServletConfig的实现由StandardWrapper来完成,这里只看一下StandardWrapper是如何获取ServletContext 的:
protected Container parent = null;
public ServletContext getServletContext() {
if (parent == null)
return (null);
else if (!(parent instanceof Context))
return (null);
else
return (((Context) parent).getServletContext());
}
servlet运行的上下文环境由Context创建,从这里可以看到,一个单独的wrapper是不能单独部署的,单独部署的Wrapper是没法获取到servletcontext的。在StandardContext中的getServletContext方法中创建了servletcontext。
public ServletContext getServletContext() {
if (context == null){
context = new ApplicationContext(getBasePath(), this);
}
return (context);
}
3、5 StandardWrapperValve后续处理流程
在获取到servlet实例后,StandardWrapperValve继续对请求进行相关处理
// StandardWrapperValve.invoke()
// Create the filter chain for this request
ApplicationFilterChain filterChain = createFilterChain(request, servlet);
在分析ApplicationFilterChain的创建过程之前,先了解下filter关联的类图
image.png
private ApplicationFilterChain createFilterChain(Request request, Servlet servlet) {
// If there is no servlet to execute, return null
if (servlet == null)
return (null);
// Create and initialize a filter chain object
// 构建了一个filterChain,
ApplicationFilterChain filterChain = new ApplicationFilterChain();
// 把servlet设置到filterChain中
filterChain.setServlet(servlet);
//
StandardWrapper wrapper = (StandardWrapper) getContainer();
filterChain.setSupport(wrapper.getInstanceSupport());
// Acquire the filter mappings for this Context
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// If there are no filter mappings, we are done
if ((filterMaps == null) || (filterMaps.length == 0)){
return (filterChain);
}
// Acquire the information we will need to match filter mappings
String requestPath = null;
if (request instanceof HttpRequest) {
HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
String contextPath = hreq.getContextPath();
if (contextPath == null){
contextPath = "";
}
String requestURI = ((HttpRequest) request).getDecodedRequestURI();
if (requestURI.length() >= contextPath.length()){
requestPath = requestURI.substring(contextPath.length());
}
}
String servletName = wrapper.getName();
int n = 0;
// Add the relevant path-mapped filters to this filter chain
// 根据url-pattern去添加过滤器
for (int i = 0; i < filterMaps.length; i++) {
if (!matchFiltersURL(filterMaps[i], requestPath)){
continue;
}
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
continue;
}
filterChain.addFilter(filterConfig);
n++;
}
// Add filters that match on servlet name second
for (int i = 0; i < filterMaps.length; i++) {
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
continue;
}
filterChain.addFilter(filterConfig);
n++;
}
return (filterChain);
}
这个构建ApplicationFilterChain的逻辑还是比较简单的,主要看这句:
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
我们在web.xml中配置的filter,如
<filter>
<filter-name>filterName</filter-name>
<filter-class>filterClass</filter-class>
</filter>
<filter-mapping>
<filter-name>filterName</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
都会被先转化为FilterDef和FilterMap,这些FilterDef和FilterMap被保存在Context中,在Context的启动过程中会被转化为ApplicationFilterConfig:
StandardContext.start():
public synchronized void start() throws LifecycleException {
if (ok) {
if (!filterStart())
ok = false;
}
}
StandardContext.filterStart():
public boolean filterStart() {
// Instantiate and record a FilterConfig for each defined filter
boolean ok = true;
synchronized (filterConfigs) {
filterConfigs.clear();
Iterator names = filterDefs.keySet().iterator();
while (names.hasNext()) {
String name = (String) names.next();
if (debug >= 1)
log(" Starting filter '" + name + "'");
ApplicationFilterConfig filterConfig = null;
try {
filterConfig = new ApplicationFilterConfig
(this, (FilterDef) filterDefs.get(name));
filterConfigs.put(name, filterConfig);
} catch (Throwable t) {
log(sm.getString("standardContext.filterStart", name), t);
ok = false;
}
}
}
return (ok);
}
所以StandardContext找FilterConfig的方式就比较简单了:
public FilterConfig findFilterConfig(String name) {
synchronized (filterConfigs) {
return ((FilterConfig) filterConfigs.get(name));
}
}
在获取到ApplicationFilterChain之后,StandardWrapperValve.invoke()中紧跟着就调用其doFilter方法,ApplicationFilterChain中doFilter最终调用了internalDoFilter()方法:
if ((servlet != null) && (filterChain != null)) {
filterChain.doFilter(sreq, sres);
}
ApplicationFilterChain.internalDoFilter()
private void internalDoFilter(ServletRequest request, ServletResponse response){
if (this.iterator == null){
this.iterator = filters.iterator();
}
// 如果还有拦截器
if (this.iterator.hasNext()) {
ApplicationFilterConfig filterConfig =
(ApplicationFilterConfig) iterator.next();
Filter filter = null;
filter = filterConfig.getFilter();
filter.doFilter(request, response, this);
return;
}
// 所有的拦截器都走完了,最后调用servlet的service()
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse)) {
servlet.service((HttpServletRequest) request,
(HttpServletResponse) response);
} else {
servlet.service(request, response);
}
}
从这段逻辑中可以看出,在自己定义拦截器的时候,如果最后没有调用ApplicationFilterChain.doFilter()方法,那后续所有的filter和servlet的service()都是没有机会被执行的,从而达到了拦截的目的。
网友评论