1. 程式人生 > 程式設計 >【原創】005 | 搭上SpringBoot請求處理原始碼分析專車

【原創】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);
    }
}複製程式碼