基础
Web项目启动完毕后,就开始处理web请求了,那么DispatcherServlet就该出场了。
DispatcherServlet是实现Servlet接口的实现类。(菜鸟教程:Servlet 教程)
我们可以在Spring项目中增加自己的Servlet处理。
通常我们直接继承HttpServlet即可。
```java
/**
- 自定义Servlet *
 @author lichlaughing */ public class MyServlet extends HttpServlet { private static final long serialVersionUID = 9211887734586730160L;
@Override protected void doGet(HttpServletRequest req
, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html");getServletContext().getRequestDispatcher("/WEB-INF/views/servlet-index.jsp").forward(req, resp);
} }
然后在web.xml中配置我们的servlet.```xml<servlet><servlet-name>myServlet</servlet-name><servlet-class>cn.lichenghao.servlet.MyServlet</servlet-class></servlet><servlet-mapping><servlet-name>myServlet</servlet-name><url-pattern>/my-index</url-pattern></servlet-mapping>
请求 http://localhost:8080/my-index 就能看到我们的 servlet-index.jsp 页面啦。
DispatcherServlet的操作和上面我们的MyServlet操作基本套路是一样的。
首先还是先看下其类的继承关系:
DispatcherServlet 的初始化
那么DispatcherServlet在什么时候初始化的?
web环境启动,会执行servlet的init方法。DispatcherServlet的init方法来到HttpServletBean。
@Overridepublic final void init() throws ServletException {// Set bean properties from init parameters.PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);if (!pvs.isEmpty()) {try {BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));initBeanWrapper(bw);bw.setPropertyValues(pvs, true);}catch (BeansException ex) {if (logger.isErrorEnabled()) {logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);}throw ex;}}// Let subclasses do whatever initialization they like.initServletBean();}
- 拿到init-param中配置的参数;
 - 将当前的bean转换成BeanWrapper,来进行属性的填充;
 - 然后交给子类去init;
 
来到子类的初始化方法
@Overrideprotected final void initServletBean() throws ServletException {getServletContext().log("Initializing Spring " + getClass().getSimpleName()+ " '" + getServletName() + "'");if (logger.isInfoEnabled()) {logger.info("Initializing Servlet '" + getServletName() + "'");}long startTime = System.currentTimeMillis();try {this.webApplicationContext = initWebApplicationContext();initFrameworkServlet();}catch (ServletException | RuntimeException ex) {logger.error("Context initialization failed", ex);throw ex;}if (logger.isDebugEnabled()) {String value = this.enableLoggingRequestDetails ?"shown which may lead to unsafe logging of potentially sensitive data" :"masked to prevent unsafe logging of potentially sensitive data";logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +"': request parameters and headers will be " + value);}if (logger.isInfoEnabled()) {logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");}}
这里主要有两个方法,initWebApplicationContext 初始化上下文,另一个是initFrameworkServlet 交给子类扩展。
所以来看:
protected WebApplicationContext initWebApplicationContext() {WebApplicationContext rootContext= WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {// A context instance was injected at construction time -> use itwac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;if (!cwac.isActive()) {// The context has not yet been refreshed -> provide services such as// setting the parent context, setting the application context id, etcif (cwac.getParent() == null) {// The context instance was injected without an explicit parent -> set// the root application context (if any; may be null) as the parentcwac.setParent(rootContext);}configureAndRefreshWebApplicationContext(cwac);}}}if (wac == null) {// No context instance was injected at construction time -> see if one// has been registered in the servlet context. If one exists, it is assumed// that the parent context (if any) has already been set and that the// user has performed any initialization such as setting the context idwac = findWebApplicationContext();}if (wac == null) {// No context instance is defined for this servlet -> create a local onewac = createWebApplicationContext(rootContext);}if (!this.refreshEventReceived) {// Either the context is not a ConfigurableApplicationContext with refresh// support or the context injected at construction time had already been// refreshed -> trigger initial onRefresh manually here.synchronized (this.onRefreshMonitor) {onRefresh(wac);}}if (this.publishContext) {// Publish the context as a servlet context attribute.String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac;}
到这里其实有点懵,因为这里也要initWebApplicationContext,在前面看到ContextLoader中也要initWebApplicationContext,这个重复工作?
确实这两个操作几乎是相同的,只不过他们关联了起来,变成了父子关系。
- 首先他获取到了rootContext,其实就是ContextLoaderListener中创建的WebApplicationContext;
 - 紧接着创建自己的WebApplicationContext,并且把上面获取到的rootContext作为父上下文;
 
DispatcherServlet创建自己的WebApplicationContext。
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {...ConfigurableWebApplicationContext wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);wac.setEnvironment(getEnvironment());// 设置父上下文wac.setParent(parent);...return wac;}
好了到这里一个基本的DispatcherServlet对象就创建出来了,但是还是个雏形,因为http能处理各种各样的请求,那就需要请求映射器,还能使用各种各样的渲染引擎,还能上传下载文件等等,这些东西好像都还没有安排上,那么在创建完基本对象后,来补充这些能力。
回到 initWebApplicationContext 方法中,当创建完毕后,有这样一块代码:
if (!this.refreshEventReceived) {// Either the context is not a ConfigurableApplicationContext with refresh// support or the context injected at construction time had already been// refreshed -> trigger initial onRefresh manually here.synchronized (this.onRefreshMonitor) {onRefresh(wac);}}
跟进onRefresh方法就会发现:
@Overrideprotected void onRefresh(ApplicationContext context) {initStrategies(context);}/*** Initialize the strategy objects that this servlet uses.* <p>May be overridden in subclasses in order to initialize further strategy objects.*/protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);}
这块逻辑正是我们上面说的,补充各种各样的能力的地方。
这块等下再说,通过变量可以猜测出refreshEventReceived这是个事件监听的操作,如果没有监听到这个事件那么就刷新操作。
那也就意味着有地方有事件监听器。如下所示:
FrameworkServlet的内部类,实现了监听器。
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {FrameworkServlet.this.onApplicationEvent(event);}}
这就明确了,监听到事件就执行刷新方法,同时变量refreshEventReceived更新为true,那么就不用手动触发。否则就手动触发。
initHandlerMappings
来看处理器映射器是如何初始化的,这个可以看成是请求url和对应Controller的映射。
DispatcherServlet接收到请求后交给handlermapping,它去匹配到对应的Controller给DispatcherServlet。
private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;if (this.detectAllHandlerMappings) {// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.Map<String, HandlerMapping> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList<>(matchingBeans.values());// We keep HandlerMappings in sorted order.AnnotationAwareOrderComparator.sort(this.handlerMappings);}} else {try {HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);this.handlerMappings = Collections.singletonList(hm);} catch (NoSuchBeanDefinitionException ex) {// Ignore, we'll add a default HandlerMapping later.}}// Ensure we have at least one HandlerMapping, by registering// a default HandlerMapping if no other mappings are found.if (this.handlerMappings == null) {this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);if (logger.isTraceEnabled()) {logger.trace("No HandlerMappings declared for servlet '" + getServletName() +"': using default strategies from DispatcherServlet.properties");}}}
默认情况下会加载Applicontext下所有实现HandlerMapping接口的bean(会有多个),否则只加载名称为handlerMapping的bean(只有一个)。
如果依然没有找到HandlerMapping那么就根据默认策略实例化一个默认的。
<init-pararn><param-name>detectAllHandlerMappings</pararm-name><param-value>false</param-value> <!-- 只加载名称为handlerMapping的bean --></init-pararm>
这个默认策略是在静态块中初始化的,加载了一个配置文件 DispatcherServlet.properties .
static {// Load default strategy implementations from properties file.// This is currently strictly internal and not meant to be customized// by application developers.try {ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);} catch (IOException ex) {throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());}}
配置文件
# Default implementation classes for DispatcherServlet's strategy interfaces.# Used as fallback when no matching beans are found in the DispatcherServlet context.# Not meant to be customized by application developers.org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolverorg.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\org.springframework.web.servlet.function.support.RouterFunctionMappingorg.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\org.springframework.web.servlet.function.support.HandlerFunctionAdapterorg.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
这里有个bean需要关注下:RequestMappingHandlerMapping 在controller和方法上的注解@RequestMapping就是由它处理的。
initHandlerAdapters
初始化请求适配器。初始化逻辑和initHandlerMappings基本是一样的。同样可以使用参数detectAllHandlerAdapters来配置是否加载所有。
<init-param><param-name>detectAllHandlerAdapters</param-name><param-value>false</param-value></init-param>
同样如果没有匹配到,那么系统会根据默认策略生成默认的处理器适配器。
DispatcherServlet 的请求处理
XX
