这一篇我们来梳理下SpringMVC中关于HandlerMapping接口及其实现类。
首先简单来看下DispatcherServlet对于请求的简单执行流程,了解下大概(关于这个过程的具体细节到时候在看还需不需要来一下具体细节),实现是根据request获取HandlerExecutionChain(其包含根据映射获取到的对应方法或者类handler(Object)通过对应的HandlerMapping获取)、HandlerInterceptor),然后通过HandlerAdapter 的supports判断当前的handler是那种,再获取对应的HandlerAdapter ,然后执行handler获取ModelAndView,再通过processDispatchResult方法处理(有些会要用到ViewResolver去处理) 。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; .......... try { ModelAndView mv = null; Exception dispatchException = null; // Determine handler for the current request. mappedHandler = getHandler(processedRequest); ......... // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); .......... if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); ............ applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } .............. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } .............. }
处理器映射器(HandlerMapping)
1、首先我们来看下这个接口本身
public interface HandlerMapping { String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping"; String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern"; String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping"; String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables"; String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables"; String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes"; @Nullable HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
其除了一些类变量,只有一个方法getHandler,然后其入参是HttpServletReqeust,返回是HandlerExecutionChain,所以这个接口就是根据请求的request返回一个处理执行链,有点类似于Tomcat中ApplicationFilterChain的。
我们现在来看下这个HandlerExecutionChain:
public class HandlerExecutionChain { private final Object handler; @Nullable private HandlerInterceptor[] interceptors; @Nullable private List<HandlerInterceptor> interceptorList; private int interceptorIndex = -1; .................. boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); .................. } void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); .............. } ................ void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) { HandlerInterceptor[] interceptors = getInterceptors(); ............. } }
可以看到这个类主要有两种成员变量handler、HandlerInterceptor,这两个一个是我们获取要处理的方法或者类(这个到后面就能明白这个变量)、还有一个就是拦截器,这个就是我们再请求一个参数的时候首先需要判断拦截器,看能不能让其最终请求到对应的处理,这个也就是类型我们再前面梳理tomcat的时候,首先是经过所有的Filter,通过后才会运行最终Servlet的service方法。
2、现在我们来看下HandlerMapping的实现类
首先是其的抽象实现类:AbstractHandlerMapping
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered { @Nullable private Object defaultHandler; private UrlPathHelper urlPathHelper = new UrlPathHelper(); private PathMatcher pathMatcher = new AntPathMatcher(); private final List<Object> interceptors = new ArrayList<>(); private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>(); private final UrlBasedCorsConfigurationSource globalCorsConfigSource = new UrlBasedCorsConfigurationSource(); ............... @Override @Nullable 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 (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; } @Nullable protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception; protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; } ............ }
可以看到前面获取的HandlerExecutionChain就是这个抽象类的getHandler返回的。然后getHandler方法调用的getHandlerInternal是一个抽象方法,需要其子类实现。
然后是AbstractHandlerMapping的两个主要子类:AbstractUrlHandlerMapping、AbstractHandlerMethodMapping,这两个Abstract类走的是两种路线。
HandlerMapping是怎样使用的
首先我们看下AbstractUrlHandlerMapping的实现子类:
1、SimpleUrlHandlerMapping:
我们通过Spring本身的测试demo来看下这个子类的具体使用:
map2.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> <bean id="urlMapping1" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <!-- <property name="defaultHandler"><ref bean="starController"/></property>--> <!-- <property name="rootHandler"><ref bean="mainController"/></property>--> <property name="urlMap"> <map> <entry key="welcome.html"><ref bean="mainController"/></entry> <entry key="oth*.html"><ref bean="otherController"/></entry> </map> </property> </bean> <bean id="urlMapping2" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <!-- <property name="defaultHandler"><ref bean="starController"/></property>--> <!-- <property name="rootHandler"><ref bean="mainController"/></property>--> <property name="mappings"><ref bean="propsForUrlMapping2"/></property> </bean> <bean id="propsForUrlMapping2" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="location"><value>map2.properties</value></property> </bean> <bean id="urlMapping3" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="urlDecode"><value>true</value></property> <property name="mappings"> <value> welcome.html=mainController star.html=mainController </value> </property> </bean> <bean id="mainController" class="java.lang.Object"/> <bean id="starController" class="java.lang.Object"/> <bean id="otherController" class="java.lang.Object"/> </beans>
map2.properties文件的内容:
welcome.html= mainController oth*.html = otherController
其对应的测试demo
public class SpringSimpleUrlHandlerMappingTest { private XmlWebApplicationContext wac; @Before public void before() { MockServletContext sc = new MockServletContext(""); wac = new XmlWebApplicationContext(); wac.setServletContext(sc); wac.setConfigLocations(new String[] {"map2.xml"}); wac.refresh(); } @Test public void urlMapping1() throws Exception { HandlerMapping hm = (HandlerMapping) wac.getBean("urlMapping1"); MockHttpServletRequest welcomeRequest = new MockHttpServletRequest("GET", "/welcome.html"); MockHttpServletRequest otherRequest = new MockHttpServletRequest("GET", "/other.html"); HandlerExecutionChain welcomeChain = getHandler(hm, welcomeRequest); HandlerExecutionChain otherChain = getHandler(hm, otherRequest); System.out.println(""); }
@Test public void urlMapping2() throws Exception { HandlerMapping hm = (HandlerMapping) wac.getBean("urlMapping2"); MockHttpServletRequest welcomeRequest = new MockHttpServletRequest("GET", "/welcome.html"); MockHttpServletRequest otherRequest = new MockHttpServletRequest("GET", "/other.html"); HandlerExecutionChain welcomeChain = getHandler(hm, welcomeRequest); HandlerExecutionChain otherChain = getHandler(hm, otherRequest); System.out.println(""); } @Test public void urlMapping3() throws Exception { HandlerMapping hm = (HandlerMapping) wac.getBean("urlMapping3"); MockHttpServletRequest welcomeRequest = new MockHttpServletRequest("GET", "/welcome.html"); MockHttpServletRequest otherRequest = new MockHttpServletRequest("GET", "/star.html"); HandlerExecutionChain welcomeChain = getHandler(hm, welcomeRequest); HandlerExecutionChain otherChain = getHandler(hm, otherRequest); System.out.println(""); } private static HandlerExecutionChain getHandler(HandlerMapping hm, MockHttpServletRequest req) throws Exception { HandlerExecutionChain hec = hm.getHandler(req); HandlerInterceptor[] interceptors = hec.getInterceptors(); if (interceptors != null) { for (HandlerInterceptor interceptor : interceptors) { interceptor.preHandle(req, null, hec.getHandler()); } } return hec; }
这里的MockHttpServletRequest我们可以看做一个request类似(最开始mappedHandler = getHandler(processedRequest)的processedRequest),你也可以将前面map.xml的内容写到tomcat项目的spring配置文件中这样去测试。然后上面的getHandler方法类似于DispatcherServlet的源码:
@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping hm : this.handlerMappings) { HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } } return null; }
可以看到DispatcherServlet的源码是需要遍历HandlerMapping,而这里demo测试是直接传入的HandlerMapping。
而map.xml中 <bean id="mainController" class="java.lang.Object"/> 这个class = Object也只是一个代表。你如果正常使用需要使用例如基础Controller接口、HttpRequestHandler、Servlet接口等的实现类(这个就是前面讲的handler,具体为什么是这些,到源码梳理的时候再进行说明):
public class RequestController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { return new ModelAndView(); } } public class HttpRequestHandlerController implements HttpRequestHandler { @Override public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException { System.out.println(""); } }
这里我们通过urlMapping1、urlMapping2、urlMapping3,分别介绍了BeanNameUrlHandlerMapping三种用法。
2、BeanNameUrlHandlerMapping:(同样我们来看下其在spring源码测试的使用demo)
map1.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <bean id="godCtrl" name="/ /mypath/welcome.html /mypath/show.html /mypath/bookseats.html /mypath/reservation.html /mypath/payment.html /mypath/confirmation.html /mypath/test*" class="java.lang.Object"/> </beans>
public class SpringBeanNameUrlHandlerMappingTest { private ConfigurableWebApplicationContext wac; @Before public void setUp() throws Exception { MockServletContext sc = new MockServletContext(""); wac = new XmlWebApplicationContext(); wac.setServletContext(sc); wac.setConfigLocation("map1.xml"); wac.refresh(); } @Test public void handlerMapping1() throws Exception { HandlerMapping hm = (HandlerMapping) wac.getBean("handlerMapping"); MockHttpServletRequest req = new MockHttpServletRequest("GET", "/mypath/welcome.html"); HandlerExecutionChain hec = hm.getHandler(req); Object handler = hec.getHandler(); System.out.println(""); } }
这个就是BeanNameUrlHandlerMapping的用法,可能首先看到map1.xml中的配置比较奇怪,因为并没有将BeanNameUrlHandlerMapping与对应的handler建立联系,但其其是这里的特殊就是,<bean>标签的的name属性有多个name(以空格分开),每个name就是对应当前bean的别名(同时别名也可以用alias 其设置)。这个BeanNameUrlHandlerMapping的作用就是去判断bean标签的name有没有以"/"开头的,如果以"/"开头就将其注册到BeanNameUrlHandlerMapping中,建立映射关系。
在Spring中关于AbstractUrlHandlerMapping子类的实现就是这两个,下面我们来看下AbstractHandlerMethodMapping子类的使用。
1、RequestMappingHandlerMapping
AbstractUrlHandlerMapping其还有一个Abstract子类:RequestMappingInfoHandlerMapping其是一个抽象类(很奇怪,按Spring的命名规则,应该是以Abstract开头的),然后RequestMappingHandlerMapping继承的是RequestMappingInfoHandlerMapping。所以其实AbstractHandlerMethodMapping这个在SpringMVC中去只有一个实现类就是RequestMappingHandlerMapping,这个RequestMappingHandlerMapping的使用就是通过@Controller、@RequestMapping注解注入的映射方法,我们通过AbstractHandlerMethodMapping这个名词,其有Method。
例如我们再springmvc配置的xml文件中:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <context:component-scan base-package="web" /> <mvc:annotation-driven/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="jspViewResolver"> <property value="org.springframework.web.servlet.view.JstlView" name="viewClass"/> <property value="/WEB-INF/" name="prefix"/> <property value=".jsp" name="suffix"/> </bean> </beans>
再在对应的web包下创建SpringController类:
@Controller public class SpringController{ @RequestMapping(value = "/spring", method = RequestMethod.GET) public String springMethod() { return "springMethod"; } }
再在/WEB-INF/目录下创建名词为springMethod的jsp文件:
springMethod.jsp文件
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%> <h1>Hello World</h1>
HandlerMapping的源码解析
我们在上面讲过HandlerMapping这个接口 本身就是一个getHandler(HttpServletRequest request)方法,通过request获取HandlerExecutionChain。
public interface HandlerMapping { @Nullable HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
1、首先我们看下其的基本实现AbstractHandlerMapping
通过这个结构关系,我们看到有两个Aware接口:ServletContextAware、ApplicationContextAware。这两个Awre接口就是去获取容器本身ApplicationContext以及ServletContext。然后AbstractHandlerMapping应该能从这两个组件中获取一些信息去处理。
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered { @Nullable private Object defaultHandler; private UrlPathHelper urlPathHelper = new UrlPathHelper(); private PathMatcher pathMatcher = new AntPathMatcher(); private final List<Object> interceptors = new ArrayList<>(); private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>(); private final UrlBasedCorsConfigurationSource globalCorsConfigSource = new UrlBasedCorsConfigurationSource(); private CorsProcessor corsProcessor = new DefaultCorsProcessor(); ............. public void setDefaultHandler(@Nullable Object defaultHandler) { this.defaultHandler = defaultHandler; } ............. @Nullable public Object getDefaultHandler() { return this.defaultHandler; } public void setInterceptors(Object... interceptors) { this.interceptors.addAll(Arrays.asList(interceptors)); } ..............(cors跨域相关) @Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.adaptedInterceptors); initInterceptors(); } protected void extendInterceptors(List<Object> interceptors) { } protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( obtainApplicationContext(), MappedInterceptor.class, true, false).values()); } protected void initInterceptors() { if (!this.interceptors.isEmpty()) { for (int i = 0; i < this.interceptors.size(); i++) { Object interceptor = this.interceptors.get(i); if (interceptor == null) { throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); } this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } } protected HandlerInterceptor adaptInterceptor(Object interceptor) { if (interceptor instanceof HandlerInterceptor) { return (HandlerInterceptor) interceptor; } else if (interceptor instanceof WebRequestInterceptor) { return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor); } else { throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName()); } } /** * Return all configured {@link MappedInterceptor}s as an array. * @return the array of {@link MappedInterceptor}s, or {@code null} if none */ @Nullable protected final MappedInterceptor[] getMappedInterceptors() { List<MappedInterceptor> mappedInterceptors = new ArrayList<>(); for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { mappedInterceptors.add((MappedInterceptor) interceptor); } } int count = mappedInterceptors.size(); return (count > 0 ? mappedInterceptors.toArray(new MappedInterceptor[count]) : null); } @Override @Nullable 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); .............(cors跨域处理) return executionChain; } @Nullable protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception; protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; } .............. }
可以看到这里的关键成员变量有defaultHandler(默认的handler)、interceptors(拦截器)(cors跨域先跳过),
1、然后有getHandler(HttpServletRequest request)方法
这个方法就是获取handler,然后有abstract方法getHandlerInternal,其子类实现。如果getHandlerInternal方法没有获取到就去获取defaultHandler,同时这里如果可以获取到的话如果handler是String类型,还需要从applicationContext中获取对应的handler Bean。
2、getHandlerExecutionChain方法:
这个方法是获取HandlerExecutionChain,首先是将handler设置到HandlerExecutionChain中,再通过urlPathHelper获取请求的对应处理的地址,然后再判断有哪些MappedInterceptor是拦截该handler的,如果是就将该MappedInterceptor添加到HandlerExecutionChain中。
然后这里就有一个疑问了,这个MappedInterceptor是什么时候设置进来的呢?这个就要回到前面讲的ApplicationContextAware接口,这个接口就是将ApplicationContext设置到当前对象,然后其有initApplicationContext方法
@Override public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException { ............ initApplicationContext(context); } ........... }
然后调用到AbstractHandlerMapping的initApplicationContext方法
@Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.adaptedInterceptors); initInterceptors(); }
extendInterceptors方法是一个空方法,detectMappedInterceptors方法:
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( obtainApplicationContext(), MappedInterceptor.class, true, false).values()); }
这个方法就是从容器中获取MappedInterceptor接口的实现类,将其添加到mappedInterceptors中
public interface HandlerInterceptor { default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }
再调用initInterceptors方法将interceptors添加到adaptInterceptor中
protected void initInterceptors() { if (!this.interceptors.isEmpty()) { for (int i = 0; i < this.interceptors.size(); i++) { Object interceptor = this.interceptors.get(i); if (interceptor == null) { throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); } this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } } protected HandlerInterceptor adaptInterceptor(Object interceptor) { if (interceptor instanceof HandlerInterceptor) { return (HandlerInterceptor) interceptor; } else if (interceptor instanceof WebRequestInterceptor) { return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor); } else { throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName()); } } public WebRequestHandlerInterceptorAdapter(WebRequestInterceptor requestInterceptor) { Assert.notNull(requestInterceptor, "WebRequestInterceptor must not be null"); this.requestInterceptor = requestInterceptor; }
这里有区分如果是WebRequestInterceptor的Interceptor将其转换到适配器WebRequestHandlerInterceptorAdapter。同时在前面的getHandlerExecutionChain有区分是不是MappedInterceptor:
public final class MappedInterceptor implements HandlerInterceptor { @Nullable private final String[] includePatterns; @Nullable private final String[] excludePatterns; private final HandlerInterceptor interceptor; @Nullable private PathMatcher pathMatcher; ............. public boolean matches(String lookupPath, PathMatcher pathMatcher) { PathMatcher pathMatcherToUse = (this.pathMatcher != null ? this.pathMatcher : pathMatcher); if (!ObjectUtils.isEmpty(this.excludePatterns)) { for (String pattern : this.excludePatterns) { if (pathMatcherToUse.match(pattern, lookupPath)) { return false; } } } if (ObjectUtils.isEmpty(this.includePatterns)) { return true; } for (String pattern : this.includePatterns) { if (pathMatcherToUse.match(pattern, lookupPath)) { return true; } } return false; } ........ }
如果是MappedInterceptor(HandlerInterceptor 接口的实现类)要调用matchers判断,这里就能变量名知道其的含义,例如放在includePatterns的就代表要使用interceptor,如果是excludePatterns中的地址就代表不要使用当前interceptor。
2、AbstractHandlerMapping类的子类
1、AbstractUrlHandlerMapping
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping { @Nullable private Object rootHandler; private boolean useTrailingSlashMatch = false; private boolean lazyInitHandlers = false; private final Map<String, Object> handlerMap = new LinkedHashMap<>(); ............. @Override @Nullable protected Object getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); ................ return handler; } @Nullable 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); } ........... return null; } ............... 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); } } protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Object resolvedHandler = handler; ................ } ................. }
我们看到关于AbstractUrlHandlerMapping其有两个主要的成员变量:rootHandler、handlerMap。handlerMap就是放对应的请求地址与对应的处理器handler(类似于配置的Servlet请求地址与对应的Servlet)的映射关系。
那这个是怎样的put的呢,这里就使用了registerHandler方法,这个方法的调用是在实现其的子类中调用的,我们看下这个方法的具体逻辑:
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { 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("/")) { setRootHandler(resolvedHandler); } else if (urlPath.equals("/*")) { setDefaultHandler(resolvedHandler); } else { this.handlerMap.put(urlPath, resolvedHandler); } } }
如果传入的handler是String类型,并且不需要懒加载Handler,就将其从applicationContext中获取处理。再从handlerMap中获取,如果能获取出来,但这两个不相等就报错,如果不能获取出来看urlPath,如果是"/",将将当前Handler赋值到RootHandler,如果是"/*"就赋值到DefaultHandler,都不是就按正常的处理put到handlerMap中。
我们梳理了handlerMap的put,接下来我们就来梳理这个handlerMap的使用,这里就是AbstractUrlHandlerMapping实现的其父类的抽象方法getHandlerInternal:
protected Object getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); Object handler = lookupHandler(lookupPath, request); if (handler == null) { Object rawHandler = null; if ("/".equals(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; }
这里是先通过UrlPathHelper获取请求的地址,这个getUrlPathHelper().getLookupPathForRequest(request)方法就不具体来梳理了,简单说一下这里面的处理,首先需要了解一个request中不同方法获取不同的地址,可以看下这篇文章、或者这篇。然后这里面还有两个知识点,一个就是你你一个正常的请求例如http://localhost:8080/demo1/spring/hello。但其实你是可以写成这样的: http://localhost:8080//demo1//spring//hello,就是说spring会将你请求的requset中的"//"转换为"/"。第二个就我记得原来看关于HTTP相关的书上,一个http的url中是可以有";",这种用法不是很常见,我也忘了,不过在getLookupPathForRequest方法中是会有对应的处理的。
获取到请求的lookupPath后(这个就是去掉了表示项目名称后的表示获取servlet的地址,我们知道tomcat有四种容器,Host下面可以有多个Context,所以你正常的一个请求是:域名+Context对应的项目名+对应servlet(相当于springMVC中对应获取handler的地址)的请求地址)。这里的lookupPath就是对应的servlet的请求地址。
然后根据这个lookupPath去获取对应的handler。如果你请求的是正常的设置,例如我们前面讲的请求地址是welcome.html,就能直接从handlerMap获取到。
<entry key="welcome.html"><ref bean="mainController"/></entry>
<entry key="oth*.html"><ref bean="otherController"/></entry>
但如果请求的是other.html,这个时候由于在handlerMap注册的urlPath是oth*.html,不能直接获取到,所以就需要走下一步。
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? List<String> matchingPatterns = new ArrayList<>(); for (String registeredPattern : this.handlerMap.keySet()) { if (getPathMatcher().match(registeredPattern, urlPath)) { matchingPatterns.add(registeredPattern); } else if (useTrailingSlashMatch()) { if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) { matchingPatterns.add(registeredPattern +"/"); } } } String bestMatch = null; Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath); if (!matchingPatterns.isEmpty()) { matchingPatterns.sort(patternComparator); bestMatch = matchingPatterns.get(0); } if (bestMatch != null) { handler = this.handlerMap.get(bestMatch); if (handler == null) { if (bestMatch.endsWith("/")) { handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1)); } if (handler == null) { throw new IllegalStateException( "Could not find handler for best pattern match [" + bestMatch + "]"); } } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } validateHandler(handler, request); String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath); Map<String, String> uriTemplateVariables = new LinkedHashMap<>(); for (String matchingPattern : matchingPatterns) { if (patternComparator.compare(bestMatch, matchingPattern) == 0) { Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath); Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars); uriTemplateVariables.putAll(decodedVars); } } return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables); } // No handler found... return 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; }
这里主要分为三部分,第一部分就是正常获取handler例如welcome.html,并通过buildPathExposingHandler方法产生一个HandlerExecutionChain 直接返回。如果不能直接获取到,就遍历handlerMap,进行
模糊匹配,通过getPathMatcher().match(registeredPattern, urlPath)方法,如果成功就添加到matchingPatterns中,如果失败就再走下面的useTrailingSlashMatch()
public boolean useTrailingSlashMatch() { return this.useTrailingSlashMatch; }
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) { matchingPatterns.add(registeredPattern +"/"); }
这个值你能在.xml文间中设置,例如:
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="defaultHandler"><ref bean="starController"/></property> <property name="rootHandler"><ref bean="mainController"/></property> <property name="useTrailingSlashMatch" value="true"/> <property name="urlMap"> ............... </property> </bean>
这个值表示的意思就是使不使用未斜杠匹配,通过前面的源码我们可以知道路径的匹配是进行字符串的比较,所以每个字符符号都用意义。例如你如果请求 http://localhost:8080/demo1/spring/hello,这个地址是正确的,在换一下,在结尾加“/”:http://localhost:8080/demo1/spring/hello/,这个时候如果useTrailingSlashMatch还是默认的false的话,就会报404,如果useTrailingSlashMatch为true,就能正确请求到目标,这个通过源码我们也能理解。
通过模糊匹配,由于是模糊,其就可能有多个,所以这里就会进行优先级,然后找到一个最符合的。确定到对应的handler后,会判断是不是String,在前面abstractHandlerMapping我们有梳理过,一个Handler可以使用延迟加载。现在就会在这里去加载到handler了。获取到handler后(validateHandler(handler, request) 为空方法),会通过
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
获取到标准映射,这里会用到Spring的一个工具方法去切割
String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true);
通过这个去试下看其是怎样切割的: String[] sa = StringUtils.tokenizeToStringArray("a,b , ,c", ",", true, false) 。
public String extractPathWithinPattern(String pattern, String path) { String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true); String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true); StringBuilder builder = new StringBuilder(); boolean pathStarted = false; for (int segment = 0; segment < patternParts.length; segment++) { String patternPart = patternParts[segment]; if (patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) { for (; segment < pathParts.length; segment++) { if (pathStarted || (segment == 0 && !pattern.startsWith(this.pathSeparator))) { builder.append(this.pathSeparator); } builder.append(pathParts[segment]); pathStarted = true; } } } return builder.toString(); }
同时这里handlerMap中所以的urlPath都是有"/"的,不管你有没有配置,这个可以在AbstractUrlHandlerMapping的子类看下。
之后还有两个方法:
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath); Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
其中getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath)方法是提取url中模块参数,这个我们看下Spring源码中其的测试demo就可以知道其的作用了:
Map<String, String> result = pathMatcher.extractUriTemplateVariables("/hotels/{hotel}", "/hotels/1"); assertEquals(Collections.singletonMap("hotel", "1"), result); result = pathMatcher.extractUriTemplateVariables("/h?tels/{hotel}", "/hotels/1"); assertEquals(Collections.singletonMap("hotel", "1"), result); result = pathMatcher.extractUriTemplateVariables("/hotels/{hotel}/bookings/{booking}", "/hotels/1/bookings/2"); Map<String, String> expected = new LinkedHashMap<>(); expected.put("hotel", "1"); expected.put("booking", "2"); assertEquals(expected, result);
而我们再xml的使用:
<map> <entry key="welcome*"><ref bean="otherController"/></entry> <entry key="/hotels/{hotel}"><ref bean="mainController"/></entry> </map>
然后模拟请求:MockHttpServletRequest req = new MockHttpServletRequest("GET", "/hotels/你好吗");
之后是getUrlPathHelper().decodePathVariables(方法是通过解码(根据对应的字符编码,request中的characterEncoding)方法获取对应的模板参数。关于这个方法,我们知道我们在url栏中输入汉字,其在请求的时候并不是直接传入汉字,而是对应的汉字asicc码,同时你如果传入空格也是会有转换的,例如你在地址栏输入:
但其在请求时是传入:
decodePathVariables 方法最后是调用Utils.decode("", CHARSET))方法去处理,我们看下测试demo:
@Test public void decode() throws UnsupportedEncodingException { assertEquals("Invalid encoded URI", "", UriUtils.decode("", CHARSET)); assertEquals("Invalid encoded URI", "\u4f60\u597d", UriUtils.decode("你好", CHARSET)); assertEquals("Invalid encoded URI", "foobar", UriUtils.decode("foobar", CHARSET)); assertEquals("Invalid encoded URI", "foo bar", UriUtils.decode("foo%20bar", CHARSET)); assertEquals("Invalid encoded URI", "foo+bar", UriUtils.decode("foo%2bbar", CHARSET)); }
例如我们将前面的模拟请求改下:
MockHttpServletRequest req = new MockHttpServletRequest("GET", "/hotels/%E4%BD%A0%E5%A5%BD"); req.setCharacterEncoding("UTF-8");
public Map<String, String> decodePathVariables(HttpServletRequest request, Map<String, String> vars) { if (this.urlDecode) { return vars; } else { Map<String, String> decodedVars = new LinkedHashMap<>(vars.size()); for (Entry<String, String> entry : vars.entrySet()) { decodedVars.put(entry.getKey(), decodeInternal(request, entry.getValue())); } return decodedVars; } } private String decodeInternal(HttpServletRequest request, String source) { String enc = determineEncoding(request); try { return UriUtils.decode(source, enc); } .......... return URLDecoder.decode(source); } }
不过要注意的是,这个使用了UrlPathHelper的成员变量urlDecode,这个urlDecode默认是为true的,所以一般这里会跳过,如果要使用这个的话,需要自动去配置这个:
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="urlDecode"><value>true</value></property> ..........
这两个方法之后是调用前面讲到构建HandlerExecutionChain的buildPathExposingHandler方法
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; }
这里构建了两个Interceptor(如果不为空的话):UriTemplateVariablesHandlerInterceptor、PathExposingHandlerInterceptor,都继承与HandlerInterceptorAdapter
private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter { private final String bestMatchingPattern; private final String pathWithinMapping; public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) { this.bestMatchingPattern = bestMatchingPattern; this.pathWithinMapping = pathWithinMapping; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request); request.setAttribute(INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings()); return true; } }
private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter { private final Map<String, String> uriTemplateVariables; public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) { this.uriTemplateVariables = uriTemplateVariables; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { exposeUriTemplateVariables(this.uriTemplateVariables, request); return true; } }
这里我们梳理了关于AbstractUrlHandlerMapping抽象类的两个重要的地方,handlerMap的注册,以及通过通过request获取定handler的过程,现在我们来看其的实现类。
SimpleUrlHandlerMapping
这个我们举例用到就是这个
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="defaultHandler"><ref bean="starController"/></property> <property name="rootHandler"><ref bean="mainController"/></property> <property name="useTrailingSlashMatch" value="true"/> <property name="urlDecode"><value>true</value></property> <property name="urlMap"> <map> <entry key="welcome*"><ref bean="otherController"/></entry> ........... <entry key="/hotels/{hotel}"><ref bean="mainController"/></entry> </map> </property> </bean>
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping { private final Map<String, Object> urlMap = new LinkedHashMap<>(); ................ @Override public void initApplicationContext() throws BeansException { super.initApplicationContext(); registerHandlers(this.urlMap); } protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); } else { urlMap.forEach((url, handler) -> { // Prepend with slash if not already present. if (!url.startsWith("/")) { url = "/" + url; } // Remove whitespace from handler bean name. if (handler instanceof String) { handler = ((String) handler).trim(); } registerHandler(url, handler); }); } } }
可以看到其主要是registerHandlers方法,去注册url,与handler的关系,这个registerHandler(url, handler)方法就会调用到其父类我们刚才梳理的AbstractUrlHandlerMapping的registerHandler方法。
可以看到这里的成员变量urlMap 的内容就是我们在xml中配置的
<property name="urlMap"> <map> <entry key="welcome*"><ref bean="otherController"/></entry> .......... <entry key="/hotels/{hotel}"><ref bean="mainController"/></entry> </map> </property>
同时这里也填了一个前面讲的一个地方,不管你有没有在xml中加"/",最好到handlerMap最前面都有"/"。
然后这个registerHandlers方法是通过initApplicationContext方法的调用,同时我们也知道AbstractHandlerMapping是有实现ApplicationContextAware接口,然后这个initApplicationContext方法就是在设置ApplicationContext是调用的:
@Override public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException { ............... else if (this.applicationContext == null) { ............ this.applicationContext = context; this.messageSourceAccessor = new MessageSourceAccessor(context); initApplicationContext(context); } ........... }
BeanNameUrlHandlerMapping
BeanNameUrlHandlerMapping继承与AbstractDetectingUrlHandlerMapping,而AbstractDetectingUrlHandlerMapping继承与AbstractUrlHandlerMapping:
public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping { private boolean detectHandlersInAncestorContexts = false; ......... @Override public void initApplicationContext() throws ApplicationContextException { super.initApplicationContext(); detectHandlers(); } ............. protected void detectHandlers() throws BeansException { ApplicationContext applicationContext = obtainApplicationContext(); ........... 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. registerHandler(urls, beanName); } else { if (logger.isDebugEnabled()) { logger.debug("Rejected bean name '" + beanName + "': no URL paths identified"); } } } } ......... protected abstract String[] determineUrlsForHandler(String beanName); }
可以看到其有一个抽象方法determineUrlsForHandler,BeanNameUrlHandlerMapping就是实现这个determineUrlsForHandler
方法:
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping { @Override protected String[] determineUrlsForHandler(String beanName) { ......... } }
所以BeanNameUrlHandlerMapping是与上面的SimpleUrlHandlerMapping方法类似通过调用initApplicationContext,再调用detectHandlers去将其的handler注册到handlerMap中去。
我们在前面梳理BeanNameUrlHandlerMapping的使用时用的配置是:
<beans> <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <bean id="godCtrl" name="/ /mypath/welcome.html /mypath/show.html /mypath/bookseats.html /mypath/reservation.html /mypath/payment.html /mypath/confirmation.html /mypath/test*" class="java.lang.Object"/> </beans>
然后我们来看detectHandlers方法,其首先是调用的 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) : applicationContext.getBeanNamesForType(Object.class))方法,可以看到其传入的类型时Object.class,所以其实这个就是获取applicationContext中使用的Bean了。
在循环遍历beanName:调用determineUrlsForHandler方法:
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或者其别名是不是以"/"开头,如果是就加入到urls,再返回给detectHandlers方法,之后再将其put到AbstractUrlHandlerMapping的handlerMap中。
2、AbstractHandlerMethodMapping
与上面的AbstractUrlHandlerMapping类似,我们也从其关于handler映射关系的注册以及获取来梳理其逻辑。
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean { ............. private boolean detectHandlerMethodsInAncestorContexts = false; @Nullable private HandlerMethodMappingNamingStrategy<T> namingStrategy; private final MappingRegistry mappingRegistry = new MappingRegistry(); ........... @Override public void afterPropertiesSet() { initHandlerMethods(); } protected void initHandlerMethods() { String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) : obtainApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { ........ if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods()); } protected void detectHandlerMethods(final Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { final Class<?> userType = ClassUtils.getUserClass(handlerType); ............. registerHandlerMethod(handler, invocableMethod, mapping); } } } protected HandlerMethod createHandlerMethod(Object handler, Method method) { HandlerMethod handlerMethod; if (handler instanceof String) { String beanName = (String) handler; handlerMethod = new HandlerMethod(beanName, obtainApplicationContext().getAutowireCapableBeanFactory(), method); } else { handlerMethod = new HandlerMethod(handler, method); } return handlerMethod; } .......... @Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); ......... HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); ........... return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } } .......... protected abstract boolean isHandler(Class<?> beanType); @Nullable protected abstract T getMappingForMethod(Method method, Class<?> handlerType); .......... @Nullable protected abstract T getMatchingMapping(T mapping, HttpServletRequest request); ............. class MappingRegistry { ............ public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping); this.mappingLookup.put(mapping, handlerMethod); ........... } .................. }
我们可以看到AbstractHandlerMethodMapping的初始化注册是通过afterPropertiesSet调用initHandlerMethods进行的(AbstractUrlHandlerMapping是通过ApplicationContextAware进行的)。
@Override public void afterPropertiesSet() { initHandlerMethods(); } protected void initHandlerMethods() { String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) : obtainApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { Class<?> beanType = null; try { beanType = obtainApplicationContext().getType(beanName); } .......... if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods()); } private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";
这里有一个参数detectHandlerMethodsInAncestorContexts(从不从其的祖先搜索,默认是false,我们知道Factory可以有实现HierarchicalBeanFactory接口,可以设置其的parent):
public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class<?> type) { String[] result = lbf.getBeanNamesForType(type); if (lbf instanceof HierarchicalBeanFactory) { HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf; if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) { String[] parentResult = beanNamesForTypeIncludingAncestors( (ListableBeanFactory) hbf.getParentBeanFactory(), type); List<String> resultList = new ArrayList<>(); resultList.addAll(Arrays.asList(result)); for (String beanName : parentResult) { if (!resultList.contains(beanName) && !hbf.containsLocalBean(beanName)) { resultList.add(beanName); } } result = StringUtils.toStringArray(resultList); } } return result; }
可以看到这里的beanType是Objecy.class,所以是获取BeanFactory中所有的beanName。
获取beanName后就遍历,条件时beanName不以"scopedTarget."开头,这个就是表示bean 是ScopedProxy,这个ScopedProxy,前面已经梳理过了好像,在通过BeanFactory获取bean的时候。简单来说,就是bean的作用域,例如单例、多例,request、session这种。而request这种是一次连接创建一次,这种就会用ScopedProxy。
然后beanName获取对应类型beanType,再判断其是不是一个handler,通过isHandler(beanType),其实一个抽象方法:
protected abstract boolean isHandler(Class<?> beanType);
如果为true 则调用detectHandlerMethods(beanName):
protected void detectHandlerMethods(final Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { final Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { return getMappingForMethod(method, userType); } ......... }); for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); registerHandlerMethod(handler, invocableMethod, mapping); } } }
首先获取其类型,然后
public static Class<?> getUserClass(Class<?> clazz) { if (clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) { Class<?> superclass = clazz.getSuperclass(); if (superclass != null && Object.class != superclass) { return superclass; } } return clazz; }
/** The CGLIB class separator: "$$" */ public static final String CGLIB_CLASS_SEPARATOR = "$$";
这个方法就是获取其实际的类,去掉前缀描叙。然后调用MethodIntrospector.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; if (!Proxy.isProxyClass(targetType)) { handlerTypes.add(targetType); specificHandlerType = targetType; } handlerTypes.addAll(Arrays.asList(targetType.getInterfaces())); for (Class<?> currentHandlerType : handlerTypes) { final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType); ReflectionUtils.doWithMethods(currentHandlerType, method -> { Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); T result = metadataLookup.inspect(specificMethod); if (result != null) { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) { methodMap.put(specificMethod, result); } } }, ReflectionUtils.USER_DECLARED_METHODS); } return methodMap; }
(MethodIntrospector.MetadataLookup<T>) method -> { try { return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } });
首先判断其是不是JDK动态代理类,如果不是就添加到handlerTypes中,再将这个targetType实现的接口也添加到handlerTypes中并且设置specificHandlerType,然后遍历。
然后整个这里比较绕(如果是一般情况也不复杂):
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType); ReflectionUtils.doWithMethods(currentHandlerType, method -> { Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); T result = metadataLookup.inspect(specificMethod); if (result != null) { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) { methodMap.put(specificMethod, result); } } }, ReflectionUtils.USER_DECLARED_METHODS);
这里的metadataLookup.inspect就是前面的lamda表达式,实际调用的就是前面的,其是一个抽象类,需要子类实现(这个方法比较重要)
return getMappingForMethod(method, userType)
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
这里的ClassUtils.getMostSpecificMethod(method, targetClass),就是获取具体的方法,这个
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) { if (isOverridable(method, targetClass) && targetClass != null && targetClass != method.getDeclaringClass()) { try { if (Modifier.isPublic(method.getModifiers())) { try { return targetClass.getMethod(method.getName(), method.getParameterTypes()); }....... } else { Method specificMethod = ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes()); return (specificMethod != null ? specificMethod : method); } }.......... } return method; }
这里如果是一般情况直接返回原method。但如果是静态代理这种,其需要返回代理类的方法。用一个例子来说明:
public interface Car { String getName(); } @Controller public class CarProxy implements Car { private Car car; @Override @RequestMapping("/car") public String getName() { System.out.println("Execution CarProxy log: "); return car.getName(); } public void setCar(Car car) { this.car = car; } }
然后我们来看这个过程:
先将specificHandlerType设置为CarProxy.class,在将两个都加入到handlerTypes中:
遍历调用ReflectionUtils.doWithMethods,再调用ClassUtils.getMostSpecificMethod(method, targetClass),这个
targetClass一直是Carproxy (targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType)),所以在处理Carproxy 的时候由于 targetClass != method.getDeclaringClass() 为false所以返回的为原Carproxy 的getName方法,当遍历到Car的时候,由于targetClass为Carproxy.class,所以其实际也是Carproxy的getName方法(这才是正确的,因为我们实际就是需要调用Carproxy)。
这里梳理后我们再回到前面的selectMethods方法,获取specificMethod后,再通过metadataLookup.inspect(specificMethod)获取result,如果不为空(与前面的抽象方法getMappingForMethod有关联),然后获取该specificMethod的桥方法(这里主要是针对泛型的)。
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
findBridgedMethod方法调用,如果specificMethod不是桥方法就直接返回specificMethod本身。如果是桥方法,就去获取其的准确方法(没有抹掉参数类型)。然后添加到methodMap返回。
再回到前面的detectHandlerMethods方法, 获取到methods之后再遍历将对应关系注册。
for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); registerHandlerMethod(handler, invocableMethod, mapping); }
获取到invocableMethod,调用registerHandlerMethod:
protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); } private final MappingRegistry mappingRegistry = new MappingRegistry(); class MappingRegistry { private final Map<T, MappingRegistration<T>> registry = new HashMap<>(); private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>(); private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>(); private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>(); private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>(); private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); ....... } public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping); ........ this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } ............ this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
这里使用了可重入的读写锁ReentrantReadWriteLock,通过createHandlerMethod方法,构建一个HandlerMethod:
protected HandlerMethod createHandlerMethod(Object handler, Method method) { HandlerMethod handlerMethod; if (handler instanceof String) { String beanName = (String) handler; handlerMethod = new HandlerMethod(beanName, obtainApplicationContext().getAutowireCapableBeanFactory(), method); } else { handlerMethod = new HandlerMethod(handler, method); } return handlerMethod; } public HandlerMethod(Object bean, Method method) { this.bean = bean; this.beanFactory = null; this.beanType = ClassUtils.getUserClass(bean); this.method = method; this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.parameters = initMethodParameters(); evaluateResponseStatus(); } private MethodParameter[] initMethodParameters() { int count = this.bridgedMethod.getParameterCount(); MethodParameter[] result = new MethodParameter[count]; for (int i = 0; i < count; i++) { HandlerMethodParameter parameter = new HandlerMethodParameter(i); GenericTypeResolver.resolveParameterType(parameter, this.beanType); result[i] = parameter; } return result; }
这个HandlerMethod就是面试处理方法的,例如这个方法有哪些参数等,再通过assertUniqueMethodMapping(handlerMethod,,mapping)方法判断handlerMethod与mapping是不是唯一映射关系,再将这个关系put到mappingLookup中。
然后是getDirectUrls,这个到其子类的时候再梳理,其与(getMappingForMethod相关)(就是看一个mapping有多少地址与其对应),将其添加到urlLookup中。之后再看有没有设置namingStrategy,如果有,就根据handlerMethod与mapping,获取对应的name,建立name与handlerMethod的关系,再通过addMappingName将其put到nameLookup中
(Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>())。
然后再将mapping与MappingRegistration建立联系,将其put到registry 中
(Map<T, MappingRegistration<T>> registry = new HashMap<>())。
至此,handleMethod与mapping就完成注册了。下面我们再来看下获取handler的 getHandlerInternal方法:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); this.mappingRegistry.acquireReadLock(); try { HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
这个通过request获取对应handler路径与前面AbstractUrlHandlerMapping的一样,通过UrlPathHelper去获取,现在看lookupHandlerMethod方法:
@Nullable protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { 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()) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator); Match bestMatch = matches.get(0); if (matches.size() > 1) { ............ Match secondBestMatch = matches.get(1); if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.handlerMethod.getMethod(); Method m2 = secondBestMatch.handlerMethod.getMethod(); throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" + m1 + ", " + m2 + "}"); } } handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }
@Nullable protected HandlerMethod handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request) throws Exception { return null; }
首先是根据lookupPath获取urlLookup的内容:
public List<T> getMappingsByUrl(String urlPath) { return this.urlLookup.get(urlPath); }
然后是用directPathMatches去与request匹配,如果配置就添加到matches中,如果不能获取就用当前的所有mappingLookup去匹配。
if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // No choice but to go through all mappings... addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); } public Map<T, HandlerMethod> getMappings() { return this.mappingLookup; }
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) { for (T mapping : mappings) { T match = getMatchingMapping(mapping, request); if (match != null) { matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping))); } } }
这个getMatchingMapping是抽象方法,到子类我们看其是怎样匹配的。
private class Match { private final T mapping; private final HandlerMethod handlerMethod; public Match(T mapping, HandlerMethod handlerMethod) { this.mapping = mapping; this.handlerMethod = handlerMethod; } @Override public String toString() { return this.mapping.toString(); } }
可以看到这个Match就是放mapping与其对应的HandlerMethod的,前面梳理的时候也一种看到这个mapping,其为泛型,同样到子类再看其是什么。
再回到前面的lookupHandlerMethod方法,将这些Match添加到matches后。到后面会对其进行排序,如果有多个,当第一与第二排序的结果是相等的就报不是唯一匹配的:
if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.handlerMethod.getMethod(); Method m2 = secondBestMatch.handlerMethod.getMethod(); throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" + m1 + ", " + m2 + "}"); }
获取到对应的Match后,调用handleMatch方法:
protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) { request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath); } String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
再从Match中获取对应的HandlerMethod返回。
RequestMappingInfoHandlerMapping、RequestMappingHandlerMapping
现在我们来梳理AbstractHandlerMethodMapping的子类RequestMappingInfoHandlerMapping,以及AbstractHandlerMethodMapping的子类RequestMappingHandlerMapping
RequestMappingInfoHandlerMapping
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> { ............... protected RequestMappingInfoHandlerMapping() { setHandlerMethodMappingNamingStrategy(new RequestMappingInfoHandlerMethodMappingNamingStrategy()); } @Override protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) { return info.getMatchingCondition(request); } @Override protected Comparator<RequestMappingInfo> getMappingComparator(final HttpServletRequest request) { return (info1, info2) -> info1.compareTo(info2, request); } @Override protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) { super.handleMatch(info, lookupPath, request); ........... } ........... private Map<String, MultiValueMap<String, String>> extractMatrixVariables( HttpServletRequest request, Map<String, String> uriVariables) { Map<String, MultiValueMap<String, String>> result = new LinkedHashMap<>(); for (Entry<String, String> uriVar : uriVariables.entrySet()) { ............ result.put(uriVar.getKey(), getUrlPathHelper().decodeMatrixVariables(request, vars)); } return result; } @Override protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request) throws ServletException { PartialMatchHelper helper = new PartialMatchHelper(infos, request); ........ if (helper.hasMethodsMismatch()) { Set<String> methods = helper.getAllowedMethods(); if (HttpMethod.OPTIONS.matches(request.getMethod())) { HttpOptionsHandler handler = new HttpOptionsHandler(methods); return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD); } throw new HttpRequestMethodNotSupportedException(request.getMethod(), methods); } ........... return null; } ........... }
首先可以看到这个RequestMappingInfoHandlerMapping也是一个抽象类,可以看到其初始化的时候是默认设置了NamingStrategy RequestMappingInfoHandlerMethodMappingNamingStrategy。
RequestMappingHandlerMapping
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping implements MatchableHandlerMapping, EmbeddedValueResolverAware { private boolean useSuffixPatternMatch = true; private boolean useRegisteredSuffixPatternMatch = false; private boolean useTrailingSlashMatch = true; ............ private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration(); ............. @Override public void afterPropertiesSet() { this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setUrlPathHelper(getUrlPathHelper()); this.config.setPathMatcher(getPathMatcher()); this.config.setSuffixPatternMatch(this.useSuffixPatternMatch); this.config.setTrailingSlashMatch(this.useTrailingSlashMatch); this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch); this.config.setContentNegotiationManager(getContentNegotiationManager()); super.afterPropertiesSet(); } ........... @Override protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); } @Override @Nullable protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { info = typeInfo.combine(info); } } return info; } @Nullable private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); } @Nullable protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) { return null; } @Nullable protected RequestCondition<?> getCustomMethodCondition(Method method) { return null; } ......... protected RequestMappingInfo createRequestMappingInfo( RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) { RequestMappingInfo.Builder builder = RequestMappingInfo .paths(resolveEmbeddedValuesInPatterns(requestMapping.path())) .methods(requestMapping.method()) .params(requestMapping.params()) .headers(requestMapping.headers()) .consumes(requestMapping.consumes()) .produces(requestMapping.produces()) .mappingName(requestMapping.name()); if (customCondition != null) { builder.customCondition(customCondition); } return builder.options(this.config).build(); } protected String[] resolveEmbeddedValuesInPatterns(String[] patterns) { .......... } @Override public RequestMatchResult match(HttpServletRequest request, String pattern) { RequestMappingInfo info = RequestMappingInfo.paths(pattern).options(this.config).build(); RequestMappingInfo matchingInfo = info.getMatchingCondition(request); ....... return new RequestMatchResult(patterns.iterator().next(), lookupPath, getPathMatcher()); } ............ }
1、首先看下前面说的抽象方法isHandler(RequestMappingHandlerMapping)
if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); }
@Override protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
可以看到判断是不是Handler是看beanType有没有@Controller、@RequestMapping (@RestController有加@Controller注解):
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Controller @ResponseBody public @interface RestController { @AliasFor(annotation = Controller.class) String value() default ""; }
2、抽象方法getMappingForMethod(RequestMappingHandlerMapping)
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); } });
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { info = typeInfo.combine(info); } } return info; }
这个handlerType按前面的梳理就是对应加了@Controller注解的bean的class描叙,method就是循环遍历的handlerType中的方法,前面我们提到过要注意这里的返回为不为
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }
为什么传入的是Method,但这里用的是AnnotatedElement,这里在前面文章介绍Class、Method、Field的结构的时候有分析过,简答说下就是Method有实现AnnotatedElement接口(用于描叙注解的内容),可以看到这里的判断是方法上面有没有注解@RequestMapping,如果没有就返回空,就不注册当前Method为一个HanlderMethod的,之后还会获取RequestCondition
@Nullable protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) { return null; } protected RequestCondition<?> getCustomMethodCondition(Method method) { return null; }
可以看到这两个方法是返回空的,应该是给开发者自定义的(Custom)
public interface RequestCondition<T> { T combine(T other); @Nullable T getMatchingCondition(HttpServletRequest request); int compareTo(T other, HttpServletRequest request); }
如果有@RequestMapping注解就createRequestMappingInfo创建RequestMappingInfo返回
下面的config是什么时候初始化的呢,是通过其实现的InitializingBean接口的的afterPropertiesSet方法:
@Override public void afterPropertiesSet() { this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setUrlPathHelper(getUrlPathHelper()); this.config.setPathMatcher(getPathMatcher()); this.config.setSuffixPatternMatch(this.useSuffixPatternMatch); this.config.setTrailingSlashMatch(this.useTrailingSlashMatch); this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch); this.config.setContentNegotiationManager(getContentNegotiationManager()); super.afterPropertiesSet(); }
protected RequestMappingInfo createRequestMappingInfo( RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) { RequestMappingInfo.Builder builder = RequestMappingInfo .paths(resolveEmbeddedValuesInPatterns(requestMapping.path())) .methods(requestMapping.method()) .params(requestMapping.params()) .headers(requestMapping.headers()) .consumes(requestMapping.consumes()) .produces(requestMapping.produces()) .mappingName(requestMapping.name()); if (customCondition != null) { builder.customCondition(customCondition); } return builder.options(this.config).build(); } private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
可以看到这里实现是构建一个RequestMappingInfo.Builder,将@RequestMapping的属性值添加到Builder,然后通过build()返回RequestMappingInfo:
public RequestMappingInfo build() { ContentNegotiationManager manager = this.options.getContentNegotiationManager(); PatternsRequestCondition patternsCondition = new PatternsRequestCondition( this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(), this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(), this.options.getFileExtensions()); return new RequestMappingInfo(this.mappingName, patternsCondition, new RequestMethodsRequestCondition(this.methods), new ParamsRequestCondition(this.params), new HeadersRequestCondition(this.headers), new ConsumesRequestCondition(this.consumes, this.headers), new ProducesRequestCondition(this.produces, this.headers, manager), this.customCondition); }
现在我们就知道了这个RequestMappingInfo有什么了(可以简单的将其看作为RequestMapping的描叙),同时@RequestMapping的属性对应了一个RequestCondition,同时还可以自定义一个RequestCondition(这个RequestCondition有什么用)。
我们再回到前面的getMappingForMethod方法,获取了Method的RequestMappingInfo后,再获取handlerType的,如果Class上面也有@RequestMapping,就合并这两个typeInfo.combine(info):
@Override public RequestMappingInfo combine(RequestMappingInfo other) { String name = combineNames(other); PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition); RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition); ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition); HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition); ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition); ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition); RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder); return new RequestMappingInfo(name, patterns, methods, params, headers, consumes, produces, custom.getCondition()); }
这里就有了RequestCondition的第一个用处,调用其的combine将两个相同类型的RequestCondition合并,例如PatternsRequestCondition的combine方法,方法测试demo
@Test public void combineMultiplePatterns() { PatternsRequestCondition c1 = new PatternsRequestCondition("/t1", "/t2"); PatternsRequestCondition c2 = new PatternsRequestCondition("/m1", "/m2"); assertEquals(new PatternsRequestCondition("/t1/m1", "/t1/m2", "/t2/m1", "/t2/m2"), c1.combine(c2)); }
这个patterns就是对应RequestMapping的path属性或者value:
paths( resolveEmbeddedValuesInPatterns(requestMapping.path() 。value的值会处理到path中(但value与path只能有一个):
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { String name() default ""; @AliasFor("path") String[] value() default {}; @AliasFor("value") String[] path() default {}; RequestMethod[] method() default {}; String[] params() default {}; String[] headers() default {}; String[] consumes() default {}; String[] produces() default {}; }
同时有两个会报错:
@RequestMapping(value = "/car",path = "/carProxy")
现在RequestMappingInfoHandlerMapping具体实现类RequestMappingHandlerMapping的主要方法就梳理的,我们再来其本身的重写或者实现的的一些主要方法:
1、getDirectUrls方法与getMappingPathPatterns方法(RequestMappingInfoHandlerMapping)
这个前面有挖这两个坑,现在来填:
public void register(T mapping, Object handler, Method method) { ............. HandlerMethod handlerMethod = createHandlerMethod(handler, method); ............... this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); } ............. }
这里就是前面在将注册HandlerMethod的时候有调用,其中mapping就是前面通过getMappingForMethod方法返回的RequestMappingInfo
private List<String> getDirectUrls(T mapping) { List<String> urls = new ArrayList<>(1); for (String path : getMappingPathPatterns(mapping)) { if (!getPathMatcher().isPattern(path)) { urls.add(path); } } return urls; }
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) { return info.getPatternsCondition().getPatterns(); }
所以这个getMappingPathPatterns方法就是获取RequestMappingInfo的patterns(Reqeuestmapping的path),同时这里可以补充说明下关于urlLookup:
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
@Override public void add(K key, @Nullable V value) { List<V> values = this.targetMap.computeIfAbsent(key, k -> new LinkedList<>()); values.add(value); }
其实一个List,就是说一个地址能对应多个RequestMappingInfo。然后联系到前面lookupHandlerMethod方法通过lookupPath
找对应的HandlerMethod
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); .......... if (!matches.isEmpty()) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator); Match bestMatch = matches.get(0); .............. Match secondBestMatch = matches.get(1); ............... }
这里有一个比比较器MatchComparator,其最终比较的是RequestMappingInfo(比较的其实就是@ReqesutMapping的属性对应的RequestCondition)
@Override public int compareTo(RequestMappingInfo other, HttpServletRequest request) { int result; // Automatic vs explicit HTTP HEAD mapping if (HttpMethod.HEAD.matches(request.getMethod())) { result = this.methodsCondition.compareTo(other.getMethodsCondition(), request); if (result != 0) { return result; } } ........... result = this.paramsCondition.compareTo(other.getParamsCondition(), request); if (result != 0) { return result; } .......... return 0; }
这里就是目前RequestCondition的第二个使用,这部分其实用的就是例如:同一个url,虽然其请求的地址一样,但地址可以表示POST,也可以表示GET,也可能有不同的参数等。
2、getMatchingMapping方法
这个是在前面讲addMatchingMappings的时候挖的:
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) { for (T mapping : mappings) { T match = getMatchingMapping(mapping, request); if (match != null) { matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping))); } } } public Map<T, HandlerMethod> getMappings() { return this.mappingLookup; }
@Override protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) { return info.getMatchingCondition(request); }
其就是通过request获取到对应的RequestMappingInfo,然后建立ReqeustMappingInfo与对应的HandlerMethod的关系,将其包装为Match。然后在联系前面的MatchComparator比较器,其就是去Match中的RequestMappingInfo
private class MatchComparator implements Comparator<Match> { .......... @Override public int compare(Match match1, Match match2) { return this.comparator.compare(match1.mapping, match2.mapping); } }
至此,整个处理器映射器(HandlerMapping)的使用及其主要流程的源码细节就梳理完毕了。