Spring是如何找到URL请求对应的Controller的。
重要概念:
1.RequestMappingInfo 这个类是对请求映射的一个抽象,它包含了请求路径,请求方法,请求头等信息。其实可以看做是@RequestMapping的一个对应类。
2. HandlerMethod这个类封装了处理器实例(Controller Bean)和 处理方法实例(Method)以及方法参数数组(MethodParameter[])
3. MethodParameter 这个类从2.0就有了,它封装了方法某个参数的相关信息及行为,如该参数的索引,该参数所属方法实例或构造器实例,该参数的类型等。
4. HandlerMapping 该接口的实现类用来定义请求和处理器之前的映射关系,其中只定义了一个方法getHandler。
5. AbstractHandlerMethodMapping 这是HandlerMapping的一个基本实现类,该类定义了请求与HandlerMethod实例的映射关系。
6. RequestMappingInfoHandlerMapping这个是AbstractHandlerMethodMapping的实现类,他维护了一个RequestMappingInfo和HandlerMethod的Map属性。
7. RequestMappingHandlerMapping这个是RequestMappingInfoHandlerMapping的子类,它将@RequestMapping注解转化为RequestMappingInfo实例,并为父类使用。也就是我们处理@RequestMapping的终点。
8. InitializingBean这个接口定义了其实现Bean在容器完成属性设置后可以执行自定义初始化操作,我们的AbstractHandlerMethodMapping便实现了这个接口,并且定义了一组自定义操作,就是用来检测处理我们的@RequestMapping注解。
9. HandlerExecutionChain处理器执行链,由处理器对象和拦截器组成
入口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 = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
我们重点看一下getHandler(processedRequest);
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
这个方法的主要所用是循环遍历内存中的所有HandlerMapping返回一个HandlerExecutionChain
hm.getHandler(request)在HandlerMapping中,具体实现在AbstractHandlerMaping
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//获取handler的执行链,主要是把拦截器的执行也构建在了这个方法里面
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;
}
getHandlerInternal(request) 具体实现在AbstractHandlerMethodMapping
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//获取请求url
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
//上锁(此类中的map线程不安全)
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
此方法根据request获取lookupPath在去调用lookupHandlerMethod(lookupPath, request)获取具体的HandlerMethod
我们再看看lookupHandlerMethod(lookupPath, request)
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 通过URL,从对应的映射map里面获取requestMappingInfo
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// 对requestMappingInfo中的各种条件做匹配,如果匹配对了,则放入matches
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
//没有匹配到,则遍历所有的requestMappingInfo
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
// 比较找到的matches , 通过requestMappingInfo中的compareTo进行排序
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
}
// 获取匹配度最高的
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
它定义了一个matchs的列表,然后内存变量mappingRegistry根据lookupPath获取所有的匹配对象。如果存在这个匹配对象,那么回调用addMatchingMappings方法将匹配的对象映射到matches上。如果未匹配到这个lookupPath,那么则会循环mappingRegistry中每个Mapping来匹配该路径(因为RequestMapping中定义的路径可以有通配符)。
getMappingsByUrl(String urlPath)
public List<T> getMappingsByUrl(String urlPath) {
return this.urlLookup.get(urlPath);
}
我们看一下 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)));
}
}
}
RequestMappingInfoHandlerMapping这个class继承了这个抽象类,泛型类型为:RequestMappingInfo。
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
return info.getMatchingCondition(request);
}
判断method params header consumes等是否相同
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
if (methods == null || params == null || headers == null || consumes == null || produces == null) {
return null;
}
PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
if (patterns == null) {
return null;
}
RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
if (custom == null) {
return null;
}
return new RequestMappingInfo(this.name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
Q1:这个mappingRegistry变量是何物,什么时候将Mapping结果保存的?
class MappingRegistry {
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
// requestMappingInfo 和HandlerMethod的对应关系
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
// url 和requestMappingInfo的对应关系
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
// name和HandlerMethod的关系
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
// 方法和跨域信息的映射
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
MappingRegistry是AbstractHandlerMethodMapping的一个内部类
当ioc容器初始化的时候会调用initHandlerMethods
public void afterPropertiesSet() {
initHandlerMethods();
}
我们再看看initHandlerMethods();
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
//获取spring容器里的所有beanName
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
//判断是否以约定头开始; 如果是scopedTarget.这个开头的bean, 则不做处理。
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);//获取bean
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
//判断这些bean是否需要处理
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
这个方法的作用就是得到IoC容器里面所有的BeanName然后去判断这个bean是否需要处理判断的标准是isHandler(),点进去可以看到判断的标准是(是否有@RequestMapping注解或者有@Controller注解)
detectHandlerMethods(beanName);处理bean
protected void detectHandlerMethods(Object handler) {
//获得类 (判断handler是字符串还是对象,如果是字符串则从容器拿)
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
//返回给定类的用户定义类:通常返回给定类,但如果是动态代理机制的情况下返回原始类(父类)。
Class<?> userType = ClassUtils.getUserClass(handlerType);
//进行方法筛选
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
//对检测到的Method进行缓存注册
//mapping是指RequestMappingInfo
//method是url对应的方法包括入参返回值等
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
返回给用户类时有两种方法一种是jvm动态
- 如果目标对象实现了接口,默认采用JDK的动态代理实现AOP,当然也可以强制使用CGLIB实现
- 如果对象没有实现接口,必须采用CGLIB实现
在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
- 使用JDK动态代理,因为有接口,所以是系统更加松耦合,缺点为每一个目标类创建接口
使用CGLIB进行动态代理,因为代理类与目标类是继承关系,所以不需要接口的存在,但因为没有使用接口,所以系统的耦合性较差。
进行方法筛选getMappingForMethod(method, userType);在RequestMappingHandlerMapping中
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//创建RequestMappingInfo(方法)
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
//创建RequestMappingInfo(类)
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
//将类级请求映射和方法级请求映射进行组合
info = typeInfo.combine(info);
}
}
return info;
}
继续看将信息注册达到缓存中
registerHandlerMethod(handler, invocableMethod, mapping);
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);//构建HandlerMethod
assertUniqueMethodMapping(handlerMethod, mapping);
// 将requestMappingInfo为键,handlerMethod为value,放入linkedHashMap
this.mappingLookup.put(mapping, handlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
}
List<String> directUrls = getDirectUrls(mapping);// 获取URL地址
// 获得url映射路径,将映射路径和匹配条件对象RequestMappingInfo存起来
for (String url : directUrls) {
//urlLookup是Map<K, List<V>> 一个url会对应多个RequestMappingInfo
this.urlLookup.add(url, mapping);
//为什么说一个url会对应多个requestMappingInfo
//同一个方法可能是url可能是post请求 也可能是别的请求(例如GET,DELETE)
//一个参数的改变就是导致RequestMappingInfo的不同所以要存储成一对多的形式
/**
* add()将给定的单个值添加到给定键的当前值列表中
* urlLookup是一个LinkedMultiValueMap private final Map<K, List<V>> targetMap;
* add()方法采用的是jdk1.8 以后map新增方法computeIfAbsent()
* 如果当前map中可以找到这个key对应的List则将这个值添加到已有的List中
* 如果当前map中找不到这个key对应的List则新new一个List并将这个值添加到List中
* /add/user/ requestMappingInfo
*/
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
注意:当我们在Controller里面两个方法的RequestMapping里面设置的属性完全一样时,当项目启动时会报
Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'employeeController' method这个错误
源码是怎么处理的?
在注册缓存时会将requestMappingInfo为键,handlerMethod为value,放入mappingLookup(linkedHashMap)在存放的时候会通过assertUniqueMethodMapping()方法进行判断
private void assertUniqueMethodMapping(HandlerMethod newHandlerMethod, T mapping) {
HandlerMethod handlerMethod = this.mappingLookup.get(mapping);
if (handlerMethod != null && !handlerMethod.equals(newHandlerMethod)) {
throw new IllegalStateException(
"Ambiguous mapping. Cannot map '" + newHandlerMethod.getBean() + "' method \n" +
newHandlerMethod + "\nto " + mapping + ": There is already '" +
handlerMethod.getBean() + "' bean method\n" + handlerMethod + " mapped.");
}
}
会先通过RequestMapping去查询是否存在对应的HandlerMethod,如果存在会与要存的HandlerMethod进行比较,如果相同会报错