这次我们说的是Spring接收消息的过程:
1:Spring有一个处理http请求的类:DispatcherServlet。在Springboot启动过程中,在DispatcherServletAutoConfiguration 类里,会创建 DispatcherServlet 实例,代码如下:
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(
this.webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(
this.webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(
this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
return dispatcherServlet;
}
2:在这个类中,也注册了 ServletRegistrationBean 实例,其构造函数中,有参数 DispatcherServlet。 如下:
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public ServletRegistrationBean dispatcherServletRegistration(
DispatcherServlet dispatcherServlet) {
ServletRegistrationBean registration = new ServletRegistrationBean(
dispatcherServlet, this.serverProperties.getServletMapping());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(
this.webMvcProperties.getServlet().getLoadOnStartup());
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
return registration;
}
3:在上一篇的tomcat启动过程中,一个很重要的方法就是调用 ServletContextInitializer接口的 onStartup 方法。而 ServletRegistrationBean 类就实现了该接口。在该方法中,很重要的就是把 DispatcherServlet 实例加入ServletContext 中。 代码如下:
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
Assert.notNull(this.servlet, "Servlet must not be null");
String name = getServletName();
if (!isEnabled()) {
logger.info("Servlet " + name + " was not registered (disabled)");
return;
}
logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings);
Dynamic added = servletContext.addServlet(name, this.servlet);
if (added == null) {
logger.info("Servlet " + name + " was not registered "
+ "(possibly already registered?)");
return;
}
configure(added);
}
4:在tomcat启动后,就可以接受http请求了。首先是 Connector 接受请求,其调用顺序如下是:Connector --> Engine --> Host --> Context -->Servlet。这里的Servlet 就是 DispatcherServlet 实例。Engine 调用 Host 的代码如下(其他类似):
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Host to be used for this Request
Host host = request.getHost();
if (host == null) {
......
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
// Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}
5:在 WebMvcAutoConfiguration 配置中,会配置 请求对象是配置:RequestMappingHandlerAdapter,代码如下:
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties != null
? this.mvcProperties.isIgnoreDefaultModelOnRedirect() : true);
return adapter;
}
6:在 WebMvcAutoConfiguration 中会注册 RequestMappingHandlerMapping 实例。如下:
@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
// Must be @Primary for MvcUriComponentsBuilder to work
return super.requestMappingHandlerMapping();
}
7:在 RequestMappingHandlerMapping 类匹配的注解是:@Controller 和 @RequestMapping。
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
该类的 afterPropertiesSet 方法里,会解析类和方法的 @RequestMapping 注解,并将结果缓存到 MappingRegistry 中。
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
}
return info;
}
protected void initHandlerMethods() {
String[] beanNames = getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = getApplicationContext().getType(beanName);
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
8:RequestMappingHandlerAdapter 继承了 InitializingBean 接口。在初始化后,会调用 afterPropertiesSet 方法。在该方法中,会注册请求和相应参数的解析类:比如:
@RequestParam 的处理类:RequestParamMethodArgumentResolver
@PathVariable 的处理类:PathVariableMethodArgumentResolver
@RequestBody 的处理类:RequestResponseBodyMethodProcessor
@ResponseBody 的处理类:RequestResponseBodyMethodProcessor
代码如下:
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
8:在方法到达 DispatcherServlet 后,会调用 service 方法。如果该方法是Post请求,则调用 doPost 方法,如果是 GET 请求,则调用 doGet 方法。DELETE,PUT等请求方法类似。 代码如下:
} 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);
9:doPost 方法进入后,方法调用顺序是:doPost() --> processRequest() --> doService() --> doDispatch()。
在这个方法里,首先根据请求的URL找到对应的@RequestMapping注册信息,然后生成对应的 HandlerExecutionChain 对象。如果类型有@Controller或者@RequestMapping注解,则找到的handler是 RequestMappingHandlerAdapter 实例。代码如下:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
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 = getApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
10:拿到 Handler 后,调用的方法顺序是:handle() --> handleInternal() --> invokeHandlerMethod()。 在方法 invokeHandlerMethod 里,创建 ServletInvocableHandlerMethod 类型对象,然后调用 invokeAndHandle 方法。
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"' with arguments " + Arrays.toString(args));
}
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"] returned [" + returnValue + "]");
}
return returnValue;
}
11:在上面的方法里,首先会进行参数解析。这里的方法就是根据参数的各种纾解去找到对应的解析类。比如注解 @RequestBody 的处理类就是RequestResponseBodyMethodProcessor。 然后调用 resolveArgument 方法解析对象。
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
}
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
12:参数解析后,利用反射,执行 invoke 方法。对返回的参数也要进行解析。代码如下(和请求参数比较类型):
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}