SpringMVC源码解析之二:一次请求的完整旅行源码分析
一次客户请求在SpringMVC框架中,所经历的完整执行流程分析。
DispatcherServlet的初始化源码分析
在SpringMVC中,所有请求都要通过前端控制器DispatcherServlet来进行处理,因为它是一个servlet了,其生命周期当然也由servlet容器(如tomcat)来管理。在它提供服务之前,必须先初始化。
在进入源码之前,先看下DispatcherServlet的继承关系:
DispatcherServlet的初始化逻辑位于:org.springframework.web.servlet.HttpServletBean#init()方法中:
public final void init() throws ServletException {
//获取DispatcherServlet的初始化配置信息(比如使用在xml配置时的contextConfigLocation)
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
//如果有配置,则通过BeanWrapper工具设置到DispatcherServlet的对应属性上。
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);
}
...
}
//调用子类的initServletBean方法继续初始化,典型的模板方法模式
// Let subclasses do whatever initialization they like.
initServletBean();
...
}
initServletBean方法的逻辑又org.springframework.web.servlet.FrameworkServlet#initServletBean方法提供:
protected final void initServletBean() throws ServletException {
try {
//初始化WebApplicationContext,这个就是DispatcherServlet位于的WebApplicationContext
this.webApplicationContext = initWebApplicationContext();
//继续给子类机会来执行其他的初始化动作,目前是空方法
initFrameworkServlet();
}
...
}
看下WebApplicationContext是如何初始出来的?org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext方法:
protected WebApplicationContext initWebApplicationContext() {
//看看ServletContext的属性“WebApplicationContext.ROOT”是否存在。
//如果在web.xml中配置了ContextLoaderListener,则servlet容器初始化后会回调其contextInitialized方法,这里面会初始化一个WebApplicationContext出来,并设置到ServletContext的属性上。
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//如果当前的DispatcherServlet中已经有webApplicationContext了(即自己给servlet注入一个我们自己创建的webApplicationContext)。
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;
//如果已经有的webApplicationContext还没有激活
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
//将当前DispatcherServlet中已经有webApplicationContext的父容器设置为ServletContext中的那个。
cwac.setParent(rootContext);
}
//配置已有的这个,并刷新容器。
configureAndRefreshWebApplicationContext(cwac);
}
}
}
//如果DispatcherServlet中没有webApplicationContext,则看看ServletContext中是不是有已经注册了的,且是已经初始化好的,只需要拿过来用就行. 这儿使用通过给DispatcherServlet配置contextAttribute属性来找。
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);
}
//看看onRefresh是不是已经调用过了,如果没有,这调用一下。
//这里面会初始化DispatcherServlet用到的一些列组件。
if (!this.refreshEventReceived) {
onRefresh(wac);
}
//如果需要的话,将WebApplicationContext发布到ServletContext的属性中
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
这里面对我们来说比较重要的就是onRefresh,它又会调用到其子类即org.springframework.web.servlet.DispatcherServlet#onRefresh方法:
protected void onRefresh(ApplicationContext context) {
//初始化springmvc用到的组件
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context); //初始化HandlerMappings
initHandlerAdapters(context); //初始化HandlerAdapters
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context); //初始化ViewResolvers
initFlashMapManager(context);
}
这一堆的初始化方法模式都非常相似,也比较简单,我们就只展开initHandlerMappings和initHandlerAdapters这两个方法.
org.springframework.web.servlet.DispatcherServlet#initHandlerMappings方法:
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
//是否检测所有的HandlerMapping,默认yes
if (this.detectAllHandlerMappings) {
//从Dispather的容器以及父容器中找出所有的HandlerMapping类型,赋值给handlerMappings属性,并进行排序。
// 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);
}
}
//如果不支持检测所有的HandlerMapping,则就找beanName为“handlerMapping”的bean,赋值给handlerMappings属性
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
}
//如果容器中没有找到的话,则使用默认策略,默认策略文件定义在DispatcherServlet同包下的DispatcherServlet.properties文件中,默认定义的是BeanNameUrlHandlerMapping和RequestMappingHandlerMapping。
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
}
org.springframework.web.servlet.DispatcherServlet#initHandlerAdapters方法:
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
//找所有的HandlerAdapter类型,并赋值给handlerAdapters属性,并排序
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
try {
//找名字为handlerAdapter,并赋值给handlerAdapters属性
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
...
}
//如果容器中没有找到的话,则使用默认策略,即 HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
}
}
其他方法类似,这儿就不赘述了。
到此,DispatcherServlet就初始化好了,其内部的组件也都设置完成,可以提供服务了。
DispatcherServlet的service源码分析
从DispatcherServlet的继承关系上,servlet的service方法最终会调用到FrameworkServlet#processRequest方法,这是一个模板方法,封装了处理请求的一般逻辑,其doService方法由子类DispatcherServlet来实现。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//Locale的相关处理
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
//异步处理的逻辑
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
//具体的处理逻辑,由子类实现
doService(request, response);
}
...
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
...
//发布请求处理事件(ServletRequestHandledEvent),使用的是DispatcherServlet的WebApplicationContext。
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
跟入DispatcherServlet#doService:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
....
//设置MVC框架用到的一些属性到request中。
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
...
try {
//具体干活的方法,最核心的方法,封装了MVC的整体执行流程
doDispatch(request, response);
}
...
}
最核心的DispatcherServlet#doDispatch方法:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
try {
//对multipartRequest的处理,使用到了multipartResolver组件。
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//获取请求对应的handler,使用到了HandlerMapping组件。这儿返回的是HandlerExecutionChain,里面封装了要应用的拦截器。
mappedHandler = getHandler(processedRequest);
//没有找到Handler时的处理,默认是404
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//找到Handler对应的HandlerAdapter。
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//对Get或者Head请求时的last-modified处理
...
//应用拦截器的前置拦截,遍历调用拦截器的preHandle方法。如果没有通过拦截器,直接返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//通过HandlerAdapter调用到真实的处理器上(即我们写的业务方法)
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...
//应用默认的ViewName,?
applyDefaultViewName(processedRequest, mv);
//应用拦截器的后置拦截,遍历调用拦截器的postHandle方法。可以看到,如果抛了一次,postHandle方法是不执行的。
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
....
//结果处理,这里面会使用viewResolver来解析视图,然后调用视图的方法来进行结果渲染。
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//抛异常是触发拦截器的完成后操作,遍历调用拦截器的afterCompletion方法。
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
...
}
可以看到,doDispatcher方法这是SpringMVC对请求处理流程的整体封装,但里面的具体工作又交由具体的组价负责处理。接下来对关键的几个组件分析下源码。
HandlerMapping组件
DispatcherServlet#getHandler方法,获取适当的HandlerExecutionChain:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
//遍历所有的HandlerMapping,依次调用getHandler方法,返回第一个不为null的HandlerExecutionChain
for (HandlerMapping hm : this.handlerMappings) {
...
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
//没找到返回null
return null;
}
上场的第一个关键组件是HandlerMapping组件,它定义了一个HandlerExecutionChain getHandler(HttpServletRequest request)
方法,用于根据请求来解析出对应的Handler执行链。
先来看下SpringMVC给我提供了哪些具体的实现:
先介绍下SimpleUrlHandlerMapping这个最简单的实现,它通过显式方式来注册url和handler的映射关系。
因为AbstractHandlerMapping继承至ApplicationObjectSupport(它实现了ApplicationContextAware接口),因此spring容器在初始AbstractHandlerMapping类型的bean时会调用setApplicationContext方法,对于SimpleUrlHandlerMapping来说,spring容器在初始化它是会回调initApplicationContext方法:
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
//将配置的url和handler的映射注册到AbstractUrlHandlerMapping的handlerMap中(这是最关键的属性)
registerHandlers(this.urlMap);
}
其注册过程也比较简单,registerHandlers方法
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
...
urlMap.forEach((url, handler) -> {
//如果url不是/开头,则加一个
if (!url.startsWith("/")) {
url = "/" + url;
}
//去除handler中的空格,这个handler是spring bean的名称
if (handler instanceof String) {
handler = ((String) handler).trim();
}
//调用父类AbstractUrlHandlerMapping的注册方法
registerHandler(url, handler);
});
}
跟入AbstractUrlHandlerMapping#registerHandler方法:
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
...
Object resolvedHandler = handler;
//如果handler是spring beanName,并且不是懒初始化的且是单例的,则通过getBean方法来创建具体的bean出来
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}
//如果这个url已经注册了一个handler了,并且新的handler和当前要注册的还不一样,则抛出异常。如果一样,那么不管
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
}
//注册一个新的url和handler
else {
//注册RootHandler
if (urlPath.equals("/")) {
...
setRootHandler(resolvedHandler);
}
//注册DefaultHandler(默认handler是找不到handler时,使用默认的)
else if (urlPath.equals("/*")) {
...
setDefaultHandler(resolvedHandler);
}
else {
//加到map中,完成注册
this.handlerMap.put(urlPath, resolvedHandler);
...
}
}
}
初始化好了之后,就可以调用它的getHandler方法了,同样,Spring惯用的模板方法模式又来了,调用入口在AbstractHandlerMapping#getHandler方法上:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//调用子类的具体实现
Object handler = getHandlerInternal(request);
//如果子类中没有找到handler,则使用默认的handler
if (handler == null) {
handler = getDefaultHandler();
}
//如果默认的handler没有配置,则返回null(即没有能够处理该请求的handler)
if (handler == null) {
return null;
}
//如果找到的handler是字符串,则当做spring beanName来实例化一个对象出来。
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//将handler包装成HandlerExecutionChain返回,将匹配该请求的拦截器封装进去。
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
...
return executionChain;
}
再次,对于SimpleUrlHandlerMapping来说,getHandlerInternal方式位于AbstractUrlHandlerMapping#getHandlerInternal:
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//从request中解析出请求路径(比如 /hello.html)
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//通过请求路径查找handler
Object handler = lookupHandler(lookupPath, request);
//没找到
if (handler == null) {
Object rawHandler = null;
//如果请求路径是"/",则返回RootHandler
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
//如果还没匹配上,则返回DefaultHandler
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
//对找到的handler,如果是String,则当做springbean的名称,然后实例化一个对象出来。
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
...
return handler;
}
继续AbstractUrlHandlerMapping#lookupHandler方法:
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
//从之前注册的handlerMap中精确查找
Object handler = this.handlerMap.get(urlPath);
//找到了直接返回
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
//没找到,使用匹配查找模式
List<String> matchingPatterns = new ArrayList<>();
//遍历所有注册的url,通过PathMatcher来看请求的路径和注册的路径是否匹配,匹配上的话就加入到matchingPatterns中。
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
...
}
//通过对所有匹配的注册url进行比较排序之后,取得第一个元素就是最匹配的注册的url。
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
bestMatch = matchingPatterns.get(0);
}
//获取最佳匹配的handler
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
...
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
//更多健壮性和兼容性处理。
...
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
到此,分析了最简单的SimpleUrlHandlerMapping初始化和调用的逻辑,处理匹配模式查找有点复杂以外,其他的都比较简单。
接下来了解下BeanNameUrlHandlerMapping实现,它比SimpleUrlHandlerMapping要高级一点的是,它是自动发现和注册url和handler的映射关系的,和SimpleUrlHandlerMapping从继承结构上来说几乎一致,只是它的初始化有些不同:
org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping#initApplicationContext方法:
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
//自动检测和注册handler
detectHandlers();
}
protected void detectHandlers() throws BeansException {
ApplicationContext applicationContext = obtainApplicationContext();
//获取spring容器中的所有beanname
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
//遍历所有beanName
for (String beanName : beanNames) {
//找到当前这个beanName的所有可以做url的名称(它自己和它的别名),由子类实现。
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
//注册url和handler(此时是beanName)的映射
registerHandler(urls, beanName);
}
...
}
}
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping#determineUrlsForHandler方法:
protected String[] determineUrlsForHandler(String beanName) {
//找到当前这个beanName的所有可以做url的名称,即它和它的别名中,以"/"开头的名字都算。
List<String> urls = new ArrayList<>();
if (beanName.startsWith("/")) {
urls.add(beanName);
}
String[] aliases = obtainApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
当完成了自动监测和注册映射之后,getHandler的操作和SimpleUrlHandlerMapping就一模一样了,没有任何区别。
HandlerMapping另外一个分支实现是RequestMappingHandlerMapping,这个才是我们最最常用的,它通过@RequestMapping注解的方式,来声明要处理的请求路径以及对应的handler(方法)。
因为RequestMappingHandlerMapping实现了InitializingBean接口,因此它的初始化逻辑位于afterPropertiesSet方法中:
public void afterPropertiesSet() {
//初始化HandlerMethods,这里面会找到所有的@RequestMapping注解方法,然后将配置的url和方法注册到mappingRegistry中。
initHandlerMethods();
}
protected void initHandlerMethods() {
//spring容器中的所有beanNames
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
...
//isHandler方法检查下beanType是不是要进一步解析的handler(常规是有Controller或者RequestMapping注解的)
if (beanType != null && isHandler(beanType)) {
//检查方法,并注册满足条件的方法。
detectHandlerMethods(beanName);
}
}
}
//一个扩展点,目前实现是空的。
handlerMethodsInitialized(getHandlerMethods());
}
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods方法:
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
final Class<?> userType = ClassUtils.getUserClass(handlerType);
//MethodIntrospector.selectMethods模板方法,作用是选择满足条件的方法,并返回一个map,key为方法,value为回调方法的返回值。
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
//回调方法的返回类型是RequestMappingInfo,由RequestMappingHandlerMapping实现,它包装了@RequestMapping的相关信息。
return getMappingForMethod(method, userType);
}
...
});
//对选出来的方法进行注册
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//将handler,对应的方法,以及注解的信息 放到 mappingRegistry中。
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod:
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//解析@RequestMapping注解,创建RequestMappingInfo对象
RequestMappingInfo info = createRequestMappingInfo(method);
...
return info;
}
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
//在方法上找到@RequestMapping注解
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
//通过注解信息来构建RequestMappingInfo对象。
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
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();
}
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register方法:
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
//构建HandlerMethod,它包装了handler和method,便于调用。
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
assertUniqueMethodMapping(handlerMethod, mapping);
//往mappingLookup这个map中放入mapping, handlerMethod的映射关系
this.mappingLookup.put(mapping, handlerMethod);
//如果注解中配置的是精确url,这注册精确url和mapping的映射关系(从这儿的注册关系可看出,查找的时候先查找urlLookup得到mapping,在查找mappingLookup得到handlerMethod)
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
...
//registry这个map中再保存mapping 和 MappingRegistration的关系。
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
初始化好之后,来简单分析下getHandler的实现,对于RequestMappingHandlerMapping,会调用到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal方法上:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//得到请求路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try {
//找请求路径对应的HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
继续org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
//这个就是在urlLookup中去找(精确查找),找出来的是RequestMappingInfo
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
//如果没有找到,则在mappingLookup中找匹配的。
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
//找到了匹配上的,那么从中选择一个最合适的。
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
Match bestMatch = matches.get(0);
...
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
//没有找到
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
源码中非常多细节没有展开分析,有条件的话,你最好debug走一遍代码。
到此,我们就将常用的HandlerMapping组件分析完了,可以看出原理简单,但是工程实践起来还是比较复杂的,特别是RequestMappingHandlerMapping的实现,为了开发者的体验更好,使用更简单,Spring做了大量的简化开发的工作,不过这也带来了spring框架的复杂性。
HandlerAdapter组件
HandlerAdapter,顾名思义,就是Handler的适配器,这样无论Handler的表现形式是什么,比如实现了Controller接口,实现了HttpRequestHandler接口,实现了Servlet,甚至就是一个常规方法,都可以通过对应的适配器,让dispatcherServlet可以以同样的方式调用。
HandlerAdapter的继承关系,SpringMVC提供了4种适配器实现,分别对应Controller接口,实现了HttpRequestHandler接口,实现了Servlet,和@RequestMapping注解的方法。
在核心的模板方法doDispatch中,重要的第二步是HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
,获取Handler对应的HandlerAdapter:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
//遍历注册的handlerAdapters,找到第一个支持当前handler的适配器。
for (HandlerAdapter ha : this.handlerAdapters) {
...
if (ha.supports(handler)) {
return ha;
}
}
}
...
}
简单的看下org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter#supports方法:
public boolean supports(Object handler) {
//如果handler类型是Controller,则返回true。
return (handler instanceof Controller);
}
其他3个Adapter的实现类似,不展开。
找到HandlerAdapter之后,接下来就是通过它来进行调用了,先忽略拦截器的相关处理,后面单独说明。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
还是以SimpleControllerHandlerAdapter举例:
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter#handle方法:
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//直接进行类型转换后,调用对应接口的处理方法。
return ((Controller) handler).handleRequest(request, response);
}
HttpRequestHandlerAdapter 和 SimpleServletHandlerAdapter 的调用实现与 SimpleControllerHandlerAdapter没什么两样。但RequestMappingHandlerAdapter的调用逻辑就比较复杂了:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal方法:
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
//是否要在同步块中执行,默认false
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
//调用handlerMethod(即我们的业务处理方法)
mav = invokeHandlerMethod(request, response, handlerMethod);
}
...
return mav;
}
具体的handerMethod调用逻辑:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//web参数绑定
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//封装一个ServletInvocableHandlerMethod,并设置参数解析器、参数发现器,预植模型数据等
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//异步处理...
....
//发起调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
...
//封装ModelAndView返回
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
跟入org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle方法:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
//调用结果为null处理
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
...
try {
//使用returnValueHandlers对结果处理后返回
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
...
}
继续跟入org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest方法:
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//获取方法参数Object[]
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
//通过反射调用业务处理方法
Object returnValue = doInvoke(args);
return returnValue;
}
org.springframework.web.method.support.InvocableHandlerMethod#doInvoke方法:
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
return getBridgedMethod().invoke(getBean(), args);
}
...
}
因为我们的业务方法参数是完全不可预知的,并且在我的业务方法上,想接受啥参数,只需要定义一个参数名就好了,SpringMVC会自动将我们想要的参数给传进来,这儿有点意思的是方法参数的获取和封装,org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues方法:
private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//获取业务方法的参数数组
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
//解析业务方法的每一个参数值
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
//初始话参数名称发现器,默认为DefaultParameterNameDiscoverer, 它里面包装其他的ParameterNameDiscoverer来具体处理
// 1. StandardReflectionParameterNameDiscoverer JDK 8才支持的,通过-parameters参数编译后的class文件中保存有参数名信息,直接获取
//2. LocalVariableTableParameterNameDiscoverer JDK8之前,编译的class文件,参数的名称在方法上是丢失了的,但是在class文件的局部变量表中还存有方法的参数名称,这个就是LocalVariableTable去获取方法名称。
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
...
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
//解析出具体的参数值。
//HandlerMethodArgumentResolver 这个接口也是一个非常庞大的家族,SpringMVC为我们提供了20多种实现,来支持各种各样的参数解析(比如@RequestParam注解的解析器、PathVariable参数解析器等等),太多,不展开。
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
....
}
...
}
return args;
}
ViewResolver组件
在调用完业务方法之后,DispatcherServlet会拿到一个ModelAndView结果,接下来就需要通过ViewResolver组件来解析出View对象。
org.springframework.web.servlet.DispatcherServlet#processDispatchResult方法:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
//如果前面逻辑中存在异常,这儿进行统一的异常处理。
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
//通过HandlerExceptionResolver来解析异常,返回异常的ModelAndView对象。
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
//进行模型渲染
render(mv, request, response);
...
}
...
//触发拦截器的afterCompletion方法
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
跟入org.springframework.web.servlet.DispatcherServlet#render方法:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
...
View view;
String viewName = mv.getViewName();
if (viewName != null) {
//使用ViewResolver,将viewName解析为View对象。
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
...
}
else {
//直接返回的就是View对象,则直接使用,不需要解析。
view = mv.getView();
...
}
// Delegate to the View object for rendering.
...
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//调用View的render方法,实现最终的渲染。
view.render(mv.getModelInternal(), request, response);
}
...
}
解析方法org.springframework.web.servlet.DispatcherServlet#resolveViewName:
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
//从注册的ViewResolver中,第一个解析出来的View不为null就直接返回。
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
我们以 BeanNameViewResolver 这个ViewResolver为例,看下它的解析方法,org.springframework.web.servlet.view.BeanNameViewResolver#resolveViewName:
public View resolveViewName(String viewName, Locale locale) throws BeansException {
ApplicationContext context = obtainApplicationContext();
//一些前置检查
...
//直接通过bean的名称获取对应的View对象。
return context.getBean(viewName, View.class);
}
对于View来说,不同的模板引擎提供了不同的实现,我们已自带的InternalResourceView为例(通常处理jsp页面渲染),看看它的render方法,该方法位于它的父类AbstractView中:
org.springframework.web.servlet.view.AbstractView#render方法:
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
//合并所有的变量,包括request中的各种属性,调用返回的模型属性等
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
//对response做一些渲染前的准备。
prepareResponse(request, response);
//渲染,调用子类方法
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
对于InternalResourceView之类来说,渲染调用的是:org.springframework.web.servlet.view.InternalResourceView#renderMergedOutputModel方法:
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//把模型数据暴露到request的属性中
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
//获取跳转的url
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(request, response);
//使用RequestDispatcher进行服务端跳转
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
...
else {
...
//进行forward跳转。
rd.forward(request, response);
}
}
到此,我们分析了SpringMVC一次典型的请求处理流程,主体逻辑在DispatcherServlet#doDispatch方法中,它是一个执行框架,具体干活的委托给HandlerMapping、HandlerAdapter、和ViewResolver组件进行具体的处理。