基础
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。
@Override
public 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;
来到子类的初始化方法
@Override
protected 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 it
wac = 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, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.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 id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = 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方法就会发现:
@Override
protected 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> {
@Override
public 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.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.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.HandlerFunctionAdapter
org.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.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.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