个人学习Spring源码系列 SpringMVC
(一)SpringMVC概述
1.SpringMVC介绍
Spring Web MVC是基于Servlet API构建,实现了Web MVC设计模式的原始轻量级Web框架,从一开始就包含在Spring框架中,其正式名称"Spring Web MVC".
2.SpringMVC核心流程
具体流程:
(1)首先浏览器发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;
(2)DispatcherServlet——>HandlerMapping,处理器映射器将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器对象、多个HandlerInterceptor拦截器)对象;
(3)DispatcherServlet——>HandlerAdapter,处理器适配器将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;
(4)HandlerAdapter——>调用处理器相应功能处理方法,并返回一个ModelAndView对象(包含模型数据、逻辑视图名);
(5)ModelAndView对象(Model部分是业务对象返回的模型数据,View部分为逻辑视图名)——> ViewResolver, 视图解析器将把逻辑视图名解析为具体的View;
(6)View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构;
(7)返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。
3.SpringMVC总结
(1)SpringMVC解决的痛点
1.将web页面的请求传给服务器
2.根据不同的请求处理不同的逻辑页面
3.返回处理结果数据并跳转页面
(2)SpringMVC相关文件
1. web.xml 必须 配置contexLoadListener,配置DispatcherServlet拦截请求
2. applicationContext.xml 非必须 可以用来配置InternalResourceViewResolver来给返回的页面加上前缀进行试图查找
3. model pojo,非必须
4.controller 继承AbstractController,用于处理Web请求
5.view 可以由jsp或其他试图框架完成
6.Spring-servlet.xml(非必需,一般用于模块化开发)
(二)DispatchServlet
1.DispatchServlet实现了Servlet接口
DispatchServlet实际上是实现了Servlet接口的类,Servlet的生命周期是由servlet容器来控制。
1.初始化阶段
servlet容器加载servlet类,将.class文件读入内存;
servlet容器创建ServletConfig对象,包含了servlet的初始化配置信息;
servlet容器创建servlet对象;
servlet容器调用servlet对象的init方法进行初始化
2.运行阶段
servlet接受请求时,针对请求创建servletRequest,servletResponse,调用service方法(通过servletRequest获得请求信息,根据servletResponse生成相应结果),最后销毁servletRequest,servletResponse两个对象
servlet被设计为请求驱动,请求会被封装为HttpServletRequest对象,然后传给对应服务方法。请求的类型一共有八中(常用的:GET,POST,PUT,DELETE,不太常用的:HEAD,TRACE,CONNECT.OPTIONS
3.销毁阶段
调用servlet容器的destory方法之后再销毁servlet对象,我们可以将一些servlet占用的资源的释放放在destroy方法中实现
2. 初始化
(1)DispatchServlet类中没有init方法,查找父类HttpServletBean中有init方法
public final void init() throws ServletException {
// Set bean properties from init parameters.
//从init配置参数set bean配置参数
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.
//初始化web环境
initServletBean();
}
(2)FrameworkServlet.java中initServletBean()
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
//初始化web环境
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
(3)FrameworkServlet.java中initWebApplicationContext()
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
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);
}
//配置和刷新Spring容器
//这个无非就是初始化spring ioc的环境,创建bean和实例化bean等操作
//这个方法最终也是调用refresh()方法,已在spring源码解析中解析过了
configureAndRefreshWebApplicationContext(cwac);
}
}
}
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();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
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.
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
(4)执行DispatcherServlet类的onRefresh()方法,初始化SpringMVC的配置
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); //上传文件
initLocaleResolver(context); //国际化
initThemeResolver(context); //前段的主题样式
initHandlerMappings(context); //初始化HandlerMappings 请求映射器重点
initHandlerAdapters(context); //初始化HandlerAdapters 处理适配器
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context); //视图转换器
initFlashMapManager(context); //重定向数据管理器
}
(5)执行initHandlerMappings()
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
//通过配置文件中的配置信息,得到handlerMappings
if (this.handlerMappings == null) {
//使用defaultStrategies获取数据
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
for (HandlerMapping mapping : this.handlerMappings) {
if (mapping.usesPathPatterns()) {
this.parseRequestPath = true;
break;
}
}
}
(6)执行getDefaultStrategies()方法
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
if (defaultStrategies == null) {
try {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
//从属性文件加载默认策略实现,说白了这里的意思就是从 DEFAULT_STRATEGIES_PATH这个文件当中拿出所有的配置,可以去数一下一共有8个: DispatcherServlet.properties == DEFAULT_STRATEGIES_PATH
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());
}
}
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
//获取class字节码文件
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
//底层是通过调用Spring getBean()的方式创建该对象
//请求映射就是在这个方法实现装配的
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
return strategies;
}
else {
return Collections.emptyList();
}
}
3.初始化分析
(1) initHandlerMappings方法,就是初始化我们的handlerMapping(请求映射器).
(2) handlerMapping的主要作用是,找到请求路径对应的controller的方法.
例如:请求的路径 “/index”,然后这个handlerMapping,在初始化的时候,已经将所有controller的请求路径映射保存在一个map集合,当请求过来的时候,就将"/index"作为一个key,从map集合中找到对应的controller的index方法。
(3) 这里初始化handlerMappings ,默认是有两个handlerMappings ,是直接在defaultStrategies配置文件中获取.
(4) 那么defaultStrategies的值是在defaultStrategies = PropertiesLoaderUtils.loadProperties(resource)
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
4.SpringMVC 请求阶段
(1)doService()方法
用户发出一个请求过来,会由Servlet接收,并调用到DispatcherServlet的doService()方法
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
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));
}
}
}
// Make framework objects available to handlers and view objects.
// 设置Request参数
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());
if (this.flashMapManager != null) {
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);
}
RequestPath previousRequestPath = null;
if (this.parseRequestPath) {
previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
ServletRequestPathUtils.parseAndCache(request);
}
try {
//核心方法
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);
}
}
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
(2)doDispatch()方法
核心方法doDispatch()方法
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
ModelAndView mv = null;
Exception dispatchException = null;
try {
//检查请求中是否有文件上传操作
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//确定当前请求的处理程序(重点),推断controller和handler的类型
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//推断适配器,不同的controller类型,交给不同的适配器去处理
//如果是一个bean,mappedHandler.getHandler()返回的是一个对象
//如果是一个method,mappedHandler.getHandler()返回的是一个方法
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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//前置拦截器处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//通过适配器,处理请求(可以理解为,反射调用方法)(重点)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
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);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
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) 检查请求中是否有文件上传操作
(2) 确定当前请求的处理的handler(重点)
(3) 推断适配器,不同的controller类型,交给不同的适配器去处理
(4) 执行前置拦截器处理interceptor
(5) 通过找到的HandlerAdapter ,反射执行相关的业务代码controller的方法。
(6) 返回结果
(3)getHandler()方法
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
//循环所有的HandlerMappings
//this.handlerMappings这个是什么时候初始化的?(重点)
//在handlerMappings初始化的时候
for (HandlerMapping mapping : this.handlerMappings) {
//把请求传过去看能不能得到一个handler
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
(1) getHandler()方法,主要是遍历在DispatcherServlet初始化是,初始化的handlerMappings。
(2) 这个方法的主要思想是,通过request的路径,去匹配对应的controller去处理
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//获取请求的路径
String lookupPath = initLookupPath(request);
Object handler;
//找到对应的handler
if (usesPathPatterns()) {
RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
handler = lookupHandler(path, lookupPath, request);
}
else {
handler = lookupHandler(lookupPath, request);
}
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if (StringUtils.matchesCharacter(lookupPath, '/')) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
protected Object lookupHandler(String lookupPath, HttpServletRequest request) throws Exception {
//通过请求的路径,在handlerMap中去匹配
Object handler = getDirectMatch(lookupPath, request);
if (handler != null) {
return handler;
}
// Pattern match?
List<String> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, lookupPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", lookupPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
}
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(lookupPath);
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
logger.trace("Matching patterns " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0);
}
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//验证Handler
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, lookupPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, lookupPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
logger.trace("URI variables " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
(4) getHandlerAdapter()
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
一开始只是找到了Handler 现在要执行了,但是有个问题,Handler不止一个, 自然而然对应的执行方式就不同了, 这时候适配器的概念就出来了,对应不同的Handler的执行方案。当找到合适的适配器的时候, 基本上就已经收尾了。
其实我们的SpringMVC关键的概念就在于Handler(处理器) 和Adapter(适配器)
通过一个关键的HandlerMappings 找到合适处理你的Controller的Handler
然后再通过HandlerAdapters找到一个合适的HandlerAdapter 来执行Handler即Controller里面的逻辑。
最后再返回ModlAndView