1.Spring MVC源码分析(一)
Spring MVC这个框架大家应该都挺熟悉的,是目前主流的WEB应用框架。Spring MVC中有一个核心类就是org.springframework.web.servlet.DispatcherServlet,也称为前端控制器,要看 Spring MVC源码,这个类是重中之重。
首先,我们看下DispatcherServlet的整体类图,对它有个整体的了解。
我们主要关注我上图中红色框内的部分,从这个类图中可以看出来,DispatcherServlet本质上就是一个Servlet,一想到Servlet我们就应该想到它的生命周期:init、service、destroy,如果对Servlet不熟悉,建议先去看一下相关内容,现在根据这个生命周期我们可以做一个大胆的猜测:1.DispatcherServlet的初始化就是在init中;2.DispatcherServlet的执行是在service中。下面我们就根据这两个方向来验证我们的猜想。
1.DispatcherServlet的初始化
刚刚我们已经猜想了DispatcherServlet的初始化是在init中,那么我们就应该找到init方法,由于顶层接口javax.servlet.Servlet是一个接口,我们就不看了,直接找到javax.servlet.GenericServlet的init开始按照类图一步一步往下看。
javax.servlet.GenericServlet(通用Servlet,不带有任何协议):
//实现了Servlet的init方法
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
//这个重载的init方法,完全交由子类实现
public void init() throws ServletException {
}
javax.servlet.http.HttpServlet(Http协议的Servlet):
我们发现该类中并没有init方法,那我们就不用看了,直接看
HttpServlet的下一个类。
org.springframework.web.servlet.HttpServletBean(配置Servlet的Bean):
我们在这个类中顺利的找到了javax.servlet.GenericServlet被重写的init方法。
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
//1.获取web.xml DispatcherServlet的init-param标签,设置配置信息。
//这个方法被final修饰了,意思就是这一部分是必须的,下面的initServletBean完全交由子类实现
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
//2.我们主要关注这里,初始化ServletBean,完全交由子类实现
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
org.springframework.web.servlet.FrameworkServlet(Spring web的基础Servlet,提供了与Spring应用上下文的集成):
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
//1.初始化Web容器,我们需要关心这个
this.webApplicationContext = initWebApplicationContext();
//2.这个不用关心,本类和子类都没有提供实现
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
protected WebApplicationContext initWebApplicationContext() {
//1.查找root application context
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//2.webApplicationContext 是否已经存在 一般都是false
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
//3.如果为空
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
//查看是否已经注册过了
wac = findWebApplicationContext();
}
//4.创建
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
//这里会自动刷新
wac = createWebApplicationContext(rootContext);
}
//5.如果走上面创建WebApplicationContext,那么将不会刷新
//当WebApplicationContext已经存在了,会走手动刷新
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
// 6.我们主要关注这里: DispatcherServlet 就是在这里实现的
// 这个方法默认啥都不做,完全交由子类实现,这里就直接到了DispatcherServlet了
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
org.springframework.web.servlet.DispatcherServlet(前端控制器):
@Override
protected void onRefresh(ApplicationContext context) {
//核心初始化方法
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
//文件上传
initMultipartResolver(context);
//国际化
initLocaleResolver(context);
//主题
initThemeResolver(context);
//请求-处理器映射
initHandlerMappings(context);
//处理器适配器
initHandlerAdapters(context);
//异常处理
initHandlerExceptionResolvers(context);
//将请求的url转为视图的名称
initRequestToViewNameTranslator(context);
//视图解析器
initViewResolvers(context);
//保存请求资源,例如session中的,供forward、redirect使用,也能及时的清除过期资源
initFlashMapManager(context);
}
上面的initStrategies方法包含了各个组件的初始化,这里我就挑两个来看一下:
org.springframework.web.servlet.DispatcherServlet#initMultipartResolver(初始化文件上传组件):
private void initMultipartResolver(ApplicationContext context) {
try {
//1.加载我们申明的MultipartResolver,这里我们申明的CommonsMultipartResolver bean的名称是固定的,否则会读取不到,DispatcherServlet已经申明好了
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
}
}
catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
//2.默认为null
this.multipartResolver = null;
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
"': no multipart request handling provided");
}
}
}
org.springframework.web.servlet.DispatcherServlet#initHandlerExceptionResolvers(初始化异常处理组件):
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
//1.默认加载所有我们自己实现的异常处理器
if (this.detectAllHandlerExceptionResolvers) {
// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values());
// We keep HandlerExceptionResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
}
else {
try {
//2.加载申明的相同bean name的异常处理器
HandlerExceptionResolver her =
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
this.handlerExceptionResolvers = Collections.singletonList(her);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, no HandlerExceptionResolver is fine too.
}
}
//3.默认异常处理器
// Ensure we have at least some HandlerExceptionResolvers, by registering
// default HandlerExceptionResolvers if no other resolvers are found.
if (this.handlerExceptionResolvers == null) {
//我们需要看下这个方法,可以知道DispatcherServlet默认值是从哪里来的?
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerExceptionResolvers found in servlet '" + getServletName() + "': using default");
}
}
}
org.springframework.web.servlet.DispatcherServlet#getDefaultStrategies:
可以看到getDefaultStrategies方法是从一个Properties中获取的值,那么问题来了,这个Properties是从哪里来的?
可以看到是静态块出事了Properties,那么我们就去看下这个Properties文件。
private static final Properties defaultStrategies;
//在静态块初始化加载了DispatcherServlet.properties文件
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
DispatcherServlet.properties(申明了DispatcherServlet的默认组件):
剩下的大家可以自己去看一下,我这里只挑了两个具有代表性的看了一下,只要理解了这里,其他的都差不多。
至此,这个DispatcherServelt的初始化工作就完成了。总结一下:
1.
DispatcherServlet是一个Servlet,初始化工作就是在init方法中完成的。2.
Servlet的实现GenericServlet是一个通用Servlet,与协议无关,init方法只是获取了ServletConfig,并调用子类的init方法。3.
HttpServletBean实现了GenericServlet的init方法,完成了web.xml中DispatcherServlet的init-param的配置,并调用子类的initServletBean方法。4.
FrameworkServlet是Spring Web模块的基础Servlet,集成了Spring Application Context。通过调用initWebApplicationContext方法初始化容器,如果容器不存在,则创建容器,并自动刷新;如果容器已经存在,则调用子类的onRefresh方法手动刷新容器,这里就到DispatcherServlet了。5.
DispatcherServlet通过调用initStrategies方法,initStrategies方法调用了各个组件的initXXX方法初始化各个组件。6.各个组件的
initXXX方法都是差不多的步骤:1.加载用户定义的组件;2.根据DispatcherServlet.properties加载默认组件。
2.DispatcherServlet的执行
我们前面已经猜想了DispatcherServlet的执行是在service方法中,那么我们现在也和上面看初始化一样,按照类图,一步一步的往下看。
和上面一样,我们直接从javax.servlet.GenericServlet开始看。
javax.servlet.GenericServlet:
//抽象方法,完全交由子类实现
public abstract void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
javax.servlet.http.HttpServlet:
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
//1.转换
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
//2.根据请求类型进行分发
service(request, response);
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
//1.获取请求类型,根据不同的类型,做不同的处理
String method = req.getMethod();
//2.这里的doXXX方法该类有实现,但是一般调用的是子类实现,这里会跳转到FrameworkServlet
//3.请求方式比较多,我们直接以get请求为例,往下看
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);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
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);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
org.springframework.web.servlet.HttpServletBean:
该类重写了init方法,在上面DispatcherServlet初始化的时候已经看过了,这里没有重写service方法,不用关心。
org.springframework.web.servlet.FrameworkServlet:
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1.所有的doXXX方法都会调用这个方法
processRequest(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1.这些国际化、属性、异步先不管
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
//2.我们要关注的:这是一个抽象方法,完全交给DispatcherServlet去实现
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
org.springframework.web.servlet.DispatcherServlet:
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
//1.保留原始属性快照
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
//2.请求属性设置
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
//3.移除与请求匹配的上一个请求保存的FlashMap,并删除过期数据
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
//4.这是我们比较熟悉的一个方法了,做分发
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//1.获取Handler,其实HandlerExecutionChain,这个类包含了Handler和HandlerInterceptor
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//2.找到Handler的适配器,因为Handler是Object,所以通过SPI机制,找到对应Handler的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//3.org.springframework.web.servlet.HandlerInterceptor#preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//4.真正的去执行
//对于@RequestBody、@ResponseBody、@RestController等注解使用的是AbstractHandlerMethodAdapter
//这个方法里面主要三步:
//1.参数解析:涉及转换器
//2.反射执行
//3.结果封装:涉及转换器
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//5.将请求转为视图名称
applyDefaultViewName(processedRequest, mv);
//6.org.springframework.web.servlet.HandlerInterceptor#postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//7.视图渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//8.org.springframework.web.servlet.HandlerInterceptor#afterCompletion
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
doDispatch方法的核心流程:
1.根据请求的url从HandlerMapping中获取HandlerExecutionChain,可以理解为Handler。
2.HandlerAdapter 典型的适配器模式,找到处理Handler的HandlerAdapter,因为Handler是Object,便于第三方框架扩展。
3.HandlerInterceptor#preHandle。
4.HandlerAdapter#handler。
1.org.springframework.web.servlet.mvc.method.annotation.
RequestResponseBodyMethodProcessor#resolveArgument 涉及转换器。
2.invoke。
3.org.springframework.web.servlet.mvc.method.annotation.
RequestResponseBodyMethodProcessor#handleReturnValue 涉及转换器。
5.HandlerInterceptor#postHandle。
6.DispatcherServlet#processDispatcherResult。
1.异常处理。
2.视图渲染 render。
7.如果发生异常:HandlerInterceptor#afterCompletion。
也就是我们经常看到的Spring MVC的核心流程图: