HandlerMapping映射器主要的作用就是将请求和方法映射起来,那么主要考虑:
- 映射器有几种,如何初始化的?
- 既然有了映射器,那么如何将请求和逻辑方法映射起来?
上面的问题简单来说就是请求来了,需要找到处理方法,需要映射器来负责,那么映射器有哪些,它怎么帮我找到方法,那么带着上面的问题,然后有目的性的看源码
2.调用流程SpringMVC的核心类是 DispatcherServlet,其中的核心方式是 doDispatch()方法,在这个方法里面会获取到处理该请求的映射器,那么是怎么被调用到的呢?
我们请求会找到 doGet()和doPost()方法,但是DispatcherServlet类中没有,那么请求首先会到达父类的 frameworkServlet的doGet() 或 doPost() 方法,
// 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);
}
我们发现父类 frameworkServlet的 doXXX 类似的方法都回调用 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);
}
}
我们发现其中最重要的方法 doService()方法,查看该方法
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;
发现这是个抽象方法,于是查看其子类 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 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);
}
}
if (this.parseRequestPath) {
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
}
子类 DispatcherServlet重写了该方法,并且在方法中调用了最最最最重要的方法 doDispatch
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 = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
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);
}
}
}
}
我们在doDispatch()方法里面看到将请求和handle映射起来的方法getHandler(processedRequest) 这也是本篇文章的重点
@Nullable
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;
}
这个方法里面主要有2点,需要我们详细讲述,
- 我们在匹配的时候发现这个 handlerMappings里面是有值得,那么这个值是如何初始化的?
- 这个 handlerMappings存在多个,代表映射器有不同的类型,那么通过 getHandler()进行匹配的时候,不同的映射器,匹配方式也不同,那么如何匹配?
上面我们看到了 handlerMappings里面存在多个值 ,代表多个映射器类型,因为我们接受请求的方式可能不同,所以肯定有多种映射器,那么接下来介绍接受请求的方式,以及HandlerMapping接口以及子类,并且这些是如何初始化的
1、编写接口的方法 1)实现HttpRequestHandler接口@Component("/handler")
public class HandleController implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
System.out.println("实现HttpRequestHandler接口方式");
}
}
2)实现Controller接口
@Component("/name")
public class BeanNameController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("实现Controller接口方式");
return null;
}
}
3)注解方法
@RestController
@RequestMapping("/controller")
public class HelloController {
@Autowired(required = false)
private UserMapper userMapper;
@GetMapping(value = "/test")
public Object test(RequestEntity requestEntity) {
System.out.println("test");
return "哈哈哈";
}
}
2、HandlerMapping 接口以及子类
HandlerMapping接口中包含多个字段和1个最重要的方法 getHandler(HttpServletRequest request) 这个方法就是将请求和Handler进行映射
public interface HandlerMapping {
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
子类
HandlerMapping映射器主要有3个常见的子类:
BeanNameUrlHandlerMapping
RequestMappingHandlerMapping
RouterFunctionMapping
类关系图 3、初始化在DispatcherServlet类中 我们进行初始化,Handler会调用initHandlerMappings(ApplicationContext context) 方法,那么这个类的调用流程是:
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() --> org.springframework.web.servlet.DispatcherServlet#initHandlerMappings()
通过上面的调用流程,我们会调用到这个初始化方法,那么我们来看一下这个方法
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 从spring的容器中取,如果这儿加了@EnableWebMvc注解,这儿取出来的就是三个
Map 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.
// 如果上面从Spring容器中获取不到映射器,那么就调用方法,从配置文件取
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;
}
}
}
上面的代码就是如果加了@EnableWebMvc注解的话,就直接能从spring容器中取出对应的三个HandlerMapping,如果没有加,则调用 getDefaultStrategies() 方法,
protectedList getDefaultStrategies(ApplicationContext context, Class strategyInterface) { if (defaultStrategies == null) { try { // Load default strategy implementations from properties file. // This is currently strictly internal and not meant to be customized // by application developers. // 从默认的配置中 ‘DispatcherServlet.properties’ 取值 ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); // 加载属性 defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage()); } } // 遍历默认的映射器注入 String key = strategyInterface.getName(); String value = defaultStrategies.getProperty(key); if (value != null) { String[] classNames = StringUtils.commaDelimitedListToStringArray(value); List strategies = new ArrayList<>(classNames.length); for (String className : classNames) { try { Class> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); Object strategy = createDefaultStrategy(context, clazz); strategies.add((T) strategy); } catch (ClassNotFoundException ex) { throw new BeanInitializationException( "Could not find DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", ex); } catch (linkageError err) { throw new BeanInitializationException( "Unresolvable class definition for DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", err); } } return strategies; } else { return Collections.emptyList(); } }
上面的代码说如果spring容器中取不到,那么会从默认的配置文件中取,这个配置在通过IDEA中查看jar包 spring-webmvc-5.3.9.jar,打开可以看到其中的一段配置
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中加载,上面两种方式的区别就在于:如果加了@EnableWebMvc注解的话,这三个类就直接受spring的管理了,同时这些Bean会进行一个完整的生命周期。而那种从配置文件中加载的,就没有经历一个完整的生命周期
三、getHandler() 映射方法上面我们介绍了HanlerMapping的分类及初始化,也就是有了映射器了,那么接下来我们介绍如何通过 getHandler()方法将请求和方法映射起来,我们需要分别介绍,不同的映射器方法不同
1、BeanNameUrlHandlerMapping 请求流程第一个是BeanNameUrlHandlerMapping,我们先来看看这个类中的getHandler方法的代码,
注意: HandlerMapping有很多子类,我们可以通过上面的类关系图, 看到 BeanNameUrlHandlerMapping 并没有重写getHandler()方法,于是往上查,发现AbstractDetectingUrlHandlerMapping类 和 AbstractUrlHandlerMapping类 都没有重写,在AbstractHandlerMapping类里面重写了
AbstractUrlHandlerMapping类
@Override
@Nullable
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);
}
// 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;
}
上面的代码通过调用getHandlerInternal(request)方法通过request来获取这个Handler,那么这个方法干了什么具体的代码如下:
这getHandlerInternal() 也有多个子类重写了,我们需要找到其父类 AbstractUrlHandlerMapping中重写的
@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// 获取请求url地址
String lookupPath = initLookupPath(request);
Object handler;
if (usesPathPatterns()) {
RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
handler = lookupHandler(path, lookupPath, request);
}
else {
// 获取handler
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,那么具体的代码如下:
@Nullable
protected Object lookupHandler(String lookupPath, HttpServletRequest request) throws Exception {
// 查找
Object handler = getDirectMatch(lookupPath, request);
// 如果找到了直接返回
if (handler != null) {
return handler;
}
// Pattern match?
List matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, lookupPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", lookupPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
}
// Pattern match?
//正则匹配
//省略一部分代码
// No handler found...
// 没有找到对应的Handler
return null;
}
上面的方法会进行查找,如果找到,则直接返回,代表精确匹配,如果没找到,会进行正则匹配,因为我们的请求允许携带 ” @Override @Nullable protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 获取请求的url String lookupPath = initLookupPath(request); // 加锁 this.mappingRegistry.acquireReadLock(); try { HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { // 解锁 this.mappingRegistry.releaseReadLock(); } }
上面的代码和BeanNameUrlHandlerMapping的代码很相似,上面的核心的方法就是lookupHandlerMethod(lookupPath, request);具体的代码如下:
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
// 创建集合
List matches = new ArrayList<>();
// 这里是重点,直接从集合 mappingRegistry 中取值
List 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 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);
}
}
其中调用了一个 getMappingsByDirectPath() 方法,该方法直接从一个Map中取值
@Nullable public ListgetMappingsByDirectPath(String urlPath) { return this.pathLookup.get(urlPath); }
我们发现这个 mappingRegistry 内部类,其中有很多Map 字段,我们的内容就是从这里取值的
class MappingRegistry {
private final Map> registry = new HashMap<>();
private final MultiValueMap pathLookup = new linkedMultiValueMap<>();
private final Map> nameLookup = new ConcurrentHashMap<>();
private final Map corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// .... 省略其它
}
我们看到 MultiValueMap 不是我们常用的Map,其实它就是继承了Map,然后自己封装了一些方法
public interface MultiValueMap初始化extends Map > { // 省略代码 }
上面也是从一个Map里面取到对应的handler,那么这个Map是如何初始化的呢?我们从RequestMappingHandlerMapping类中看到了 afterPropertiesSet()方法
@Override
@SuppressWarnings("deprecation")
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();
}
上面的方法调用了父类 AbstractHandlerMethodMapping 的afterPropertiesSet()方法
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
那么afterPropertiesSet()这个方法何时被调用,我们再来看下RequestMappingHandlerMapping类的继承的关系,具体的如下图:
可以发现RequestMappingHandlerMappering的父类AbstractHandlerMethodMapping实现InitializeBean的接口
// 凡是继承该接口的类,在初始化的时候都会执行 afterPropertiesSet()
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
所以就会调用到上面的afterPropertiesSet()方法的,在afterPropertiesSet()方法中调用了initHandlerMethods()方法,具体的代码如下:
protected void initHandlerMethods() {
//遍历所有的BeanName
for (String beanName : getCandidateBeanNames()) {
//如果beanName的开头不是scopedTarget.
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// 处理候选的bean
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
上面的代码用到了getCandidateBeanNames() 方法,该方法会获取到所有的bean对象
protected String[] getCandidateBeanNames() {
// 直接从Spring容器中取出所有的Object类型的对象,就代表直接取所有的bean
return (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
}
然后代码会遍历所有的beanName,然后只要开头不是scopedTarget.,就会执行processCandidateBean(beanName),这个方法主要是进行判断,然后处理,具体的代码如下:
protected void processCandidateBean(String beanName) {
Class> beanType = null;
try {
// 获取Class类型
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);
}
}
这里面会通过isHandler()方法判断类上面是否加了@Controller注解或者是@RequestMapping注解
@Override
protected boolean isHandler(Class> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
找到加了@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);
// 这里面通过 selectMethods() 来匹配方法
Map methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.metadataLookup) method -> {
try {
// 通过 getMappingForMethod() 方法创建RequestMappingInfo 添加到结果集中
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);
});
}
}
上面的代码通过selectMethods()方法来查找匹配的方法,具体的代码如下:
public staticMap selectMethods(Class> targetType, final metadataLookup metadataLookup) { // 创建结果集 final Map methodMap = new linkedHashMap<>(); Set > 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中去。具体的代码如下:
@Override
@Nullable
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;
}
getMappingForMethod()这个方法就是解析@RequestMapping注解的数据,然后根据这个@RequestMapping这个注解创建RequestMappingInfo对象返回,至此就方法和请求的地址映射起来,同时存到methodMap中去,最后返回。回到之前的 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);
// 这里面通过 selectMethods() 来匹配方法
Map methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.metadataLookup) method -> {
try {
// 通过 getMappingForMethod() 方法创建RequestMappingInfo 添加到结果集中
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);
// 完成映射,就是将这些的对象包装起来存到mappingRegistry
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
我们继续看最后一步,完成映射的代码,具体就是调用registerHandlerMethod(handler, invocableMethod, mapping);具体的代码如下:
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
继续调用 register() 方法注册,具体的代码如下:
public void register(T mapping, Object handler, Method method) {
// 加写锁
this.readWriteLock.writeLock().lock();
try {
// 将方法包装成HandlerMethod对象
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// 校验
validateMethodMapping(handlerMethod, mapping);
// 获取Url
Set directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
for (String path : directPaths) {
// 将Url和mapping关联起来
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();
}
}
上面的代码就是根据提供的方法创建HandlerMethod对象,最后将一些变量put到registry中去。最终就就完成Url和方法的映射。
简单而说,RequestMappingHandlerMapping初始化,也是把请求地址和Handler提前存到1个Map里面,然后请求的时候遍历查找,
那么这和BeanNameUrlHandlerMapping有啥区别呢,都是存到Map里面,
BeanNameUrlHandlerMapping是需要类实现HttpRequestServlet或者Controller接口,那么这样就1个类
只能实现1个接口,同时这个类又注入到了Spring容器中,就相当于我们直接从所有的bean对象中找到对象名带“/”的,就相当于找到了这个类
RequestMappingHandlerMapping是通过@Controller和@RequestMapping注解来实现,那么一个类中可能存在多个方法,这时候我们就需要先通过
Spring容器遍历出有这两个注解的类,然后通过反射获取方法进行判断,然后注入
有了前面的例子,笔者还是带大家看下RouterFunctionMapping这个类的继承结构,看看有没有实现什么Spring的接口,具体的图如下:
请求流程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;
}
}
初始化
这个类实现了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 beans =
(this.detectHandlerFunctionsInAncestorContexts ?
BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RouterFunction.class) :
applicationContext.getBeansOfType(RouterFunction.class));
List 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> 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这四个消息转换器。这个时候初始化的流程完成。
流程图ion.class) :
applicationContext.getBeansOfType(RouterFunction.class));
ListrouterFunctions = 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`属性中去。最后还是要调用初始化消息转换器。具体的代码如下:
```java
private void initMessageConverters() {
List> 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这四个消息转换器。这个时候初始化的流程完成。
流程图


