今天的天气有点阴,但是丝毫不影响帅气的我,闪亮登场,哈哈…
这回简单看看Spring MVC请求流程及源码分析。
一、简述Spring MVC启动流程
1、请求发送到DispatcherServlet
2、 DispatcherServlet收到请求,调用处理器映射器HandlerMapping。处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet。
3、DispatcherServlet根据处理器Handler获取处理器适配器 HandlerAdapter,执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作
4、执行拦截器前置方法
5、执行处理器Handler(Controller)的业务方法,Handler执行完成返回ModelAndView到DispatcherServlet。
6、执行拦截器后置方法
7、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器,ViewReslover解析后返回具体View
7、 DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)。
8、DispatcherServlet响应用户。
画了一个简易流程图:
二、源码分析
前端发出请求,会调用Servlet的service方法。我们注册了DispatcherServlet拦截所有请求。
看下DispatcherServlet继承图:
debug调试下代码:
核心方法就是doDispatch(request, response)方法。去掉非核心代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
HandlerExecutionChain mappedHandler = null;
//1、通过HandlerMapping获得HandlerExecutionChain处理器执行链(handler和拦截器)
mappedHandler = getHandler(processedRequest);
//2、根据handler处理获得对应的HandlerAdapter处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//3、执行拦截器的applyPreHandle前置方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//4、执行handler处理器方法,也就是执行我们后端业务方法,返回ModelAndView
mappedHandler.getHandler());
//5、执行拦截器的applyPostHandle后置方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
//6、处理结果,ViewReslover视图解析器解析,View渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
...
}
请求流程的核心代码就这么多,哈哈…是不是很easy~
下面我们看下getHandler方法,通过HandlerMapping获得HandlerExecutionChain处理器执行链(handler和拦截器)
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
//调用HandlerMapping的getHandler方法,获得HandlerExecutionChain,不为空,则返回
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
获得多个HandlerMapping,按照order排好序的,按序调用,返回的HandlerExecutionChain不为空,就返回。
下面看看第一个RequestMappingHandlerMapping,代码调试下,先看
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//获取handler
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//获得HandlerExecutionChain
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
getHandlerInternal方法,先获得请求路径,然后寻找HandlerMethod。
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//request中获得请求路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
//加读锁
this.mappingRegistry.acquireReadLock();
try {
//寻找HandlerMethod,即找到对应的controller和方法
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
//释放锁
this.mappingRegistry.releaseReadLock();
}
}
lookupHandlerMethod方法,找到HandlerMethod,匹配到要controller和方法。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
//mappingRegistry的urlLookup的中找,返回RequestMappingInfo信息,直接匹配路径,比如请求:“/hello”
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
//匹配请求方式,参数,headers等,就是各种condition的getMatchingCondition
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
//路径直接匹配不到的候,拿所有的RequestMappingInfo信息去匹配。如"/hello/{xxx}"
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
//多个匹配排序,选择最优的RequestMappingInfo
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)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
//处理匹配的uri模板变量等,比如 后端:/hello/{xxx} 请求是:"/hello/111",得到xxx = 111
handleMatch(bestMatch.mapping, lookupPath, request);
//放回handlerMethod,包含contoller和方法等信息
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
再看下 getHandlerExecutionChain(handler, request)创建HandlerExecutionChain ,添加拦截器
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
//创建HandlerExecutionChain ,设置拦截器
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
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;
}
到此我们大致看了下这个根据请求寻找后端方法的代码流程。匹配细节就不展开啦。
下面我们看下Spring MVC是如何注册组件的?
@EnableWebMvc注解import了DelegatingWebMvcConfiguration
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
DelegatingWebMvcConfiguration继承WebMvcConfigurationSupport,在父类中以@Bean的形式注入了相关组件
那么我们还有一个疑惑,MappingRegistry是何时初始化的?
看下这个继承图:
这样上步注入RequestMappingHandlerMapping时,会调用afterPropertiesSet方法。
public void afterPropertiesSet() {
initHandlerMethods();
}
initHandlerMethods初始化HandlerMethod
protected void initHandlerMethods() {
//获取所有Object
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//处理候选类
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
processCandidateBean方法
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// 看看是不是handler
if (beanType != null && isHandler(beanType)) {
//获取HandlerMethods,注册
detectHandlerMethods(beanName);
}
}
isHandler方法,判断是不是handler,包含Controller和RequestMapping注解的bean
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
detectHandlerMethods方法:
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
//创建RequestMappingInfo
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//遍历,注册
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
registerHandlerMethod方法注册HandlerMethod
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
public void register(T mapping, Object handler, Method method) {
if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
Class<?>[] parameterTypes = method.getParameterTypes();
if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
}
}
//加锁
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
//所有uri
this.mappingLookup.put(mapping, handlerMethod);
//直接的uri,不包含 "/hello/{xxx}" 这种
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);
}
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();
}
}
到此,我们看了主要逻辑,工程启动时注入RequestMappingHandlerMapping,初始化后,生成MappingRegistry,管理所有的url和handlerMethod的关系。