1.写在前面
今天开始,笔者就带着大家来看springMVC的源码了,由于我们之前在SpringMVC源码系列(一)手动模拟SpringMVC中笔者已经模拟了springMVC的源码了。有了这篇博客的基础,读起springMVC的源码,就比较简单了。由于springMVC源码的体系比较庞大,笔者一篇博客肯定讲不完,所以要分几次来讲。
2.三大HandlerMapping
因为我们都知道SpringMVC的核心类就是DispatcherServlet
,所以我们需要看下这个DispatcherServlet
类的构造方法,同时看下静态的代码块,具体的代码如下:
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
//加载DispatcherServlet的配置文件
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
//设置到对应变量中去,方便后面的使用www
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
public DispatcherServlet(WebApplicationContext webApplicationContext) {
super(webApplicationContext);
setDispatchOptionsRequest(true);
}
我们发现这个类,加载了DispatcherServlet
的配置文件,同时调用了父类的构造函数,然后发现这个类中没有doGet
和doPost
的方法,所以请求进来的时候是先调用父类FrameworkServlet
中的doGet
或者是doPost
方法,我们可以先看下DispatchServlet
类的继承关系,具体的如下图:
既然FrameworkServlet
中有doGet方法和doPost方法,所以我们要看springMVC的源码,走来先看的就是这两个方法,因为拦截的请求,走来就会执行这两个方法中的其中的一个方法。具体的doGet和doPost方法如下:
@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(request, response);
方法,于是笔者在这个方法打了一个断点,准备跟进。具体的的代码如下:
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);
}
}
上面的一大串的代码都是处理的一些赋值什么的,核心的代码就是doService(request, response);
,具体的代码如下:
@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);
}
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);
}
}
}
}
上面的代码也是设置一些值,然后核心的方法是doDispatch(request, response);
,这个方法的代码如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
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);
}
}
}
}
上面做将请求的地址和方法映射的方法就是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
的长度不为空而是三个,那么是什么时候添加进去的呢?还记得我们之前读取的配置文件吗?你会发现这儿的handlerMappings就是配置文件中配置的。那就简单了,那么是什么时候加进去的呢?就有笔者告诉你吧,具体的调用链如下:
org.springframework.web.servlet.HttpServletBean#init()
--> org.springframework.web.servlet.FrameworkServlet#initServletBean()
--> org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext()
--> org.springframework.web.servlet.DispatcherServlet#onRefresh()
--> org.springframework.web.servlet.DispatcherServlet#initStrategies()
配置文件中的数据是在这个方法中加载的,具体的代码如下:
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
这儿会调用initHandlerMappings(context);
方法来完成往handlerMappings
的map中添加三个变量,具体的代码如下:
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 从spring的容器中取,如果这儿加了@EnableWebMvc注解,这儿取出来的就是三个
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.
// 如果没有加对应的注解,就直接加载刚才的配置文件,从文件中取出对应HandlerMapping
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");
}
}
}
上面的代码就是如果加了@EnableWebMvc
注解的话,就直接能从spring容器中取出对应的三个HandlerMapping
,如果没有加,就直接加载对应的配置文件中的HandlerMapping
,具体的配置的内容如下:
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
至于怎么将这个三个类加载到spring容器中去的,可以参考我的博客Spring源码系列(九)MapperScan注解的原理(一),这儿是通过Import类,然后这个类是一个配置类,同时它的父类中添加的许多的@Bean
的注解的方法,于是就将这些HandlerMapping
的类扫描成BeanDefinition
然后添加到spring容器中去了。我们可以看下@EnableWebMvc
的注解的源码,具体的源码如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
上面两种方式的区别就在于:如果加了@EnableWebMvc
注解的话,这三个类就直接受spring的管理了,同时这些Bean会进行一个完整的生命周期。而那种从配置文件中加载的,就没有经历一个完整的生命周期。
上篇博客遗留了一个问题,我们先看如下springMVC启动调用的代码:
package com.ys.config;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
System.out.println("Tomcat启动的时候调用了");
//加载spring web的环境
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
// 注册DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(context);
ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("*.do");
}
}
你会发现这儿没有调用refresh()
方法,那么spring的容器在什么地方初始化的呢?这个时候就需要看下对应的调用链了
org.springframework.web.servlet.HttpServletBean#init
-->org.springframework.web.servlet.FrameworkServlet#initServletBean
-->org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
-->org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext
-->org.springframework.context.support.AbstractApplicationContext#refresh
servlet的生命周期大家都是知道的,所有这儿调用的FrameworkServlet
的父类的HttpServletBean
的init
方法,然后经过一系列的处理调用到refresh
方法,这样spring容器就初始化完成了。
3.getHandler方法
上小节,笔者已经讲到怎么调用到getHandler方法,同时不管有没有加@EnableWebMvc
的注解,这个时候都会有三个HandlerMapping
,本节笔者主要讲getHandler
方法,具体的代码如下:
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;
}
可以发现调用的就是这三大HandlerMapping
中getHandler
方法,如果有一个HandlerMapping
中的getHandler
的方法返回值不是null
,就直接返回,我们这儿按照对应的顺序,分别看看这个三个HandlerMapping
中的getHandler
方法中干了什么。
3.1BeanNameUrlHandlerMapping
第一个是BeanNameUrlHandlerMapping,我们先来看看这个类中的getHandler方法的代码,具体如下:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//获取指定的handler
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);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
上面的代码通过调用getHandlerInternal(request);方法通过request来获取这个Handler,那么这个方法干了什么具体的代码如下:
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
//调用下面的方法查找Handler
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if (StringUtils.matchesCharacter(lookupPath, '/')) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
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;
}
上面的代码是通过lookupHandler()方法来查找Handler,那么具体的代码如下:
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Direct match?
//直接匹配
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);
}
// Pattern match?
//正则匹配
//省略一部分代码
// No handler found...
// 没有找到对应的Handler
return null;
}
这儿笔者只打算讲直接匹配,上面handlerMap在什么时候有值呢?或者是什么时候往其中put值呢?于是笔者使用idea的工具进行查找,终于找到如下的代码,然后给对应的地方打上断点,然后进行调试,具体的如下:
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// Eagerly resolve handler if referencing singleton via name.
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}
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.");
}
}
else {
if (urlPath.equals("/")) {
if (logger.isTraceEnabled()) {
logger.trace("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}
else if (urlPath.equals("/*")) {
if (logger.isTraceEnabled()) {
logger.trace("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isTraceEnabled()) {
logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
那么这个方法在什么时候调用的呢?我们可以具体看下调用栈和类的继承图,具体的如下:
发现这个三个HandlerMapping
都是实现ApplicationContextAware
的接口,所有会调用其中的setApplicationContext()
方法,在这个方法中调用了initApplicationContext()
方法,这个方法会一直重写下去,所以调用的是子类的方法,由于RouterFunctionMapping
和RequestMappingHandlerMapping
这两个类没有重写initApplicationContext()
方法,所以调用的父类AbstractHandlerMapping
中的initApplicationContext()
方法,BeanNameUrlHandlerMapping
中也没有重写initApplicationContext()
方法,但是调用了父类AbstractDetectingUrlHandlerMapping
中的initApplicationContext()
方法,具体的内容如下:
@Override
public void initApplicationContext() throws ApplicationContextException {
//调用父类AbstractHandlerMapping中的initApplicationContext()方法
super.initApplicationContext();
//添加匹配的Bean到handlerMap
detectHandlers();
}
这个时候调用的父类initApplicationContext()
方法和另外两个HandlerMapping
的调用的父类的内容是一样的。具体的内容如下:主要的内容就是初始化拦截器
@Override
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
既然已经搞清楚了怎么调用registerHandler(),那么我们继续开始分析,先从detectHandlers()方法说起,具体的代码如下:
protected void detectHandlers() throws BeansException {
//获取容器环境
ApplicationContext applicationContext = obtainApplicationContext();
//根据类型获取所有的beanName
//detectHandlersInAncestorContexts 如果为true,就要从父容器中获取,这儿默认是false
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
for (String beanName : beanNames) {
//调用对应的方法进行匹配筛选
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
//注册对应的Url,简单的说就是添加到handlerMap
registerHandler(urls, beanName);
}
}
if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
}
}
上面的方法走来获取所有的beanName
,然后遍历调用determineUrlsForHandler
方法进行筛选,具体的代码如下:
//如果beanName是以/开头或者这个beanName的别名是以/开头的都添加到list中去,然后转成数组返回出去
protected String[] determineUrlsForHandler(String beanName) {
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);
}
上面的代码就是如果beanName是以/开头或者这个beanName的别名是以/开头的都添加到list中去,然后转成数组返回出去,接着看registerHandler(urls, beanName);
方法,具体的代码如下:
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
for (String urlPath : urlPaths) {
registerHandler(urlPath, beanName);
}
}
先遍历这个整的集合,然后对每一个urlPath
进行注册,我们再看registerHandler(urlPath, beanName);
方法,具体的代码如下:
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// Eagerly resolve handler if referencing singleton via name.
// 这个判断正常的情况下是成立的
if (!this.lazyInitHandlers && handler instanceof String) {
//获取对应的handler,就是对应的beanName
String handlerName = (String) handler;
//获取容器
ApplicationContext applicationContext = obtainApplicationContext();
//判断这个bean是否是单例的
if (applicationContext.isSingleton(handlerName)) {
//获取这个bean,如果没有创建了,就直接创建,如果有创建,直接从容器中获取
resolvedHandler = applicationContext.getBean(handlerName);
}
}
//判断这个map中有没有这个Url,如果有并且这两个值不同,就直接报错
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.");
}
}
else {
//如果这个Url等于/直接设置到为RootHandler
if (urlPath.equals("/")) {
if (logger.isTraceEnabled()) {
logger.trace("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}
//如果这个Url等于/*直接设置到为DefaultHandler
else if (urlPath.equals("/*")) {
if (logger.isTraceEnabled()) {
logger.trace("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
//如果都不是的话,直接put到这个handlerMap中去。
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isTraceEnabled()) {
logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
上面的代码就是进行一些判断,如果这个Url为"/",那么我们将这个设置为RootHandler
,如果这个Url是"/*",那么我们将这个设置为DefaultHandler
,最后如果都不是的话,我们直接put到handlerMap
中去。我们可以写一个类测试一下这种情况,具体的代码如下:
package com.ys.controller;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component("/beanName.do")
public class BeanNameController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
return new ModelAndView("index");
}
}
然后我们继续debug,具体的如下图:
可以看到我们这个handlerMap
中就是我们刚才添加的Bean。我们继续看原来的代码:
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Direct match?
//直接匹配 找到我们刚才的beanName.do
Object handler = this.handlerMap.get(urlPath);
//如果没有找到的话,直接返回null
if (handler != null) {
// Bean name or resolved handler?
// 这儿表示这个handler还没有创建
if (handler instanceof String) {
String handlerName = (String) handler;
//从容器中获取这个Handler
handler = obtainApplicationContext().getBean(handlerName);
}
//校验这个Handler,默认的是空的
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// Pattern match?
//正则匹配
//省略一部分代码
// No handler found...
// 没有找到对应的Handler
return null;
}
然后调用buildPathExposingHandler(handler, urlPath, urlPath, null);
方法,具体的代码如下:
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
于是这儿就封装好了HandlerExecutionChain
对象然后返回,我们继续看原来的代码。
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//获取请求的Url,这儿是/beanName.do
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//往request中设置一个属性值
request.setAttribute(LOOKUP_PATH, lookupPath);
//这个就是调用上面的获取Handler的方法,这儿是会获取到的
Object handler = lookupHandler(lookupPath, request);
//如果为空的话
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
//而请求的地址正好是/
if (StringUtils.matchesCharacter(lookupPath, '/')) {
rawHandler = getRootHandler();
}
//如果还是空,就获取默认的Handler,也就是前面封装的/*
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
//如果上面获取的值不为空,这儿就走一遍验证,然后封装成HandlerExecutionChain对象返回
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;
}
这儿就将获取的handler返回,然后我们再看原来的代码,具体的如下:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//获取handler
Object handler = getHandlerInternal(request);
//如果handler为空直接获取默认的handler
if (handler == null) {
handler = getDefaultHandler();
}
//如果还是空就直接返回null
if (handler == null) {
return null;
}
// Bean name or resolved handler?
// 如果handler的类型是String,证明没有创建,就getBean创建一遍
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//从handler中获取HandlerExecutionChain对象
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
//判断是不是跨域的请求,如果是重新封装一遍
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
//返回封装好的HandlerExecutionChain对象
return executionChain;
}
到此整个BeanNameUrlHandlerMapping
的处理的流程就讲完了,笔者来做个总结,具体的如下图:
笔者这儿把第一个BeanNameUrlHandlerMapping
的图就画完了,后面还有两个Handler
的方法,我们继续往下看。
3.2RequestMappingHandlerMapping
前面第一个HandlerMapping
的没有找到对应的Handler
,这个时候就需要调用下一个HandlerMapping
来查找Handler
,笔者写出如下的测试的代码,具体的代码如下:
package com.ys.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping("/index.do")
public String index(){
return "index";
}
}
这个时候我们在浏览器中访问这个地址,然后笔者继续debug看RequestMappingHandlerMapping
中的getHandler
方法。调用的还是父类中的getHandler()方法,具体的代码如下:
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);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
和原来的代码一样,笔者在这就不赘述,直接看getHandlerInternal(request);方法,具体的代码如下:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
try {
return super.getHandlerInternal(request);
}
finally {
ProducesRequestCondition.clearMediaTypesAttribute(request);
}
}
可以发现代码走来移除了某个属性,同时调用了父类的getHandlerInternal(request);
方法,具体的代码如下:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//获取请求的Url
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//设置属性
request.setAttribute(LOOKUP_PATH, lookupPath);
//加锁
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
//解锁
this.mappingRegistry.releaseReadLock();
}
}
上面的代码和BeanNameUrlHandlerMapping
的代码很相似,上面的核心的方法就是lookupHandlerMethod(lookupPath, request);
具体的代码如下:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
//这个时候会从mappingRegistery中取出这个Url对应的handler,你会发现这个handler的值不为空。所以什么时候添加进去的呢?
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
//省略一部分代码...
}
上面的代码是根据Url来获取对应的Handler
,而到这个时候的mappingRegistery
的值不为空,那么什么时候往这个值添加了值,通过查找的方式,我们找到了如下的代码。具体的代码如下:
public void afterPropertiesSet() {
initHandlerMethods();
}
我们再来看下RequestMappingHandlerMapping
类的继承的关系,具体的如下图:
可以发现RequestMappingHandlerMappering
的父类AbstractHandlerMethodMapping
实现InitializeBean
的接口,所以就会调用到上面的afterPropertiesSet()
方法的,在afterPropertiesSet()
方法中调用了initHandlerMethods()
方法,具体的代码如下:
protected void initHandlerMethods() {
//遍历所有的BeanName
for (String beanName : getCandidateBeanNames()) {
//如果beanName的开头不是scopedTarget.
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
上面的代码会遍历所有的beanName
,然后只要开头不是scopedTarget.
,就会执行processCandidateBean(beanName);
具体的代码如下:
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);
}
}
//判断这个类上面是否加了@Controller注解或者是@RequestMapping注解,加了就执行下面的代码
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
如果这个类加了@Controller
或者@RequestMapping
的注解的话,就调用detectHandlerMethods(beanName);
方法,具体的代码如下:
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));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
上面的代码就是调用selectMethods()
方法来查找匹配的方法,具体的代码如下:
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
final Map<Method, T> methodMap = new LinkedHashMap<>();
Set<Class<?>> handlerTypes = new LinkedHashSet<>();
Class<?> specificHandlerType = null;
//如果这个类不是jdk动态代理的类,再次判断这个类是不是cglib的类,反正获取的就是原始的类
if (!Proxy.isProxyClass(targetType)) {
specificHandlerType = ClassUtils.getUserClass(targetType);
//添加到集合中
handlerTypes.add(specificHandlerType);
}
//将这个类的所有的接口添加到集合中去,一般的时候都是空的
handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
for (Class<?> currentHandlerType : handlerTypes) {
//获取目标类
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
//查找给定lambda表达式匹配的方法
ReflectionUtils.doWithMethods(currentHandlerType, method -> {
//获取具体的方法
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
//回调原来写好的lambda表达式中的方法
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
//查找对应的桥接方法,一般情况下不是桥接方法
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
//不是桥接方法,就直接添加到methodMap中去。
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
methodMap.put(specificMethod, result);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}
上面经过一系列的操作,最终还是会调用对应的getMappingForMethod()
方法,来创建RequestMappingInfo
对象,最终添加到methodMap
中去。具体的代码如下:
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//根据方法创建RequestMapping对象,主要是解析@RequestMapping注解上的数据
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
//根据类型床RequestMapping对象,主要是解析@RequestMapping注解上的数据
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
//将两个合起来
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
上面的代码就是解析@RequestMapping
注解的数据,然后根据这个@RequestMapping
这个注解创建RequestMappingInfo
对象返回,至此就方法和请求的地址映射起来,同时存到methodMap
中去,最后返回。
protected void detectHandlerMethods(Object handler) {
//获取类型
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
//获取原始类型,这个类可能是代理类
Class<?> userType = ClassUtils.getUserClass(handlerType);
//查找对应的方法,最后将方法和请求的Url对应起来,但是这个时候还没有完成映射
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));
}
//遍历刚才的那个map
methods.forEach((method, mapping) -> {
//选择可调用的方法
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//完成映射,就是将这些的对象包装起来存到mappingRegistry
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
我们继续看最后一步,完成映射的代码,具体就是调用registerHandlerMethod(handler, invocableMethod, mapping);
具体的代码如下:
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
继续调用对应的方法,具体的代码如下:
public void register(T mapping, Object handler, Method method) {
// Assert that the handler method is not a suspending one.
// 这段代码是有关kotlin,可以直接跳过
if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
Class<?>[] parameterTypes = method.getParameterTypes();
if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
}
}
//加锁
this.readWriteLock.writeLock().lock();
try {
//将方法包装成HandlerMethod对象
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
//校验
validateMethodMapping(handlerMethod, mapping);
//将请求的地址和方法映射起来存储到mappingLookup中去
this.mappingLookup.put(mapping, handlerMethod);
//获取Url
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
//将Url和mapping关联起来
this.urlLookup.add(url, mapping);
}
String name = null;
//名字的生成策略,这里用默认
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
//跨域的配置
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
//完成最终的映射
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
上面的代码就是根据提供的方法创建HandlerMethod
对象,最后将一些变量put到registry
中去。最终就就完成Url和方法的映射,至此整个mappingRegistry变量就有值了。我们继续看原来的代码,具体的代码如下:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
//先根据Url找到对应的mapping
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
//根据匹配到mapping找到对应的HandlerMethod
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
// 没有找到匹配,再次遍历所有的
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
//取出第一个匹配的
Match bestMatch = matches.get(0);
//如果长度大于1,就进行排序,取出一个最满足条件的HandlerMethod
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)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
//设置好属性
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
//又设置了一个属性
handleMatch(bestMatch.mapping, lookupPath, request);
//将这个HandlerMethod返回
return bestMatch.handlerMethod;
}
else {
//重新再查找一遍
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
上面的代码就是从mappingRegistry
中取出Url对应的mapping
,然后根据这个mapping查找对应的HandlerMethod
返回,如果查不到,就直接返回null,最后将这个HandlerMethod
返回,具体的代码如下:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//如果不为空,就调用createWithResolvedBean方法,这个方法就是判断这个要调用的方法所在的类是否已经创建,如果没有创建,就需要创建。
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
至此整个RequestMappingHandlerMapping
查找Handler
的方法就讲完了。因为后面调用的方法和BeanNameUrlHandlerMapping
调用的方法是一样的,调用的是这两个类的父类中的方法,所以这儿笔者就不概述。笔者还是像上面的BeanNameUrlHandlerMapping
的一样,进行总结一下,具体的如下图:
3.3RouterFunctionMapping
有了前面的例子,笔者还是带大家看下RouterFunctionMapping
这个类的继承结构,看看有没有实现什么Spring的接口,具体的图如下:
这个类实现了InitializingBean
并且写了afterPropertiesSet()
方法,Spring在创建完成Bean后会调用这个方法,这个方法的代码如下:
public void afterPropertiesSet() throws Exception {
//如果这个roterFunction为空,就执行initRouterFunction()
if (this.routerFunction == null) {
initRouterFunction();
}
//默认的时候为空
if (CollectionUtils.isEmpty(this.messageConverters)) {
//初始化消息转换器
initMessageConverters();
}
}
这个时候如果roterFunction
为空,就会执行initRouterFunction()
具体的代码如下:
private void initRouterFunction() {
//先获取容器的环境
ApplicationContext applicationContext = obtainApplicationContext();
//获取Bean类型是RouterFunction
Map<String, RouterFunction> beans =
(this.detectHandlerFunctionsInAncestorContexts ?
BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RouterFunction.class) :
applicationContext.getBeansOfType(RouterFunction.class));
List<RouterFunction> routerFunctions = new ArrayList<>(beans.values());
//打印这些路由规则
if (!CollectionUtils.isEmpty(routerFunctions) && logger.isInfoEnabled()) {
routerFunctions.forEach(routerFunction -> logger.info("Mapped " + routerFunction));
}
//将这个routerFunction收集起来
this.routerFunction = routerFunctions.stream()
.reduce(RouterFunction::andOther)
.orElse(null);
}
上面的代码就是将实现RouterFunction
接口的Bean
给封装到routerFunction
属性中去。最后还是要调用初始化消息转换器。具体的代码如下:
private void initMessageConverters() {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(4);
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
try {
messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
this.messageConverters = messageConverters;
}
上面添加了四个消息转换器,主要是ByteArrayHttpMessageConverter
、StringHttpMessageConverter
、SourceHttpMessageConverter
、AllEncompassingFormHttpMessageConverter
这四个消息转换器。这个时候初始化的流程完成。这个时候继续看找Handler的流程,具体的代码如下:
protected Object getHandlerInternal(HttpServletRequest servletRequest) throws Exception {
//获取请求的Url
String lookupPath = getUrlPathHelper().getLookupPathForRequest(servletRequest);
//设置好属性
servletRequest.setAttribute(LOOKUP_PATH, lookupPath);
//前面初始化routerFunction不为空
if (this.routerFunction != null) {
//根据消息转换器和request创建成ServerRequest
ServerRequest request = ServerRequest.create(servletRequest, this.messageConverters);
//添加对应的属性
servletRequest.setAttribute(RouterFunctions.REQUEST_ATTRIBUTE, request);
//跳转指定的路由,如果没有指定的路由就返回null
return this.routerFunction.route(request).orElse(null);
}
else {
return null;
}
}
上面的代码就是从routerFunction
中找到指定的路由,如果没有指定的路由,就直接返回null。至此所有Spring自带的HandlerMapping已经介绍完了。最后再总结一波,具体的如下图:
4.写在最后
Spring提供的三大HandlerMapping
,然后调用HandlerMapping
中的getHandler
方法,获取对应的Handler
,如果有一个HandlerMapping
中能找到一个Handler
就直接返回,如果没有找到,就继续调用下一个HandlerMapping
的getHandler
方法,直至找到对应Handler
,如果没有找到就直接返回null,同时这几个HandlerMapping
都利用了Spring的扩展点,往对应的HandlerMapping
初始化了一些东西。后面的博客会介绍HandlerAdapter
。