携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// Ensure presence of cached lookupPath for interceptors and others
if (!ServletRequestPathUtils.hasCachedPath(request)) {
initLookupPath(request);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = getCorsConfiguration(handler, request);
if (getCorsConfigurationSource() != null) {
CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
config = (globalConfig != null ? globalConfig.combine(config) : config);
}
if (config != null) {
config.validateAllowCredentials();
}
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
首先,就去调用getHandlerInternal方法去获得处理器,其实就是对应的处理方法。
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// 根据请求得到对应的映射路径,比如:"/hello"
String lookupPath = initLookupPath(request);
Object handler;
if (usesPathPatterns()) { // 如果此 HandlerMapping 已启用以使用已解析的 PathPatterns,则返回“true”。
RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
handler = lookupHandler(path, lookupPath, request);
}
else {
// 通过请求的映射路径找到对应的处理方法及方法上面@RequestMapping注解对应的RequestMappingInfo对象
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;
}
首先就根据request拿到对应的请求路径,然后把他设置到request中。然后根据这个映射路径去找对应的处理方法,我们前面应该也提到,有两个list集合存储了url,method,和RequestMappingInfo的对应关系。那么我们看看到底是不是这样找到的。进入到lookupHandler方法。
// 查找给定 URL 路径的处理程序实例。当使用 PathMatcher 进行字符串模式匹配时使用此方法。
protected Object lookupHandler(String lookupPath, HttpServletRequest request) throws Exception {
// 这里直接能拿到 RequestMappingInfo 的对应关系
Object handler = getDirectMatch(lookupPath, request);
if (handler != null) {
return handler;
}
...
}
由于我们handler不为空,我们返回到getHandler方法下,直接来到getHandlerExecutionChain(handler, request)方法下,去拿到HandlerExecutionChain,也就是处理器执行链了。
上面我是通过实现Controller接口来到这个方法里面,但是如果通过注解
@RequestMapping则getHandlerInternal是进入下面的方法
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 根据请求得到对应的映射路径
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
//通过请求的映射路径找到对应的处理方法及方法上面@RequestMapping注解对应的RequestMappingInfo对象
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
而这里则是通过lookupHandlerMethod拿到RequestMappingInfo的对应关系,有两个map集合存储,不是上面的list了,
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
for (Match match : matches) {
if (match.hasCorsConfig()) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
}
}
else {
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.getHandlerMethod().getMethod();
Method m2 = secondBestMatch.getHandlerMethod().getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.getHandlerMethod();
}
else {
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}
}
刚进入这个方法,我们就看到一个Match对象,这个对象是什么呢。
其实这个对象就是将方法和方法上的注解对象进行了一个包装而已。了解了这个之后,我们继续,它会创建一个list集合,用来存储Match对象,接着去url去拿到对应的RequestMappingInfo对象集合。
public List<T> getMappingsByDirectPath(String urlPath) {
return this.pathLookup.get(urlPath);
}
这里我们就很清楚了,就是从我们以前的urlLookup中去找,找到对应的RequestMappingInfo。然后返回。找到之后,会把他放到一个个的match中,然后再由matches存储起来。我们来看看是怎么存储的。
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.getRegistrations().get(mapping)));
}
}
}
依然是去遍历mappings,那么getMatchingMapping这个方法是干什么呢,其实这个方法就是去重新包装RequestMappingInfo,把request中的参数都放到RequestMappingInfo中,有兴趣的可以去看看。包装完成后,就会把这个RequestMappingInfo和他对应的方法包装为一个个match被matches所存起来。那么它对应的方法是怎么来的呢。getMappings这个方法,返回了一个map,可以看到这个map就是当初我们存储RequestMappingInfo和method的map,通过它就可以找到对应的method了。存储好之后,经过一些算法找到比较好的那个处理方法,然后返回。返回到getHandlerInternal当中,拿到对应的HandlerMethod对应的处理方法之后,然后再通过createWithResolvedBean方法给HandlerMethod设置一下他所对应的类之后,将HandlerMethod返回。
上面通过注解实现结束
然后一直返回到getHandler当中。此时我们已经拿到了request对应的处理方法,然后就要去拿到HandlerExecutionChain,也就是处理器链了。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(request)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
其实就是把定义的拦截器放到我们执行链中,我们把我们的拦截器存放在adaptedInterceptors的List下,此时我们没有配置,所以adaptedInterceptors的·长度为0,直接跳过增强for循环,然后返回,返回到getHandler当中,拿到处理器执行链,然后进行一些跨域的处理之后,返回执行链。返回到doDispatch当中。然后再去拿到处理器设配器。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
依然是遍历查找,选择支持当前类新的Adapter,然后返回到doDispatch当中,我这里debug里看到的是4个类,正是我们从DispatcherServlet.properties实例化HandlerAdapters的类,我拿到的是SimpleControllerHandlerAdapter。拿到之后,请求的处理类型,以get为例,开始判断是不是get方法 并且是否被修改(修改说明不安全了,直接返回),然后进入下面的判断,是拦截器的的流程
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 实际上调用处理程序
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
拦截器的实现方法applyPreHandle返回false的时候,那么就不会继续执行,而是返回,只有他返回true的时候才会继续执行处理方法等等。我们主要来看一看他是怎么执行处理方法的。
// 应用注册拦截器的 preHandle 方法。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
执行之后handle方法处理
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
->
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg","abc");
mv.setViewName("hello");
return mv;
}
}
一下子来到了我自己实现的Controller类进行处理,这个是不通过注解形式的,我们再看看我们的注解
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
->
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod); // <-- 这里
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
invokeHandlerMethod方法中会去调用invokeAndHandle方法去反射调用处理方法。我们直接看这个方法。
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
...
invocableMethod.invokeAndHandle(webRequest, mavContainer); // <- 这里
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
进入invokeAndHandle里面,主要就是invokeForRequest方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // <-- 这里
setResponseStatus(webRequest);
...
}
进来看invokeForRequest方法
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
拿到参数,doInvoke反射调用处理方法。但是getMethodArgumentValues是怎么匹配这些参数的呢。
// 参数绑定
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 得到参数数组
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
// 得到参数具体的名字
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
// 是否提供相应参数的注解,一般是不提供的 为null
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 拿到对应的参数解析器
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
supportsParameter这个方法我们要注意一下,用来拿到对应的参数解析器。
进入到getArgumentResolver方法,遍历argumentResolvers,因为参数解析器有很多,这里使用的的是RequestParamMethodArgumentResolver类。
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null && StringUtils.hasText(requestParam.name()));
}
else {
return true;
}
}
else {
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
parameter = parameter.nestedIfOptional();
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
}
else if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}
else {
return false;
}
}
}
再到这个方法,看看参数是否有RequestParam注解,如果有,在看看RequestParam是不是有参数,有的话返回到getArgumentResolver中,将参数和解析器放到缓存中,便于我们下次使用。最后返回到getMethodArgumentValues方法中。下一步就应该resolveArgument方法里去解析参数了。
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
\