前言
一次為了解決跨域問題屹逛,采用了CORS方法汛骂。根據(jù)官方解釋
香缺,只需要在響應頭里設置
1、Access-Control-Allow-Origin
2图张、Access-Control-Allow-Methods
3、Access-Control-Allow-Headers
三個值就可以了祸轮,于是想到在HandlerInterceptor#preHandle()里去攔截跨域請求(options),然后再根據(jù)自定義注解判斷請求的controller是否支持跨域請求柄错,再設置對應的響應頭苦酱。(項目基于spring3.2.x)但是發(fā)現(xiàn)請求死活無法進入preHandle里(項目里只有一個自定義的preHandle,不存在提前被別的HandlerInterceptor返回的情況)颂跨。于是利用debug大法扯饶,發(fā)現(xiàn)spring獲取攔截器時是根據(jù)url和請求類型進行判斷的池颈,由于跨域請類型是options钓丰,無法獲取對應的handler和HandlerInterceptor,導致直接就返回了携丁,沒有進入攔截器里。(spring4.x后有個默認的handler支持處理options)则北。于是把debug過程中學習到的知識痕慢,下次排查問題可以更快。
Dispathcher處理請求的流程概覽
組件 | 說明 |
---|---|
Dispatcher | 負責接收用戶請求掖举,并且協(xié)調內(nèi)部的各個組件完成請求的響應 |
HandlerMapping | 通過request獲取handler和interceptors |
HandlerAdapter | 處理器的適配器。Spring 中的處理器的實現(xiàn)多變方篮,可以通過實現(xiàn) Controller 接口励负,也可以用 @RequestMapping 注解將方法作為一個處理器等,這就導致調用處理器是不確定的继榆。所以這里需要一個處理器適配器,統(tǒng)一調用邏輯集币。 |
ViewResolver | 解析視圖翠忠,返回數(shù)據(jù) |
Dispathcer的繼承圖
從繼承視圖可以看出,Dispatcher是Servlet的一個實現(xiàn)類当娱。也就是遵循了J2EE規(guī)范的處理器考榨。
Servlet是一個接口,包含以下方法
public interface Servlet {
/**
* 對配置文件(web.xml)的解析董虱,初始化
*/
public void init(ServletConfig config) throws ServletException;
public ServletConfig getServletConfig();
/**
* 業(yè)務邏輯實現(xiàn)在該方法內(nèi)
* 該方法會被Web容器(如:Tomcat)調用
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
public String getServletInfo();
public void destroy();
}
HttpServlet這個類是和 HTTP 協(xié)議相關申鱼。該類的關注點在于怎么處理 HTTP 請求云头,比如其定義了 doGet 方法處理 GET 類型的請求,定義了 doPost 方法處理 POST 類型的請求等匣砖。我們?nèi)粜枰?Servlet 寫 Web 應用昏滴,應繼承該類,并覆蓋指定的方法谣殊。所有的處理get請求姻几、post請求都是由service 方法進行調用的宜狐。如下:
public abstract class HttpServlet extends GenericServlet
implements java.io.Serializable {
/**
*實現(xiàn)Servlet的service方法,并且將請求轉為http請求
*調用內(nèi)部方法service(HttpServletRequest req, HttpServletResponse resp)抚恒,處理http請求
*
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
/**
*http請求的分發(fā)
*/
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req, resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req, resp);
}
}
//其他方法
}
Dispatcher沒有直接實現(xiàn)servlet,而是繼承了HttpServlet络拌。對于http請求的處理流程:
HttpServlet.service -> FrameworkServlet.service -> FrameworkServlet.processRequest -> DispatcherServlet.doService -> DispatcherServlet.doDispatch
Dispatcher#doDispatch
Dispatcher對請求進行處理在doDispatch方法里
// 省略了內(nèi)部實現(xiàn)俭驮,只看核心的地方
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//S1 先獲取到請求的處理器Handler和攔截器interceptors
HandlerExecutionChain mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
/*
* S2
* 執(zhí)行攔截器,一般自定義的HandlerInterceptor#preHandle就是在這里執(zhí)行的
* 里面也很簡單春贸,就是一個for循環(huán)表鳍,不停的執(zhí)行preHandle方法,直到某個攔截器返回false
* 或者循環(huán)結束
*/
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//S3 獲取Handler對于的HandlerAdapter,負責調用Handler獲取結果
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//S4 執(zhí)行handler#handle,返回ModelAndView
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//S5 同理祥诽,一個for循環(huán)執(zhí)行HandlerInterceptor#postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
//S6 解析并渲染視圖
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
以上比較核心的三步是:
1譬圣、獲取HandlerExecutionChain,也就是處理器和攔截器
2雄坪、獲取handler的adapter
3厘熟、執(zhí)行handler#handle,返回結果
下面分別看下三個步驟的實現(xiàn)
獲取HandlerExecutionChain
//HandlerExecutionChain mappedHandler = getHandler(processedRequest, false);
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
邏輯很簡單:for循環(huán)去匹配request對應的HandlerExecutionChain维哈,其中handlerMappings被定義為List<HandlerMapping>绳姨。HandlerMapping是一個接口阔挠,繼承關系如下:
HandlerMapping的getHandler方法如下:
//省略內(nèi)部實現(xiàn)
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
return getHandlerExecutionChain(handler, request);
}
1飘庄、通過getHandlerInternal獲取handler,是一個模板方法购撼,由子類具有去實現(xiàn)跪削,主要有兩個實現(xiàn)
1.1谴仙、AbstractHandlerMethodMapping#getHandlerInternal
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
}
1.2、AbstractUrlHandlerMapping#getHandlerInternal
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
Object handler = lookupHandler(lookupPath, request);
}
尋找handler的方法都是獲取request的請求url碾盐,然后根據(jù)url去獲取controller了晃跺。這里也就是使用@RequestMapping注解的方法。
以lookupHandler為例
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 能直接匹配就返回毫玖,比如 "/test" matches "/test"
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// "/t*" matches both "/test" and "/team"
List<String> matchingPatterns = new ArrayList<String>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
}
// spring官方解釋掀虎,按照最長路徑進行匹配
String bestPatternMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
Collections.sort(matchingPatterns, patternComparator);
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
bestPatternMatch = matchingPatterns.get(0);
}
if (bestPatternMatch != null) {
handler = this.handlerMap.get(bestPatternMatch);
validateHandler(handler, request);
}
這里的核心是this.handlerMap.get(urlPath),所以的操作都是為了從map從獲取數(shù)據(jù)付枫。map是怎么被初始化的呢烹玉?
map是通過registerHandler方法初始化的,每個子類都可以覆蓋該方法阐滩,實現(xiàn)自己的數(shù)據(jù)初始化二打,但是最終的匹配handler過程是由父類統(tǒng)一實現(xiàn)的狰右。實現(xiàn)了數(shù)據(jù)和操作的分離。
registerHandler也很簡單浮定,先根據(jù)url從map中取handler叔锐,如果存在多個handler則報錯(一個url無法對應多個handler)。
沒有則存入handler很魂。
2、找到攔截器,將處理器和攔截器封裝后返回饱溢。
/**
* 獲取攔截器的邏輯比較簡單,也是url匹配
*/
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
chain.addInterceptors(getAdaptedInterceptors());
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
return chain;
}
public class HandlerExecutionChain {
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
private final Object handler;
private HandlerInterceptor[] interceptors;
private List<HandlerInterceptor> interceptorList;
private int interceptorIndex = -1;
}
這里不太理解為什么同時需要interceptors 和 interceptorList走芋,都是同樣的類型绩郎。
獲取HandlerAdapter
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
HandlerAdapter接口定義很簡單,通過support判斷是否支持該handler翁逞,通過handler執(zhí)行handler的方法肋杖。
@ResponseBody 和@RequestBody的使用
? 一般controller的入?yún)⒑统鰠⒍际莏son的形式,只需要使用注解@ResponseBody 和 @RequestBody就可以完成http請求報文和pojo對象之間的轉化挖函。消息的轉化都是通過HttpMessageConverter實現(xiàn)的状植。
public interface HttpMessageConverter<T> {
// 當前轉換器是否能將對象類型轉換為HTTP報文
boolean canWrite(Class<?> clazz, MediaType mediaType);
// 轉換器能支持的HTTP媒體類型
List<MediaType> getSupportedMediaTypes();
// 轉換HTTP報文為特定類型
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
// 將特定類型對象轉換為HTTP報文
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
read方法即是讀取HTTP請求轉換為參數(shù)對象,write方法即是將返回值對象轉換為HTTP響應報文怨喘。Spring定義了參數(shù)解析器HandlerMethodArgumentResolver和返回值處理器HandlerMethodReturnValueHandler統(tǒng)一處理津畸。
// 參數(shù)解析器接口
public interface HandlerMethodArgumentResolver {
// 解析器是否支持方法參數(shù)
boolean supportsParameter(MethodParameter parameter);
// 解析HTTP報文中對應的方法參數(shù)
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}
// 返回值處理器接口
public interface HandlerMethodReturnValueHandler {
// 處理器是否支持返回值類型
boolean supportsReturnType(MethodParameter returnType);
// 將返回值解析為HTTP響應報文
void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
整體的處理流程:
而對于@ResponseBody和@RequestBody都由RequestResponseBodyMethodProcessor統(tǒng)一進行處理。也就是RequestResponseBodyMethodProcessor即實現(xiàn)了HandlerMethodArgumentResolver 也實現(xiàn)了 HandlerMethodReturnValueHandler 必怜。
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
// 支持RequestBody注解參數(shù)
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
// 支持ResponseBody注解返回值
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
//解析參數(shù)
@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
return arg;
}
// 解析返回值
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
}