Spring MVC 源码分析
一、Spring MVC运行流程图
二、源码分析
第一步:发送请求
这里我们需要清楚的知道spring mvc的入口是org.springframework.web.servlet.DispatcherServlet#doDispatch()。
DispatcherServlet是SpringMVC中的前端控制器,负责接收request并将request转发给对应的处理组件。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {}
可以看到参数是HttpServletRequest,HttpServletResponse,专门来处理请求,和相应的。我们可以查看类图清晰的知道其实DispatcherServlet也是一个实现了HtttServlet接口的,是一个标准的Servlet容器。
第二步:获取handler
HanlerMapping是SpringMVC中完成url到Controller映射的组件,也就是每个url都有一个对应的Handler去处理,而对应的handler里有对应的Controller。
首先,需要知道的是都有哪些handler,为什么会有这么多handler
- 有哪些handler
首先有这么多的handler,这个是在spring boot下进行的调试,如果只是单单的使用spring mvc,可能会少一个。
-
为什么有这么多的handler
因为历史原因,Spring MVC 在发展的过程中有很多种写法,其中最熟悉的写法如下:
@RestController public class UserController { @GetMapping("/hello") public String sayHello() { return "hello"; } @PostMapping("/bye") public String sayBye(){ return "bye"; } }
但是在spring boot出现之前,或者在spring的早期版本中,还有如下写法也可以处理客户端请求
@Component("/demo3") public class ControllerDemo3 implements HttpRequestHandler { @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { WebApplicationContext webApplicationContext = RequestContextUtils.findWebApplicationContext(request); System.out.println("===实现HttpRequestHandler接口==="); } }
亦或是
@Component("/demo2") public class MyController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("123123"); return null; } }
亦或是
@Component("/demo4") public class TemproController { public String sayHello(){ return "demo4"; } }
每种写法的背后都需要一种对应的处理器去处理,因为写法的不通,所以早就了这么多的handler。
其次,需要看spring 是如何查找handler的
-
在讲解如何查找handler之前,需要先了解这里的设计结构
看第三部分HandlerMapping
- 具体查找如下,可以看到doDispatch方法如下
getHandler()方法如下
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
/**
* 通过for循环在所有的handlerMappings里查找对应的handler。
* 怎么查找继续看mapping.getHandler()
*
*/
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
......
}
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//这里可以看到lookupPath就是我们请求的路径 如:/demo3
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
//这里lookupHandlerMethod就是查找handler的地方
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
重点来了,这里就是最终查找handler的方法
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List matches = new ArrayList<>();
//①
List 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()) {
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Comparator comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
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);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
①处调试代码如下:
可以看到所有的请求地址都在LinkedMultivalueMap中,这里没有匹配到,如果没有匹配到最后
,这里在第二轮的查找中找到了,如下:
这里找到handler之后,在看是如何返回的,返回的过程中,有没有对此ControdderDemo3做过什么修改。我们看到最后return了一个buildPathExposingHandler。这个方法具体做了什么,我们接着往下看
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, @Nullable Map uriTemplateVariables) {
/**
* 这里使用了HandlerExecutionChain把原有的处理器给包裹了,并暴露请求的路径地址以及uri模板变量。
* 这里为什么包裹了,后面再说......
*/
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
也就是说这里返回了一个包含原有处理器的HandlerExecutionChain,并且HandlerExecutionChain内新增了了一个会拦截请求的拦截器。
如下:
也就是说
这个lookupHandler返回了一个包装了原有处理器ControllerDemo3和拦截/demo3的拦截器的一个HandlerExecutionChain对象。
整体获取Handler的流程图如下:
第三步:获取Adapter
核心代码如下:
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
//匹配适配器
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
这里this.handlerAdapters与上一步中的this.handlerMappings有一定的对应关系。如下图:
这里匹配到了一个Adapter。
此时的HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
返回的ha就是HttpRequestHandlerAdapter
继续往下执行
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
handler方法:
//这个时候的handler参数实际是ControllerDemo3
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//将ControllerDemo3强转为父类型,调用handleRequest方法,实际调用的就是ControllerDemo3的handleRequest方法
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
ControllerDemo3就是实现了HttpRequestHandler接口,代码如下:
@Component("/demo3")
public class ControllerDemo3 implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
WebApplicationContext webApplicationContext = RequestContextUtils.findWebApplicationContext(request);
System.out.println("===实现HttpRequestHandler接口===");
}
}
后续的流程这里不在分析了,至此Spring MVC的核心流程分析完毕。
三、HandlerMapping
public interface HandlerMapping {
获取HandlerExecutionChain
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
一开始我们用了4中方式去实现一个自己的Controller。这4中Controller在spring启动的时候,会被分门别类的去处理。写法不同,处理方式肯定不同。虽然处理凡是不同,但是结果又都是相同的。所以这里抽象出一个接口,多个实现去在不通情况下获取HandlerExecutionChain的。
这里以RequestMappinghandlerMapping为例进行分析
这里通过查找了解到,AbstractHandlerMethodMapping
实现了HandlerMapping
接口
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
return executionChain;
}
getHandlerInternal的实现如下,中间省略了部分代码:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List matches = new ArrayList<>();
// ①当前这种handler的核心获取方式是在
List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// ② 这里会在初始化后的mappingRegistry.getMappings()方法里找到匹配的自定义controller
addMatchingMappings(directPathMatches, matches, request);
}
if (!matches.isEmpty()) {
// ③获取match
Match bestMatch = matches.get(0);
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
// ④这里bestMatch.handlerMethod方法返回的就是UserController对象
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
返回的UserController对象又回被封装成HandlerExecutionChain对象。执行和前面分析的逻辑一样。
四、这里在分析HandlerAdapter接口
public interface HandlerAdapter {
boolean supports(Object handler);
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
handlerAdapter的实现:
这里针对不通写法的controller,对应不通的xxxAdapter适配器。这里我们以RequestMappingHanderAdapter为例分析一下,这个也是适配如下的controller
@RestController
public class UserController {
@GetMapping("/hello")
public String sayHello() {
return "hello";
}
}
至于为什么处理的是这种controller,我们可以看下HandlerAdapter接口的supports实现,也就是RequestMappingHanderAdapter实现类中的supports方法即可知道原因。
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
这里我们只需关注handler instanceof HandlerMethod
这里返回的true,所以也就是RequestMappingHanderAdapter来处理UserContoller了。这里我们也看到了UserContoller类也没有实现任何抽象,那么UserContoller又如何是HandlerMethod类型呢,后面接着说。这里RequestMappingHanderAdapter最后也是通过反射调到了实际的controller#sayHello()方法,反射在这里就不过多赘述了。其他的HandlerAdapter实现通过同样的方式去分析也就清楚了这里为什么要有适配器。
五、Controller和HandlerMethod
RequestMappingHandlerMapping实现了InitializingBean接口,在afterPropertiesSet()方法了,完成UserController到HandlerMethod类型的转变。
public void afterPropertiesSet() {
......
super.afterPropertiesSet();
}
父类方法:
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//重点在这里
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
protected void processCandidateBean(String beanName) {
......
if (beanType != null && isHandler(beanType)) {
//进入这里
detectHandlerMethods(beanName);
}
}
protected void detectHandlerMethods(Object handler) {
Class> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class> userType = ClassUtils.getUserClass(handlerType);
// 看图3就很清楚当前代码的作用了。不过我们的关注点在第四步
Map methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//看这里 第四步
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
图3:
接着看registerHandlerMethod(handler, invocableMethod, mapping);
跟下去,会跟到如下代码
AbstractHandlerMethodMapping.java
public void register(T mapping, Object handler, Method method) {
// Assert that the handler method is not a suspending one.
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);
......
}
......
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 重点 //
// 重点 //
// 重点 //
// 重点 //
// 重点 //
//这个方法就是把handler封装一下,最后返回HandlerMethod,这里最后也就说明了为什么我们自定义的Controller的类型是HandlerMethod了//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
if (handler instanceof String) {
//看这里,明白了一切
return new HandlerMethod((String) handler,
obtainApplicationContext().getAutowireCapableBeanFactory(), method);
}
return new HandlerMethod(handler, method);
}
这里在提一个问题,为什么要把Controller封装成HandlerMethod?这里是因为controller的方法有很多,如果不用HandlerMethod这个通用的类去封装的话,一些其他功能实现起来可能就比较麻烦了。比如HandlerInterceptor接口,比如我们实现了这么一个接口
@Component
public class MyInterceptor extends HandlerInterceptorAdapter {
//如果这里不使用handler,传入的事controller,那么到了调用父类的super.preHandle方法就没法适配其他controller的方法了
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("handler is executed!!!!!!!");
return super.preHandle(request, response, handler);
}
......
}
看下HandlerMethod的注释
/**
* Encapsulates information about a handler method consisting of a
* {@linkplain #getMethod() method} and a {@linkplain #getBean() bean}.
* Provides convenient access to method parameters, the method return value,
* method annotations, etc.
*
* The class may be created with a bean instance or with a bean name
* (e.g. lazy-init bean, prototype bean). Use {@link #createWithResolvedBean()}
* to obtain a {@code HandlerMethod} instance with a bean instance resolved
* through the associated {@link BeanFactory}.
*/
public class HandlerMethod {}
大意就是:
HandlerMethod
封装了很多属性,在访问请求方法的时候可以方便的访问到方法、方法参数、方法上的注解、所属类等并且对方法参数封装处理,也可以方便的访问到方法参数的注解等信息