Spring MVC原始碼解析
Spring MVC的使用原理其實是通過配置一個Servlet來接管所有的請求,所有的請求由這個Servlet來進行分發處理。
我們可以從web.xml裡面看出這一點
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
這個Servlet就是Spring MVC提供的DispatcherServlet,它的繼承圖如下:
DispatcherServlet大致接收請求的時序圖如下:
下面我們先設定幾個問題然後再去看原始碼。
- DispatcherServlet初始化的時機?
我們將斷點打在DispatcherServlet中的onRefresh方法上
,然後啟動專案。
從這個堆疊的圖可以看出,最開始是HttpServletBean的init方法執行,然後啟動Spring容器,Spring容器初始化的最後一步finishRefresh裡面進行了事件通知。
FrameworkServlet裡有個內部類ContextRefreshListener,實現了ApplicationListener介面,接收到了上面的事件通知,執行onApplicationEvent方法。
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
繼續跟蹤onApplicationEvent方法
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
synchronized (this.onRefreshMonitor) {
onRefresh(event.getApplicationContext());
}
}
發現最終是由onRefresh方法來進行具體處理的,並且這個onRefresh方法在FrameworkServlet裡面是個空方法,是由它的子類DispatcherServlet來實現的。
我們來看DispatcherServlet裡的onRefresh方法
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
這樣就把SpringMVC的九大元件給初始化了。
- 我們的@RequestMapping註解在方法上,SpringMVC是怎麼根據這個註解就能將對應的請求執行到這個方法上的?
從上面可以看到有個initHandlerMappings方法:
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// 找所有實現了HandlerMapping介面的類
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
//預設情況下,我們是使用的這個方法。
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
繼續跟蹤getDefaultStrategies方法
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
//省略部分程式碼...
}
return strategies;
}
else {
return new LinkedList<>();
}
}
可以看出對classNames進行了遍歷,這裡有兩個值,
BeanNameUrlHandlerMapping和RequestMappingHandlerMapping,我們一般用的是RequestMappingHandlerMapping。
RequestMappingHandlerMapping的父類AbstractHandlerMethodMapping實現了InitializingBean介面,我們來看看它實現的afterPropertiesSet方法。
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();
}
前面都是一些配置,後面是呼叫父類的afterPropertiesSet方法,此方法裡只有initHandlerMethods一個方法
protected void initHandlerMethods() {
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);
}
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
繼續跟蹤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
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.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
//遍歷map,處理裡面的方法
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
繼續跟蹤裡面的register方法
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);
}
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();
}
}
可以看出mappingLookup、urlLookup、registry都放入了值。這時我也不知道每個的具體作用。我把斷點打到這3個屬性上。然後前端發起一個get請求。
- 請求匹配的過程?
前端發起請求,斷點停在了AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl方法上,以前端發起的請求路徑,從urlLookup上獲取對應的值。
最後看看請求分發的主流程,也是springMVC最核心的程式碼
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 {
//multipart請求處理
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 獲取合適的HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//獲取合適的HandlerAdapter
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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// handle執行呼叫(核心)
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);
}
}
}
}
總結:
-
tomcat的Servlet調起Spring容器啟動,Spring容器啟動完,事件通知到SpringMVC的DispatcherServlet。
-
這時會掃描所有的bean,將註解了@Controller和@RequestMapping的解析出來。
-
前端請求發過來,DispatcherServlet接收到(因為它是個servlet,配置在web.xml的),根據上一步處理好的對映關係,找到對應的方法來處理。
如通過/test能找到test方法
@RequestMapping("/test")
public String test(String name, HttpServletRequest request,Model model){
System.out.println("name");
model.addAttribute("date",new Date());
return "success";
}
- 找到對應的方法後,反射呼叫
method.invoke(Object obj,Object... args)
- 組裝modelAndView渲染檢視到前端