源码分析基于spring 4.3.x
文章主要记录看spring mvc源码时的一些关键点。不当之处,还望指出。
HttpServlet接口结构
HttpServlet.pngHttpServletBean
HttpServletBean是接口结构第一个Spring的类,它从web.xml或WebApplicationInitializer接收servlet的init-param值,注入到bean的属性。
FrameworkServlet
FrameworkServlet集成了Servlet功能与Spring web application context上下文,实现了ApplicationContextAware接口。 但它也能够自行创建web application context。
HttpServletBean父类将init-params注入为bean属性。
HttpServlet中doGet/doPost/doPut/doDelete等方法,统一调用processRequest方法处理(该方法最终会调用DispatcherServlet.doService)
DispatcherServlet
DispatcherServlet是springmvc的核心类,实现了doService方法,
负责请求准备,Flash映射,请求分派(doDispatch),异常处理等工作。
doDispatch抽取核心代码如下
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
ModelAndView mv = null;
try {
// 查找handler
mappedHandler = getHandler(processedRequest);
// 查找HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 调用HandlerInterceptor.preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 调用handler处理
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
// 调用HandlerInterceptor.preHandlepostHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
...
}
// 结果处理
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
finally {
// 资源清理
...
}
}
HandlerMapping
HandlerMapping.pngHandlerMapping负责定义requests和handler的映射关系。
AbstractHandlerMapping.getHandler获取HandlerExecutionChain,HandlerExecutionChain是对handler的一个扩展,包含了handler(一般是HandlerMethod)和interceptors
AbstractHandlerMapping.getHandlerInternal获取对应的处理方法HandlerMethod,通过lookupHandlerMethod方法查找。
AbstractHandlerMethodMapping 实现了InitializingBean, afterPropertiesSet方法查找并解析所有RequestMapping注解的方法,并将结果存储在mappingRegistry属性中。
HandlerAdapter
HandlerAdapter.pngHandlerAdapter负责使用handler处理requests,核心方法ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
spring中提供了多种HandlerAdapter, SimpleServletHandlerAdapter/SimpleControllerHandlerAdapter/RequestMappingHandlerAdapter(springmvc使用)。
RequestMappingHandlerAdapter.invokeHandlerMethod方法具体实现了方法调用
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 获取方法
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
}
ServletInvocableHandlerMethod.invokeAndHandle负责参数解析,方法调用,结果处理。
参数解析
HandlerMethod.pngHandlerMethod是对RequestMapping映射方法的抽象。
InvocableHandlerMethod.getMethodArgumentValues
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取方法参数
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// argumentResolvers是否支持
if (this.argumentResolvers.supportsParameter(parameter)) {
// argumentResolvers解析
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
}
...
}
return args;
}
参数解析通过HandlerMethodArgumentResolver完成。
HandlerMethodArgumentResolver.png
RequestParamMethodArgumentResolver负责处理@RequestParam注解的参数
PathVariableMethodArgumentResolver负责处理@PathVariable注解的参数
RequestResponseBodyMethodProcessor负责处理 @RequestBody 注解,
从AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters中可以看到,它会根据HttpReqeust的contentType,查找对应的HttpMessageConverter进行处理。
AbstractMessageConverterMethodArgumentResolver.messageConverters属性保存所有的HttpMessageConverter,包括json, xml的转化。
结果处理
方法调用结果有两个地方处理。
ServletInvocableHandlerMethod.handleReturnValue
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 查找HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
// 结果处理
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
我们一般使用@ResponseBody注解方法,声明返回json结果。@ResponseBody注解也是由RequestResponseBodyMethodProcessor进行处理的。
AbstractMessageConverterMethodProcessor.writeWithMessageConverters转化结果并输出到客户端。
- DispatcherServlet.processDispatchResult负责渲染ModelAndView
HandlerInterceptor
HandlerInterceptor也是我们常用的功能。HandlerInterceptor类似于Servlet 规范中的过滤器Filter,可以对每个request进行预处理和后处理。通过HandlerInterceptor,我们可以实现身份认证,日志等功能。
前面说过了,HandlerExecutionChain中包含了HandlerInterceptor,DispatcherServlet.doDispatch在handler处理前后会调用mappedHandler.applyPreHandle
, mappedHandler.applyPostHandle
,而mappedHandler.triggerAfterCompletion
在ModelAndView渲染后调用。
异常处理
springmvc提供了多种处理异常的方法,如Controller中定义
@Controller
public class SimpleController {
// @RequestMapping methods omitted ...
@ExceptionHandler(IOException.class)
public ResponseEntity<String> handleIOException(IOException ex) {
// prepare responseEntity
return responseEntity;
}
}
DispatcherServlet.processHandlerException负责处理异常
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
...
throw ex;
}
springmvc提供了三种HandlerExceptionResolver实现,ExceptionHandlerExceptionResolver/DefaultHandlerExceptionResolver/ResponseStatusExceptionResolver,
上面@ExceptionHandler注解方法就是由ExceptionHandlerExceptionResolver进行处理。
我们也可以自定义HandlerExceptionResolver的实现,自行进行异常处理。
可参考 sprimgmvc-1.6. Exception Handling
Spring ApplicationContext层次
springmvc中有两处配置会创建Spring ApplicationContext。
ContextLoaderListener
ContextLoaderListener实现了接口,当web容器启动时,会调用他的contextInitialized方法,web容器关闭时会调用contextDestroyed方法。
ContextLoaderListener会创建ApplicationContext。
先看看web.xml配置
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-core-config.xml</param-value>
</context-param>
ContextLoaderListener.contextInitialized
主要调用ContextLoader.initWebApplicationContext
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
...
if (this.context == null) {
// 默认创建XmlWebApplicationContext
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
...
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
// 将ApplicationContext放到 ServletContext
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
return this.context;
}
注意:servletContext.setAttribute
这里将这个spring ApplicationContext放到了ServletContext中(后面会使用到它)。
configureAndRefreshWebApplicationContext
会找到ServletContext中contextConfigLocation参数(这里是spring-core-config.xml)作为ApplicationContext配置路径,并refresh
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
...
wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
wac.refresh();
}
FrameworkServlet
FrameworkServlet.initServletBean方法也会创建一个spring context。
web.xml的配置
<servlet>
<servlet-name>hello-dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
可以看看他的initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (wac == null) {
// 从ServletContext中查找WebApplicationContext
wac = findWebApplicationContext();
}
if (wac == null) {
// 创建WebApplicationContext
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
onRefresh(wac);
}
return wac;
}
这里的rootContext,会找到ContextLoaderListener中创建的WebApplicationContext
看看createWebApplicationContext
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
// 默认XmlWebApplicationContext
Class<?> contextClass = getContextClass();
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
// 设置parent
wac.setParent(parent);
// 设置配置路径
wac.setConfigLocation(getContextConfigLocation());
// refresh
configureAndRefreshWebApplicationContext(wac);
return wac;
}
这里同样将contextConfigLocation参数(这里是spring-mvc-config.xml)作为WebApplicationContext的配置路径。
要注意的是,这里将ContextLoaderListener中创建的WebApplicationContext作为parent了。
ApplicationContext查找bean时,会先从parent context中查找。所以,FrameworkServlet Application可以找到ContextLoaderListener Application中定义的bean,但ContextLoaderListener Application找不到FrameworkServlet Application中定义的bean。这点值得注意,以免出现bean注入失败的异常。
网友评论