【原創】005 | 搭上SpringBoot請求處理原始碼分析專車
專車介紹
該趟專車是開往Spring Boot請求處理原始碼分析專車,主要用來分析Spring Boot是如何將我們的請求路由到指定的控制器方法以及呼叫執行。
專車問題
為什麼我們在控制器中新增一個方法,使用@RequestMapping註解標註,指定一個路徑,就可以用來處理一個web請求?
如果多個方法的請求路徑一致,Spring Boot是如何處理的?
專車示例
@RestController
@RequestMapping("/persons")
public class PersonController {
private static List<Person> personList = new ArrayList<>();
static {
personList.add(new Person(10001,"test1" ));
personList.add(new Person(10002,"test2"));
personList.add(new Person(10003,"test3"));
personList.add(new Person(10004,"test4"));
personList.add(new Person(10005,"test5"));
}
@GetMapping("/")
public List<Person> list() {
return personList;
}
@GetMapping("/{id}" )
public Person get(@PathVariable("id") Integer id) {
Person defaultPerson = new Person(88888,"default");
return personList.stream().filter(person -> Objects.equals(person.getId(),id)).findFirst().orElse(defaultPerson);
}
@PostMapping("/")
public void add(@RequestBody Person person) {
personList.add(person);
}
@PutMapping("/" )
public void update(@RequestBody Person person) {
personList.removeIf(p -> Objects.equals(p.getId(),person.getId()));
personList.add(person);
}
}
複製程式碼
示例程式碼提供了GET、POST、PUT請求,接下里我們會結合示例進行原始碼分析
專車分析
此次分析主要從2個大的方面進行分析:請求初始化、請求處理
請求初始化
請求流程
一次完成的請求流程就是請求--->處理--->響應,業務邏輯處理最終交由我們建立的Servlet來進行處理。以前在使用Spring MVC框架的時候,我們都會在web.xml中配置一個DispathcherServlet。接下來就讓我們來看看DispathcherServlet的類圖
從如上圖可以清晰的看到DispatcherServlet的繼承關係。其中一個名為HttpServlet的類,如果寫過Servlet的應該都比較的熟悉,以往基於Servlet開發,都會建立一個Servlet實現類,繼承HttpServlet並重寫service方法,最後在web.xml中配置我們我們建立的Servlet實現類,這樣我們就可以使用建立的Servlet實現類來處理我們的web請求了。
HttpServlet初始化
在我們第一次請求的時候會進行Servlet的初始化,主要用來初始化資源。HttpServlet的init方法由父類GenericServlet宣告,由子類HttpServletBean實現。
初始化方法:HttpServletBean#init
@Override
public final void init() throws ServletException {
// ...省略部分程式碼
// Let subclasses do whatever initialization they like.
// 暴露出去一個方法,可以讓子類初始化一些自己想要初始化的內容
initServletBean();
}
複製程式碼
建立WebApplicationContext:FrameworkServlet#initServletBean
@Override
protected final void initServletBean() throws ServletException {
// ...省略部分程式碼
try {
// 初始化WebApplicationContext物件
this.webApplicationContext = initWebApplicationContext();
// 空實現
initFrameworkServlet();
}
// ...省略部分程式碼
}
複製程式碼
初始化WebApplicationContext物件:FrameworkServlet#initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() {
// 獲取WebApplicationContext物件
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
// ... 省略部分程式碼
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
// 重新整理資源
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName,wac);
}
return wac;
}
複製程式碼
重新整理資源:DispatcherServlet#onRefresh
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
複製程式碼
初始化策略:DispatcherServlet#initStrategies
protected void initStrategies(ApplicationContext context) {
// 初始化多檔案解析器
initMultipartResolver(context);
// 初始化本地化解析器
initLocaleResolver(context);
// 初始化主題解析器
initThemeResolver(context);
// 初始化HandlerMapping
initHandlerMappings(context);
// 初始化HandlerAdapter
initHandlerAdapters(context);
// 初始化異常解析器
initHandlerExceptionResolvers(context);
// 初始化請求到檢視名稱翻譯器
initRequestToViewNameTranslator(context);
// 初始化檢視解析器
initViewResolvers(context);
initFlashMapManager(context);
}
複製程式碼
來看一下初始化HandlerMapping實現:DispatcherServlet#initHandlerMappings
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext,including ancestor contexts.
// 從IOC容器中獲取型別為HandlerMapping的bean
// 對應的bean有RequestMappingHandlerMapping、SimpleUrlHandlerMapping、WelcomePageHandlerMapping
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.
// 對HandlerMapping進行排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
}
複製程式碼
通過對初始化HandlerMapping實現的分析,我們可以得出,所有的初始化操作就是從IOC容器中獲取相應型別的Bean,然後進行屬性賦值。
既然能從IOC容器中獲取到HandlerMapping bean,那麼一定存在定義bean 的地方。開啟WebMvcAutoConfiguration類,可以看到如下程式碼
/**
* Configuration equivalent to {@code @EnableWebMvc}.
* 此配置等同於使用@EnableWebMvc註解
*/
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
private final WebMvcProperties mvcProperties;
private final ListableBeanFactory beanFactory;
private final WebMvcRegistrations mvcRegistrations;
public EnableWebMvcConfiguration(
ObjectProvider<WebMvcProperties> mvcPropertiesProvider,ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,ListableBeanFactory beanFactory) {
this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
this.beanFactory = beanFactory;
}
// 宣告RequestMappingHandlerAdapter bean
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null
|| this.mvcProperties.isIgnoreDefaultModelOnRedirect());
return adapter;
}
// 宣告RequestMappingHandlerMapping bean
@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
// Must be @Primary for MvcUriComponentsBuilder to work
return super.requestMappingHandlerMapping();
}
}
複製程式碼
在如上程式碼中可以看到HandlerAdapter和HandlerMapping bean的宣告
建立RequestMappingHandlerMapping
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
// 建立RequestMappingHandlerMapping物件
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
// 設定屬性
mapping.setOrder(0);
// 設定攔截器
mapping.setInterceptors(getInterceptors());
mapping.setContentNegotiationManager(mvcContentNegotiationManager());
mapping.setCorsConfigurations(getCorsConfigurations());
// ...省略部分程式碼
return mapping;
}
複製程式碼
可以看到除了建立RequestMappingHandlerMapping物件,其它的都是設定屬性資訊,接下來重點分析建立物件部分的程式碼
WebMvcConfigurationSupport#createRequestMappingHandlerMapping
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
複製程式碼
可以建立RequestMappingHandlerMapping物件的程式碼很簡單,就是呼叫了無引數構造進行初始化。但是通過檢視RequestMappingHandlerMapping的繼承關係,我們可以看到該類實現了InitializingBean介面,這也就告訴我們當看到很簡單的程式碼的時候,我們就要看看類的繼承關係,來看看是否使用其他形式進行邏輯實現。
既然實現了InitializingBean介面,那就看看建立bean後的初始化方法afterPropertiesSet
@Override
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();
}
複製程式碼
@Override
public void afterPropertiesSet() {
// 初始化處理方法
initHandlerMethods();
}
複製程式碼
初始化處理方法:AbstractHandlerMethodMapping#initHandlerMethods
protected void initHandlerMethods() {
// 獲取並遍歷候選bean名稱,候選bean就是從IOC容器中獲取型別為Object的bean名稱,也就是所有的Bean名稱
for (String beanName : getCandidateBeanNames()) {
// 如果bean的名稱不以“scopedTarget.”開頭,才進行處理
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// 處理候選bean名稱
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
複製程式碼
處理候選bean名稱:AbstractHandlerMethodMapping#processCandidateBean
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
// 根據bean的名稱獲取對應bean的型別
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);
}
}
// 如果bean的型別不為空並且對應類上含有@Controller註解或者@RequestMapping註解
if (beanType != null && isHandler(beanType)) {
// 推斷處理方法
detectHandlerMethods(beanName);
}
}
複製程式碼
推斷處理方法:AbstractHandlerMethodMapping#detectHandlerMethods
protected void detectHandlerMethods(Object handler) {
// 根據bean名稱獲取型別
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 {
// selectMethods方法獲取當前類中所有的方法,針對PersonController類就有list、get、add、update四個方法,遍歷這四個方法,分別建立對應的RequestMappingInfo物件
// 根據method獲取RequestMappingInfo物件
return getMappingForMethod(method,userType);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType,methods));
}
methods.forEach((method,mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method,userType);
registerHandlerMethod(handler,invocableMethod,mapping);
});
}
}
複製程式碼
根據method獲取RequestMappingInfo物件:RequestMappingHandlerMapping#getMappingForMethod
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method,Class<?> handlerType) {
// 根據method物件建立RequestMappingInfo物件
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// 如果當前方法所在的類也含有@RequestMapping物件,那麼也建立一個RequestMappingInfo物件
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
// 將兩個RequestMappingInfo物件進行合併,比如我們PersonController上指定@RequestMapping("/persons"),針對list方法,list方法上指定@RequestMapping("/"),那麼合併後的對映路徑就是/persons/
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).build().combine(info);
}
}
// 返回RequestMappingInfo物件
return info;
}
複製程式碼
回到推斷處理方法中:AbstractHandlerMethodMapping#detectHandlerMethods
protected void detectHandlerMethods(Object handler) {
// 根據bean名稱獲取型別
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 獲取處理方法,每個方法都有對應的RequestMappingInfo物件
Map<Method,(MethodIntrospector.MetadataLookup<T>) method -> {
try {
// selectMethods方法中當前類中所有的方法,針對PersonController類就有list、get、add、update四個方法,遍歷這四個方法,分別建立對應的RequestMappingInfo物件
// 根據method獲取RequestMappingInfo物件
return getMappingForMethod(method,methods));
}
// 遍歷處理方法
methods.forEach((method,mapping) -> {
// 獲取可以執行的method物件
Method invocableMethod = AopUtils.selectInvocableMethod(method,userType);
// 註冊處理方法
registerHandlerMethod(handler,mapping);
});
}
}
複製程式碼
註冊處理方法:AbstractHandlerMethodMapping.MappingRegistry#register
public void register(T mapping,Object handler,Method method) {
// 加寫鎖,加鎖是因為我們可以在程式碼中手動註冊處理方法,為了防止併發問題,此處需要加鎖處理
this.readWriteLock.writeLock().lock();
try {
// 建立HandlerMethod物件
HandlerMethod handlerMethod = createHandlerMethod(handler,method);
assertUniqueMethodMapping(handlerMethod,mapping);
// 將RequestMappingInfo物件和HandlerMethod物件新增到map集合中
this.mappingLookup.put(mapping,handlerMethod);
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
// 將url和RequestMappingInfo物件新增到map集合中
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);
}
// 將RequestMappingInfo和MappingRegistration物件新增到map集合中
this.registry.put(mapping,new MappingRegistration<>(mapping,handlerMethod,directUrls,name));
}
finally {
// 釋放鎖
this.readWriteLock.writeLock().unlock();
}
}
複製程式碼
所有方法遍歷完成後的結果如下:
到此RequestMappingHandlerMapping物件建立初始化就結束了
RequestMappingHandlerMapping物件建立總結
呼叫afterPropertiesSet初始化方法
獲取所有的bean名稱
遍歷所有的bean名稱,如果bean名稱不是以”scopedTarget.“開頭就繼續處理
根據bean名稱獲取bean型別,獲取對應的型別上是否含有@Controller註解或@RequestMapping註解,如果有就繼續處理
獲取當前類中所有的方法,遍歷所有的方法
根據Method物件生成RequestMappingInfo物件,如果類上也很有@RequestMapping註解,那麼也生成RequestMappingInfo物件,將這兩個RequestMappingInfo物件進行合併
遍歷Method、RequestMappingInfo對應的map集合並註冊到對應的mappingLookup、urlLookup、registry集合中
建立RequestMappingHandlerAdapter
建立RequestMappingHandlerAdapter:WebMvcAutoConfiguration.EnableWebMvcConfiguration#requestMappingHandlerAdapter
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
// 呼叫父類建立RequestMappingHandlerAdapter物件
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
// ...省略部分程式碼
return adapter;
}
複製程式碼
呼叫父類建立RequestMappingHandlerAdapter:WebMvcConfigurationSupport#requestMappingHandlerAdapter
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
// 建立RequestMappingHandlerAdapter物件
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
// 省略部分程式碼
return adapter;
}
複製程式碼
請求處理
請求處理流程
遍歷所有的HandlerMapping物件,找到匹配當前請求對應的HandlerMethod
將HandlerMethod包裝成HandlerExecutionChain物件
根據HandlerMethod找到HandlerAdapter
HandlerAdapter執行HandlerMethod
匹配HandlerMethod幷包裝成HandlerExecutionChain物件
protected void doDispatch(HttpServletRequest request,HttpServletResponse response) throws Exception {
HandlerExecutionChain mappedHandler = null;
// 匹配HandlerMethod幷包裝成HandlerExecutionChain物件
mappedHandler = getHandler(processedRequest);
}
複製程式碼
獲取HandlerExecutionChain物件:DispatcherServlet#getHandler
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍歷所有的HandlerMapping
for (HandlerMapping mapping : this.handlerMappings) {
// 根據HandlerMapping獲取HandlerExecutionChain物件,此處的HandlerMapping就是上面分析過的RequestMappingHandlerMapping物件
HandlerExecutionChain handler = mapping.getHandler(request);
// 如果獲取到HandlerExecutionChain物件,那麼直接將HandlerExecutionChain物件返回
if (handler != null) {
return handler;
}
}
}
return null;
}
複製程式碼
根據HandlerMapping獲取HandlerExecutionChain物件:AbstractHandlerMapping#getHandler
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 獲取HandlerMethod物件
Object handler = getHandlerInternal(request);
// ...省略部分程式碼
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler,request);
// ...省略部分程式碼
return executionChain;
}
複製程式碼
獲取HandlerMethod物件
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 獲取請求的路徑,假設此處請求的路徑為/persons/
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 加鎖
this.mappingRegistry.acquireReadLock();
try {
// 尋找HandlerMethod物件
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath,request);
// 獲取HandlerMethod所在類對應的bean,然後建立HandlerMethod物件
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
// 釋放鎖
this.mappingRegistry.releaseReadLock();
}
}
複製程式碼
尋找HandlerMethod物件:AbstractHandlerMethodMapping#lookupHandlerMethod
在該方法之前再看一下上文中對RequestMappingHandlerMapping分析的結果
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath,HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 從urlLookup屬性中找到當前請求路徑對應的RequestMappingInfo資訊
// 假設請求的路徑為/persons/,那麼此處得到的結果有3個
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// 尋找最匹配的RequestMappingInfo
// 匹配的方式包括:請求方法、請求header、請求引數等
addMatchingMappings(directPathMatches,matches,request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(),request);
}
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
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);
// 返回匹配的HandlerMethod
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(),request);
}
}
複製程式碼
獲取HandlerAdapter
protected void doDispatch(HttpServletRequest request,HttpServletResponse response) throws Exception {
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
}
複製程式碼
獲取HandlerAdapter:DispatcherServlet#getHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
// 如果當前HandlerAdapter支援當前要處理的HnadlerMethod,那麼就返回此HandlerAdapter
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");
}
複製程式碼
匹配方法:此處拿RequestMappingHandlerAdapter舉例,呼叫AbstractHandlerMethodAdapter#supports
public final boolean supports(Object handler) {
// 如果當前的hander是HandlerMethod,則返回true;後一個表示式直接返回的就是true
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
複製程式碼
從如上分析可以得出的結論就是最終返回的HandlerAdapter為RequestMappingHandlerAdapter
HandlerAdapter執行HandlerMethod
protected void doDispatch(HttpServletRequest request,HttpServletResponse response) throws Exception {
mv = ha.handle(processedRequest,response,mappedHandler.getHandler());
}
複製程式碼
處理目標方法:RequestMappingHandlerAdapter#handleInternal
@Override
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) {
// ...省略部分程式碼
}
else {
// No synchronization on session demanded at all...
// 呼叫HandlerMethod方法
mav = invokeHandlerMethod(request,handlerMethod);
}
// ...省略部分程式碼
return mav;
}
複製程式碼
呼叫HandlerMethod方法:RequestMappingHandlerAdapter#invokeHandlerMethod
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request,response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod,binderFactory);
// 建立ServletInvocableHandlerMethod物件,就是把handlerMethod物件的屬性賦值給ServletInvocableHandlerMethod物件的屬性
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// ...省略部分程式碼
// 呼叫方法並處理返回值
invocableMethod.invokeAndHandle(webRequest,mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer,modelFactory,webRequest);
}
finally {
webRequest.requestCompleted();
}
}
複製程式碼
呼叫方法並處理返回值:ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest,ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
// 執行請求,獲取返回值
Object returnValue = invokeForRequest(webRequest,mavContainer,providedArgs);
setResponseStatus(webRequest);
// ...省略部分程式碼
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null,"No return value handlers");
try {
// 處理返回值
this.returnValueHandlers.handleReturnValue(
returnValue,getReturnValueType(returnValue),webRequest);
}
}
複製程式碼
執行請求:InvocableHandlerMethod#invokeForRequest
@Nullable
public Object invokeForRequest(NativeWebRequest request,@Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
// 獲取方法引數
Object[] args = getMethodArgumentValues(request,providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 目標方法呼叫
return doInvoke(args);
}
複製程式碼
目標方法呼叫:InvocableHandlerMethod#doInvoke
@Nullable
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 通過反射執行目標方法
return getBridgedMethod().invoke(getBean(),args);
}
// ...省略部分程式碼
}
複製程式碼
到此請求處理的原始碼分析就結束了,最終再來看看doDispatch完整的方法: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.
// 1、獲取handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest,response);
return;
}
// Determine handler adapter for the current request.
// 2、獲取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;
}
// Actually invoke the handler.
// 呼叫目標方法
mv = ha.handle(processedRequest,mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest,mv);
// 執行攔截器的處理方法
mappedHandler.applyPostHandle(processedRequest,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,mappedHandler,mv,dispatchException);
}
catch (Exception ex) {
// 執行攔截器的後置方法,常用語釋放資源
triggerAfterCompletion(processedRequest,ex);
}
catch (Throwable err) {
// 執行攔截器的後置方法,常用語釋放資源
triggerAfterCompletion(processedRequest,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);
}
}
}
}
複製程式碼
專車總結
一次請求原理如下:
請求初始化
請求處理
請求初始化
初始化RequestMappingHandlerMapping
初始化RequestMappingHandlerAdapter
請求處理
獲取HandlerMethod,組裝HandlerExecutionChain物件
獲取HandlerAdapter
使用HandlerAdapter執行HandlerMethod
專車回顧
為什麼我們在控制器中新增一個方法,使用@RequestMapping註解標註,指定一個路徑,就可以用來處理一個web請求?因為在初始化過程中,會將請求路徑和處理方法進行繫結,我們在請求ulr的時候,匹配到我們對應的處理方法,然後呼叫處理方法,就可以執行此次的ewb請求了。
如果多個方法的請求路徑一致,Spring Boot是如何處理的?如果多個方法的請求路徑一致,會拿請求方法、請求引數、請求header等,最終會匹配出最符合的一個處理方法,如果匹配出多個結果,就會報錯。
專車遺漏問題
SpringBoot如何處理請求引數
SpringBoot如何處理返回結果
SpringBoot攔截器如何工作
專車擴充套件
如果是基於微服務開發,那麼該如何定義我們的服務?
定義微服務介面:
@RequestMapping("/persons")
public interface PersonApi {
/**
* list
*
* @return
*/
@GetMapping("/")
List<Person> list();
/**
* get
*
* @param id
* @return
*/
@GetMapping("/{id}")
Person get(@PathVariable("id") Integer id);
/**
* add
*
* @param person
* @return
*/
@PostMapping("/")
void add(@RequestBody Person person);
/**
* update
*
* @param person
* @return
*/
@PutMapping("/")
void update(@RequestBody Person person);
}
複製程式碼
定義介面實現:
@RestController
public class PersonController implements PersonApi {
private static List<Person> personList = new ArrayList<>();
static {
personList.add(new Person(10001,"test5"));
}
@Override
public List<Person> list() {
return personList;
}
@Override
public Person get(Integer id) {
Person defaultPerson = new Person(88888,id)).findFirst().orElse(defaultPerson);
}
@Override
public void add(Person person) {
personList.add(person);
}
@Override
public void update(Person person) {
personList.removeIf(p -> Objects.equals(p.getId(),person.getId()));
personList.add(person);
}
}複製程式碼