配置DispatcherServlet
Servlet3.0之后, Servlet可以被动态监测到,web.xml不再是必须的,一般的servlet可以使用@WebServlet,除了添加注解,也可编程配置。WebApplicationInitializer的实现类在应用部署的时候会被执行,可以再WebApplicationInitializer.onStartup中配置Spring的DispatcherServlet:
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext();
webApplicationContext.register(AppConfig.class);
DispatcherServlet dispatcherServlet = new DispatcherServlet(webApplicationContext);
ServletRegistration.Dynamic registration = servletContext.addServlet("springApp", dispatcherServlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
WebApplicationInitializer会被执行的原因是,从servlet3.0之后,Servlet容器会根据SPI机制,在Spring-web模块下的MATE-INF/services/javax.servlet.ServletContainerInitializer文件中,找到ServletContainerInitializer具体实现类:
//文件:MATE-INF/services/javax.servlet.ServletContainerInitializer
org.springframework.web.SpringServletContainerInitializer
ServletContainerInitializer只有一个方法,第一个参数将会是HandlesTypes注解声明将会处理的类型;在Spring中实现类就是SpringServletContainerInitializer,HandlesTypes声明的类型是WebApplicationInitializer, 所以在应用初始化的时候WebApplicationInitializer的实现类将会被执行,可以在其中配置servlet、filter等等。
public interface ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx)
throws ServletException;
}
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = Collections.emptyList();
// ... 省略部分:
// 主要内容是:将webAppInitializerClasses中非接口和非抽象类的实例化后,添加到initializers
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
可以配置多个DispatcherServlet,每个Servlet有自己专属的Servlet WebApplicationContext, 它的parent将会是Root WebAppcalitionContext。通过这种方式,可以将公共的services等bean配置在ROOT中;
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { App1Config.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/app1/*" };
}
}
DispatcherServlet 的初始化
init是Servlet生命周期的开始,对DispatcherServlet来说,配置webApplicationContext并初始Bean都在其中。org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext中包含主要的两部分逻辑:
- org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext
- org.springframework.web.servlet.DispatcherServlet#initStrategies
初始化基础Bean
这部分,除了ApplicationContext上记录了servlet相关的信息之外,创建Bean的逻辑与之前了解到的没有什么不同,这里目前还不包含对Controller的处理。
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();
Servlet处理请求用的相关Bean
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
这里先主要关心Controller 以及RestContoller的工作流程,这里HandlerMapping是非常重要的,如果没有配置自己的HandlerMapping的话,将会从DispatcherServlet.properties读取默认的实现类:
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.function.support.RouterFunctionMapping
读取到这些类并实例化为Spring中的Bean,这些类都实现了afterPropertiesSet。以RequestMappingHandlerMapping为例:
// AbstractHandlerMethodMapping.java
public void afterPropertiesSet() {
initHandlerMethods();
}
/**
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #getCandidateBeanNames()
* @see #processCandidateBean
* @see #handlerMethodsInitialized
*/
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
可以看会对所有的Bean进行处理,如果Bean含有Controller或者RequestMapping注解,那么对所有的 RequestMapping注解的方法进行处理:
- 根据@RequestMapping注解,构造RequestMappingInfo
protected RequestMappingInfo createRequestMappingInfo( RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) { RequestMappingInfo.Builder builder = RequestMappingInfo .paths(resolveEmbeddedValuesInPatterns(requestMapping.path())) .methods(requestMapping.method()) .params(requestMapping.params()) .headers(requestMapping.headers()) .consumes(requestMapping.consumes()) .produces(requestMapping.produces()) .mappingName(requestMapping.name()); if (customCondition != null) { builder.customCondition(customCondition); } return builder.options(this.config).build(); }
- 构造HandlerMethod,它主要包含两个重要属性:bean 以及对应的含有@RequestMapping的Method
- 获取DirectPath, 并记录DirectPath与RequestMappingInfo的关联
- 记录方法名,与HandlerMethod对象的关联
- 记录 handlerMethod 与 CorsConfiguration配置对象的关联
- 记录RequestMappingInfo 与 MappingRegistratio对象的关联
//MappingRegistration public MappingRegistration(T mapping, HandlerMethod handlerMethod, @Nullable Set<String> directPaths, @Nullable String mappingName, boolean corsConfig) { Assert.notNull(mapping, "Mapping must not be null"); Assert.notNull(handlerMethod, "HandlerMethod must not be null"); this.mapping = mapping; this.handlerMethod = handlerMethod; this.directPaths = (directPaths != null ? directPaths : Collections.emptySet()); this.mappingName = mappingName; this.corsConfig = corsConfig; }
处理请求
当客户端发起请求的时候,如果满足DispatcherServlet的处理路径,那么经过doService,交由doDispatch进行处理请求,主要处理逻辑如下:
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
大致的处理流程如下
第一步是根据请求,获取对应的HandlerMethod
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
基本流程是获取到请求的path, 然后得到RequestMappingInfo,最后取得HandlerMethod
第二步,是构造HandlerExecutionChain,主要是添加合适的拦截器
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(request)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
第三步,处理CorsConfiguration
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
HandlerExecutionChain chain, @Nullable CorsConfiguration config) {
if (CorsUtils.isPreFlightRequest(request)) {
HandlerInterceptor[] interceptors = chain.getInterceptors();
return new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
}
else {
chain.addInterceptor(0, new CorsInterceptor(config));
return chain;
}
}
第四步,确定HandlerAdapter,各个adapter是否可以处理当前handler的判断也比较简单,比如AbstractHandlerMethodAdapter 基本只要handler是HandlerMethod实例可以。
// AbstractHandlerMethodAdapter.java
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
第五步,优先调用各拦截器的preHandle方法,如果有个拦截器返回false,那么结束处理
第六步,通过HandlerAdapter的实例调用具体的bean,像RequestMappingHandlerAdapter.handleInternal,并得到ModelAndView
第七步,调用各拦截器的postHandle方法