为了方便利用使用的思维进行理解,我们可以先从程序调用入口出发,先对调用层面进行说明,再对程序框架本身处理进行深入。简而言之就是先说明一个请求进入mvc逻辑需要经过哪些处理步骤(1~7节),再说明处理步骤中mvc是怎样提供参数支持的(8~10节)。
Servlet
是一个处理http请求的标准类,它处于javax.servlet.http
包,属于java标准库的扩展部分。其中主要有init、service、destroy方法,作用分别为初始化时调用,接收到请求调用,销毁时调用。
Servlet是一个顶层接口,其中Spring传入到Tomcat容器的ApplicationFilterChain
类中的Servlet参数是DispatcherServlet
实例。
在客户端发送请求时,ApplicationFilterChain
过滤器链会调用Servlet
类中的service方法。
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
......
servlet.service(request, response);
......
}
此处的servlet是DispatcherServlet的实例,至于是如何创建的,需要去阅读相关源码,此处不做深入讨论。
由于DispatcherServlet中未重写service(ServletRequest req, ServletResponse res)
方法,所以此处调用的是HttpServlet类中的方法。
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException(lStrings.getString("http.non_http"));
}
service(request, response);
}
在Spring项目中,FrameworkServlet作为HttpServlet的子类,DispatcherServlet的父类,重写了service(HttpServletRequest request, HttpServletResponse response)
方法,所以此处service(request, response)
调用的是FrameworkServlet中的方法。
/**
* Override the parent class implementation in order to intercept PATCH requests.
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
如果请求的method是patch类型或者没有请求的method,则直接进行处理,否则调用父类(HttpServlet)中的service方法,注意此处的参数类型为HttpServletRequest
和HttpServletResponse
。HttpServlet
中的service方法:
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
主要功能就是根据method(get/put/post…)调用子类的doGet、doPost、doPut等等方法,这些方法都在FrameworkServlet
类中有相应实现。
如:
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
其中大部分调用的都是processRequest方法进行处理。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
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);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
其中主要功能为:
1)向ThreadLocal
中存入LocaleContext
与RequestAttributes
。
2)执行doService方法。
3)将ThreadLocal中的LocaleContext与RequestAttributes置空。
4)标记请求处理已完成。
5)发布请求处理事件。
上述逻辑处理后,我们可以解锁以下几个隐藏操作:
1)任意代码段即可获取LocaleContext与RequestAttributes。
// 获取语言环境
LocaleContext localeContext = LocaleContextHolder.getLocaleContext();
// 获取HttpServletRequest
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
2)通过事件,获取到当前请求的url、sessionId、method等等信息。
@Component
public class MyEventListener implements ApplicationListener<ServletRequestHandledEvent> {
@Override
public void onApplicationEvent(ServletRequestHandledEvent event) {
System.out.println(event.getRequestUrl());
}
}
processRequest方法调用的doService方法,是在DispatcherServlet
类中重写的一个方法。
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// 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());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
RequestPath previousRequestPath = null;
if (this.parseRequestPath) {
previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
ServletRequestPathUtils.parseAndCache(request);
}
try {
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
其中主要就是给request设置参数与调用doDispatch方法。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;cc
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
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 = "GET".equals(method);
if (isGet || "HEAD".equals(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);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
以上代码主要功能:
1)检查是否是文件请求。
2)根据request查找到对应的HandlerMethod
与HandlerInterceptor
,并封装为一个HandlerExecutionChain
。
3)根据HandlerAdapter
的supports方法找到支持当前HandlerMethod的HandlerAdapter。
4)调用HandlerExecutionChain拥有的拦截器的preHandle方法,如果返回false则直接触发拦截器的afterCompletion方法,并终止执行。
5)对请求进行supportedMethods和requireSession检查,使用反射执行方法。
6)执行拦截器的postHandle方法。
这里有一个需要着重需要注意的地方,那就是MVC是怎样根据request找到对应的执行方法HandlerMethod的呢?
我们接着查看mappedHandler = getHandler(processedRequest);
这行代码。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
此处逻辑非常简单,就是循环handlerMappings对象,调用其getHandler方法,如果得到的结果不为null,那么直接返回这个结果。
这里的handlerMappings对象有5个值:
具体是从哪里设置的,等会儿我们再细讲。这几个类本身都没有实现getHandler方法,都是调用其共同父类AbstractHandlerMapping
类中的实现。
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// Ensure presence of cached lookupPath for interceptors and others
if (!ServletRequestPathUtils.hasCachedPath(request)) {
initLookupPath(request);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = getCorsConfiguration(handler, request);
if (getCorsConfigurationSource() != null) {
CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
config = (globalConfig != null ? globalConfig.combine(config) : config);
}
if (config != null) {
config.validateAllowCredentials();
}
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
通过以上代码,我们可以得知其主要功能:
1)根据request查找到对应的handler,如果对应的handler为null,则设置默认的handler,如果返回的是一个String,则在beanFactory中查找以此为beanName的bean作为handler。
2)确保缓存lookupPath。
3)将handler与拦截器封装为一个HandlerExecutionChain。
4)跨域配置。
继续进入getHandlerInternal方法。
在AbstractHandlerMapping
的子类RequestMappingInfoHandlerMapping
中有实现:
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
try {
return super.getHandlerInternal(request);
}
finally {
ProducesRequestCondition.clearMediaTypesAttribute(request);
}
}
这里调用了父类AbstractHandlerMethodMapping
中的实现。
@Override
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();
}
}
1)获取去除了content-path的请求路径。
2)mappingRegistry获取读锁,查找HandlerMethod,mappingRegistry释放读锁。
继续深入lookupHandlerMethod查找方法。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
for (Match match : matches) {
if (match.hasCorsConfig()) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
}
}
else {
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.getHandlerMethod().getMethod();
Method m2 = secondBestMatch.getHandlerMethod().getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.getHandlerMethod();
}
else {
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}
}
其中主要执行逻辑为:
1)从MappingRegistry
的pathLookup中匹配请求路径,得到对应的RequestMappingInfo
,再根据RequestMappingInfo
自身的methodsCondition、paramsCondition、headersCondition、consumesCondition、producesCondition、pathPatternsCondition、patternsCondition、customConditionHolder进行一系列的匹配,只要全都匹配成功,那就将根据此RequestMappingInfo
为key,在MappingRegistry
类中的类型为MapMappingRegistration
的value。(MappingRegistration中保存了HandlerMethod)。
2)如果匹配到多个结果,则按照MatchComparator
排序取第一个为bestMatch。返回bestMatch持有的HandlerMethod
。
至此我们成功的找到了request对应的HandlerMethod
。
但是,这些对象的变量是什么时候给设置进去的呢?它们的初始化逻辑是怎样的呢?
众所周知,第一次调用Servlet
时会执行其init方法,DispatcherServlet也是个Servlet,那么来看看其初始化流程。
在初始执行Servlet时,Tomcat容器会调用Servlet类的init方法,源码在org.apache.catalina.core.StandardWrapper
中的initServlet方法。
private synchronized void initServlet(Servlet servlet)
throws ServletException {
......
servlet.init(facade);
......
}
此处Servlet为DispatcherServlet,是Spring容器启动时传入到Tomcat容器的参数。
而DispatcherServlet并未重写init方法,所以一路向上,调用父类GenericServlet
的init方法:
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
这里的init被GenericServlet的子类,DispatcherServlet的父类HttpServletBean
重写:
public final void init() throws ServletException {
......
initServletBean();
}
这里的initServletBean被HttpServletBean的的子类,DispatcherServlet的父类FrameworkServlet
重写:
protected final void initServletBean() throws ServletException {
......
this.webApplicationContext = initWebApplicationContext();
}
protected WebApplicationContext initWebApplicationContext() {
......
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
......
return wac;
}
这里的onRefresh方法就是调用的被子类DispatcherServlet实现的方法。
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
调用栈如下:
这里我们直接分析initStrategies方法。
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
可以看到,Spring写的代码挺漂亮的,从方法名上就能清晰的知道其作用。
这个方法主要做这几件事:
1)初始化文件解析器,可用于判断文件参数,如max-file-size、max-request-size等的判断。
2)初始化区域解析器,其功能为设置消息国际化。
3)初始化主题解析器,可动态切换主题。
4)初始化处理映射器,用来找到请求对应的具体处理方法。
5)初始化处理适配器,用来执行method。
6)初始化异常解析器,用来处理异常信息。
7)初始化视图名称翻译器,其中可以设置prefix、suffix等信息,跳转视图时,viewNameTranslator可以拼接前、后缀得到完整的视图名称。
8)初始化视图解析器,有了这个,接口返回的视图才能正常的进行跳转。
9)初始化FlashMapManager,主要用于redirect传参。
我们现在主要分析handlerMapping中那些参数是怎样读取并设置进去的,所以我们需要解读initHandlerMappings方法。
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");
}
}
for (HandlerMapping mapping : this.handlerMappings) {
if (mapping.usesPathPatterns()) {
this.parseRequestPath = true;
break;
}
}
}
这个方法最重要的就是从beanFactory中获取HandlerMapping
。
可以看到此时handlerMappings变量中有5个类型的HandlerMapping。
我们来分析一下RequestMappingHandlerMapping。
可以看到,RequestMappingHandlerMapping
间接的实现了InitializingBean
接口,那么,它就会在实例化bean时,在调用init方法之前,调用其afterPropertiesSet方法。
不太熟悉SpringBean生命周期的,可以参考文章《死磕Spring系列:从源码理解SpringBean生命周期》。
RequestMappingHandlerMapping
类中afterPropertiesSet具体实现如下:
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setTrailingSlashMatch(useTrailingSlashMatch());
this.config.setContentNegotiationManager(getContentNegotiationManager());
if (getPatternParser() != null) {
this.config.setPatternParser(getPatternParser());
Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
"Suffix pattern matching not supported with PathPatternParser.");
}
else {
this.config.setSuffixPatternMatch(useSuffixPatternMatch());
this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
this.config.setPathMatcher(getPathMatcher());
}
super.afterPropertiesSet();
}
就是设置一些必要的参数与调用父类的afterPropertiesSet方法。这个方法是在AbstractHandlerMethodMapping
类中的实现。
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
其中就是获取beanFactory中几乎所有的beanName,如果不是以scopedTarget.
开头的bean则执行processCandidateBean方法。
最后执行handlerMethodsInitialized方法。
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);
}
}
主要逻辑为如果该beanName在beanFactory中,且该类有@Controller
或者@RequestMapping
注解,则执行detectHandlerMethods。
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
else if (mappingsLogger.isDebugEnabled()) {
mappingsLogger.debug(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
其中的主要就是:
1)扫描类的带有@RequestMapping
注解的Method
,将其与为去其生成的RequestMappingInfo
逐个注册到mappingRegistry中。
注册的方法registerHandlerMethod是调用MappingRegistry
类的register方法:
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
corsConfig.validateAllowCredentials();
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping,
new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
其中的主要就是:
1)开启写锁。
2)创建HandlerMethod
对象。
3)将路径中没有可变参数的将其path与RequestMappingInfo
添加到pathLookup中。如**"/test/"、"/test/{id}"即为有可变参数。
4)将handlerMethod与其生成的name添加到nameLookup中。如DataController
中的req方法,生成的name为DC#req
。
5)将配置了跨域信息的handlerMethod与跨域配置CorsConfiguration放入corsLookup。
6)类中所有的MVC方法都会以RequestMappingInfo
为key,MappingRegistration
为value的形式添加到registry中。
7)释放写锁。
由于这里已经将相关数据初始化,所以在对请求进行方法匹配的时候,就能直接使用已有数据进行处理。
protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
// Total includes detected mappings + explicit registrations via registerMapping
int total = handlerMethods.size();
if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) {
logger.debug(total + " mappings in " + formatMappingName());
}
}
这里就是打印日志的功能。
总结来讲,MVC的工作逻辑分为两步。
1)首次调用时,初始化各种解析器,并封装各种元数据到变量中,便于后续处理。
2)发起http请求时,DispatcherServlet
接收到处理请求,首先使用HandlerMapping
匹配得到HandlerExecutionChain
,再由HandlerExecutionChain
中的HandlerMethod
得到适配的HandlerAdapter
,由HandlerAdapter
负责执行HandlerMethod
后得到ModelAndView
,最后由视图解析器ViewResolver
解析得到View
,由View
进行页面跳转。