DispatchServlet初始化过程分析:
- 类的立体结构
整体继承类结构:
我个人理解的各个类及其作用:
Object所有类最终的父类,实现细节用javap -c指令得,java7是编译时发现没有父类编译器会直接后面添加extends Object,java8后测试发现并没有显式的添加,细节应该由JVM实现。回过头来看,继承Object类能得到底层这些native方法getClass();hashCode();notify();wait()对于DispatchServlet以及其他所有类来说,在反射、集合、多线程这几块用的比较多。
GenericServlet一个抽象类,抽象类更多的是一种描述,具体实现交给子类。对比servlet接口,他为子类多作的事情:基本的属性Servletconfig及初始参数获取方法,servise方法依然是无协议的ServletRequest形式(例如没有加http),增加无参数的init方法方便子类覆盖,增加一些log记录方法。(直接继承servlet初始基本流程:web容器(tomcat)通过web.xml配置或者注解获取ServletConfig对象参数,将ServletConfig传入init(servletconfig sc)方法得到一个servlet。
子类继承GenericServlet需要重写Service方法)
HttpServlet:重写service(ServletRequest,ServeltResponse)方法,仅仅只是将两个参数强转类型为HttpServletRequest/HttpServletResponse再调用自身的带Http的service方法;这个方法做的事:根据请求方法GET调用doget,POST调用doPost等。(其中doGet需要判断,lastModify=-1直接调用,请求信息中时间小于lastModify时间就更新,并把lastModify更新至响应,在调用doget,如果不小于,直接返回304浏览器直接取缓存)。doGet\doPost方法供子类重写,而不需要重写doService方法。doTrace主要将一些请求头信息写入响应(测试用?),doOption()主要借助getAllDeclareMethod(Class)方法响应头写入子类所有重写方法的信息(Allow属性)。
子类继承HttpServlet方法需要重写doXxx方法和自己需要init()方法,否则实例处理请求时将抛出异常。(后面的frameworkservlet重写了doXxx系列方法)
抽象类HttpServletBean 重写了init()方法,这个方法能将web.xml中init-param参数注入进该Servlet中。该方法主要源码:
//ServletConfigPropertyValues这个类是HttpservletBean的内部类(便于我自己理解,类比为<String,Object>linkedHashMap)追踪构造源码,取出config的initparams遍历,存储进ProperValues,另外在requiredProperties移除,最终检测扔有就抛出缺少必要属性异常。
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(),this.requiredProperties);
//把当前servlet对象进行包装
BeanWrapper bw =PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
//默认实现是空的
initBeanWrapper(bw);
//将属性设置入当前包装对象。意味着web.xml配置的属性init-param,此时已经设置入对象了。如果新建一个Servlet继承HttpServletBean,我的servlet也能直接用init-param设置属性(需要提供public 修饰的setXxx方法),单纯继承HttpServlet是不行的。
(过程其实远远没有看上去这么几行代码简单,再细细讨论已经是Spring的底层了。太多)
bw.setPropertyValues(pvs, true);
继承HttpServletBean按照需要可重写方法:initServletBean 设置bean属性值
FrameworkServlet :框架相关的Servlet,重写了inservletbean();该方法源代码:
try {
//初始容器
this.webApplicationContext = initWebApplicationContext();
//该默认空交给子类实现
initFrameworkServlet();
}
继续追踪initWebApplicationContext();
protected WebApplicationContext initWebApplicationContext() {
//这句代码可以简单看成=servletContext.getAttr(webappName + ".ROOT")
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
//发现已经有了web上下文,直接拿来用
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
//如果上下文不是当前活动的
if (cwac.getParent() == null) {
//父类没有注入,将Spring 的rootContext注入为父类
cwac.setParent(rootContext);
}
//并刷新Spring上下文
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
//简化看成:servletContext.getattr(contextAttribute);
wac = findWebApplicationContext();
}
if (wac == null) {
//没找到,设置XmlWebApplicationContext为当前上下文设置父上下文并刷新
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
//如果上下文没有被刷新过,调用子类刷新。(该方法由子类实现)
onRefresh(wac);
}
if (this.publishContext) {
// 发布此上下文作为Servlet上下文的一个属性
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
FrameworkServlet和Spring框架紧紧相关,会将Spring上下文设置为父上下文,将自身上下文设置为Servlet上下文的一个属性。另外注意,这个类将doGet/doPost方法重写,最终都调用了抽象doServie()方法,交给子类实现。
继承这个类时需要注意重写doService/按自身需要onRefresh两个方法
DispatchServlet的onRefresh方法:
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
//此时做自身的初始设置即可,文章有点长,我打算下面每个方法都写一点
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}