SpringBoot是怎么處理請求的


概念

​ 一種軟件架構風格、設計風格,而不是標准,只是提供了一組設計原則和約束條件。它主要用於客戶端和服務器交互類的軟件。基於這個風格設計的軟件可以更簡潔,更有層次,更易於實現緩存等機制。

URL定義

資源:互聯網所有的事物都可以被抽象為資源
資源操作:使用POST、DELETE、PUT、GET,使用不同方法對資源進行操作。
分別對應 添加、 刪除、修改、查詢。

傳統方式操作資源
http://127.0.0.1/item/queryUser.action?id=1 查詢,GET
http://127.0.0.1/item/saveUser.action 新增,POST
http://127.0.0.1/item/updateUser.action 更新,POST
http://127.0.0.1/item/deleteUser.action?id=1 刪除,GET或POST

請求方式

可以通過 GET、 POST、 PUT、 PATCH、 DELETE 等方式對服務端的資源進行操作。其中,GET 用於查詢資源,POST 用於創建資源,PUT 用於更新服務端的資源的全部信息,PATCH 用於更新服務端的資源的部分信息,DELETE 用於刪除服務端的資源。

這里使用“用戶”的案例進行回顧通過 GET、 POST、 PUT、 PATCH、 DELETE 等方式對服務端的資源進行操作。

SpringBoot測試

測試代碼:

    @RequestMapping(value = "/user",method = RequestMethod.GET)
    public String getUser(){
        return "GET-張三";
    }

    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public String saveUser(){
        return "POST-張三";
    }


    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    public String putUser(){
        return "PUT-張三";
    }

    @RequestMapping(value = "/user",method = RequestMethod.DELETE)
    public String deleteUser(){
        return "DELETE-張三";
    }

首先html中只能發送GET后者POST,如果要實現REST風格的話,需要把html修改成下面的這種

<form action="/user" method="get">
    <input value="REST-GET 提交" type="submit"/>
</form>
<form action="/user" method="post">
    <input value="REST-POST 提交" type="submit"/>
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="delete"/>
    <input name="_m" type="hidden" value="delete"/>
    <input value="REST-DELETE 提交" type="submit"/>
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="PUT"/>
    <input value="REST-PUT 提交" type="submit"/>
</form>

但是光改成這樣,不能生效,因為在自動配置類中,默認是關閉的,需要手動開啟。

image-20210310140907385

spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true

image-20210310141126087

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		HttpServletRequest requestToUse = request;
       //這里先判斷請求是否是POST,而且沒有異常
		if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
            //methodParam 就是_method
			String paramValue = request.getParameter(this.methodParam);
			if (StringUtils.hasLength(paramValue)) {
				String method = paramValue.toUpperCase(Locale.ENGLISH);
                //兼容以下請求;**PUT**.**DELETE**.**PATCH**
				if (ALLOWED_METHODS.contains(method)) {
                    //原生request(post),包裝模式requesWrapper重寫了getMethod方法,返回的是傳入的值
                    //過濾器鏈放行的時候用wrapper。以后的方法調用getMethod是調用****requesWrapper的。
					requestToUse = new HttpMethodRequestWrapper(request, method);
				}
			}
		}

		filterChain.doFilter(requestToUse, response);
	}

源碼

畫了一個簡易的流程圖,方便結合下面的代碼一起看。

image-20210311171148472

  • 首先請求都會經過mvc的DispatcherServlet的doDispatch方法。

image-20210311154645601

其中這里的request方法,里面包含了各種請求信息。

image-20210310154839176

  • 然后進去到這個getHandler方法。
	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//這里的是,List<HandlerMapping>,具體見下圖
        if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
                //這里會for循環變量各個請求映射器,看那個可以處理請求。
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

image-20210310160140576

  • 然后進去到 AbstractHandlerMapping的getHandler方法中。
	@Override
	@Nullable
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //  這里就是返回的HandlerMethod,具體信息如下圖
		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 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 = getCorsConfiguration(handler, request);
			if (getCorsConfigurationSource() != null) {
				CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
				config = (globalConfig != null ? globalConfig.combine(config) : config);
			}
			if (config != null) {
				config.validateAllowCredentials();
			}
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}

		return executionChain;
	}

image-20210311161444551

	@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        //lookupPath 就是請求的路徑 /user
		String lookupPath = initLookupPath(request);
        //這里獲取一個讀鎖
		this.mappingRegistry.acquireReadLock();
		try {
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
            //釋放鎖
			this.mappingRegistry.releaseReadLock();
		}
	}
@Nullable
	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        //注,這里的Match里面保存了Mapping和HandlerMethod
		List<Match> matches = new ArrayList<>();
        //這里根據請求的路徑,找到能匹配的路徑,見下圖可以找到4個
		List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
		}
		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);
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.handlerMethod;
		}
		else {
			return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
		}
	}

image-20210310172143598

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
		for (T mapping : mappings) {
            //這里對那4個請求進行判斷。
			T match = getMatchingMapping(mapping, request);
			if (match != null) {
				matches.add(new Match(match,
						this.mappingRegistry.getRegistrations().get(mapping).getHandlerMethod()));
			}
		}
	}

image-20210311161043552

然后找到匹配的請求后,會封裝成一個Match,具體信息如下:

image-20210311161210027

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                                       //這步,會把方法放到攔截器當中。
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
    //這里會遍歷所有的攔截器
		for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
			if (interceptor instanceof MappedInterceptor) {
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
				if (mappedInterceptor.matches(request)) {
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}

這里先分析到請求進來,然后找到合適的處理器映射器,再到綁定攔截器,下面的流程后面在分析。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM