前言
一次为了解决跨域问题,采用了CORS方法。根据官方解释,只需要在响应头里设置
1、Access-Control-Allow-Origin
2、Access-Control-Allow-Methods
3、Access-Control-Allow-Headers
三个值就可以了,于是想到在HandlerInterceptor#preHandle()里去拦截跨域请求(options),然后再根据自定义注解判断请求的controller是否支持跨域请求,再设置对应的响应头。(项目基于spring3.2.x)但是发现请求死活无法进入preHandle里(项目里只有一个自定义的preHandle,不存在提前被别的HandlerInterceptor返回的情况)。于是利用debug大法,发现spring获取拦截器时是根据url和请求类型进行判断的,由于跨域请类型是options,无法获取对于的handler和HandlerInterceptor,导致直接就返回了,没有进入拦截器里。(spring4.x后有个默认的handler支持处理options)。于是把debug过程中学习到的知识,下次排查问题可以更快。
Dispathcher处理请求的流程概览
组件 | 说明 |
---|---|
Dispatcher | 负责接收用户请求,并且协调内部的各个组件完成请求的响应 |
HandlerMapping | 通过request获取handler和interceptors |
HandlerAdapter | 处理器的适配器。Spring 中的处理器的实现多变,可以通过实现 Controller 接口,也可以用 @RequestMapping 注解将方法作为一个处理器等,这就导致调用处理器是不确定的。所以这里需要一个处理器适配器,统一调用逻辑。 |
ViewResolver | 解析视图,返回数据 |
Dispathcer的继承图
从继承视图可以看出,Dispatcher是Servlet的一个实现类。也就是遵循了J2EE规范的处理器。
Servlet是一个接口,包含以下方法
public interface Servlet {
/**
* 对配置文件(web.xml)的解析,初始化
*/
public void init(ServletConfig config) throws ServletException;
public ServletConfig getServletConfig();
/**
* 业务逻辑实现在该方法内
* 该方法会被Web容器(如:Tomcat)调用
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
public String getServletInfo();
public void destroy();
}
HttpServlet这个类是和 HTTP 协议相关。该类的关注点在于怎么处理 HTTP 请求,比如其定义了 doGet 方法处理 GET 类型的请求,定义了 doPost 方法处理 POST 类型的请求等。我们若需要基于 Servlet 写 Web 应用,应继承该类,并覆盖指定的方法。所有的处理get请求、post请求都是由service 方法进行调用的。如下:
public abstract class HttpServlet extends GenericServlet
implements java.io.Serializable {
/**
*实现Servlet的service方法,并且将请求转为http请求
*调用内部方法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请求的分发
*/
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