SpringMVC 架构分析

1:SpringMVC初始化过程:

1.1. web.xml配置如下:

  
    
        demo-springmvc
        org.springframework.web.servlet.DispatcherServlet
        
        
            contextConfigLocation
            classpath:spring/springmvc.xml
        
        1
    
    
        demo-springmvc
        /
    

1.2. HttpServletBean:

Servlet 创建时可以直接调用无参数的 init 方法。HTTPServletBean的 init方法如下:

public final void init() throws ServletException {	
		try {
                    // 将Servlet 中配置的参数封装到
                      PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			initBeanWrapper(bw);
			bw.setPropertyValues(pvs, true);
		}		
		initServletBean();
	}

BeanWrapper的作用参考:

1.3. FrameworkServlet

从HttpServletBean 中可知,FrameworkServlet的初始化入口方法应该是initServletBean;

protected final void initServletBean() throws ServletException {
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
	}

上面只有两句核心代码:

  • 初始化WebApplicationContext
  • 用于初始化FrameworkServlet,而且initFrameworkServlet 方法是模板方法,子类可以覆盖后再里面做一些初始化工作

下面看一下initWebApplicationContext方法:

protected WebApplicationContext initWebApplicationContext() {
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					if (cwac.getParent() == null) {
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			onRefresh(wac);
		}

		if (this.publishContext) {
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}
		return wac;
	}

initWebApplicationContext 方法做了三件事情:

  1. 获取spring 的根容器rootContext,如果为 null 则进行创建createWebApplicationContext
  2. 设置webApplicationContext 并根据情况调用onRefresh方法。
  3. 将webApplicationContext设置到ServletContext

获取跟容器的原理是,默认情况下spring会将自己的容器设置成ServeletContext的属相,默认的跟容器的key为:org.springframework.web.context.WebApplicationContext.ROOT,定义下

org.springframework.web.context.WebApplicationContext 中。

	String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

所以获取根容器只需要调用ServletContext 的getAttribute 就可以了。

ServletContext#getAttribute("org.springframework.web.context.WebApplicationContext.ROOT")

其中createWebApplicationContext 的初始化过程:

  1. createBeanFactory
  2. loadBeanDefinitions(contextConfigLocation)
  3. registerBeanDefinition
IOC 加载完毕

1.4. DispatcherServlet

protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

其中initStrategies (初始化mvc环境)

  1. initHandlerMapping 初始化映射配置
  2. initHandlerAdapters 初始化适配器
  3. initHandlerExceptionResolvers
  4. initViewResolvers

2.SpringMVC架构如下图

SpringMVC 架构分析_第1张图片

DispatcherServlet  核心类如下:

SpringMVC 架构分析_第2张图片

  • DispatcherServlet:  SpringMVC 最核心的类,整个处理过程的顶层设计都在这里面。
  • HandlerMapping:  根据request 找到相应的处理器Handler 和 Interceptors。
  • HandlerAdapter:  控制器执行适配器,可以理解为使用处理器干活的人。
  • ViewResolver:  视图仓库,用来将String类型的视图名和Local解析为View类型的视图。
  • HandlerExceptionResolver:  根据异常设置ModeAndView, 之后再缴费render 方法进行渲染。

DispatcherServlet 请求过程:

1、  首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;

2、  DispatcherServlet——> 遍历注册的HandlerMappings,然后根据request 获取执行链对象(HandlerExecutionChain),包含一个Handler处理器,多个HandlerInterceptor拦截器对象。

3、  DispatcherServlet——>遍历系统中注册的HandlerAdapters 与 handler获取一个handler适配器。

4、调用执行链,调用拦截器当中的preHandle() 方法,进行预处理。

4、  HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);

5、通过执行链,去调用拦截器当中的postHandle()方法,进行拦截处理。

5、  ModelAndView的逻辑视图名——> 遍历注册的viewResolves 与 返回的viewName进行匹配

6、  View——>渲染,调用view.render() 进行视图解析和返回,设置model到request中;

7、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。

8、异常-遍历handlerExceptionResolvers 调用.......,最后跳转至异常MV


DispatcherServlet 核心代码如下:

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 {
                               //1.检查是不是上传的请求,如果是将通过MultipartResolver解析
                               processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// 2.请求到处理器的映射,通过HandlerMapping 进行映射
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}
                  // 3.遍历注册的HandlerAdapters  找到Handler相对应的适配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// 处理GET、HEAD请求的Last-Modified
				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;
					}
				}
                                  // 执行相应Interceptor 的preHandle方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
				// 由适配器执行对象的处理器(调用处理器相应功能处理方法)
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                                 //如果需要异步处理,直接返回
				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
                                 //当view 为空时(比如,Handler返回值为void),根据request设置默认view
				applyDefaultViewName(processedRequest, mv);
                                 // 执行相应的Interceptor 的postHandle方法,此方法的执行实在返回ModeAndView之前
                                mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
                       // 处理返回结果。包括异常防护力、渲染页面、发出完成 通知触发Inceptor的afterCompletion
                        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}

	}
 

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

        boolean errorView = false;

        if (exception != null) {
            if (exception instanceof ModelAndViewDefiningException) {
                logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
            }
            else {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                mv = processHandlerException(request, response, handler, exception);
                errorView = (mv != null);
            }
        }

        // Did the handler return a view to render?
        if (mv != null && !mv.wasCleared()) {
         // 解析视图进行视图的渲染,由ViewResolver解析view,视图在渲染时会把Model传入
            render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                        "': assuming HandlerAdapter completed request handling");
            }
        }

        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Concurrent handling started during a forward
            return;
        }

        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

2.1、SpringMVC 组件之HandlerMapping

HandlerMapping实现类结构:

SpringMVC 架构分析_第3张图片

BeanNameUrlHandlerMapping:基于ioc name中以 /开头的Bean时  注册到映射

SimpleUrlHandlerMapping: 基于手动配置url 与 Controller映射

RequestMappingHandlerMapping: 基于注解方法配置对应映射


public interface HandlerMapping {
	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

方法的实现非常灵活,只要使用Request返回HandlerExecutionChain就可以了,下面是实现一个自定义的HandlerMapping:

public class DemoHandlerMapping implements HandlerMapping {

	@Override
	public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		String url = request.getRequestURI().toString();
		String method = request.getMethod();

		if (url.startsWith("/demo1")) {
			return "demo1对应的handle";
		}
		return null;
	}
}

查找Handler是按顺序遍历所有的Handlermapping,当找到一个Handlermapping后立即终止并返回,代码如下:

	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

2.2、SpringMVC 组件之HandlerAdapter

HandlerAdapter 实现类结构图:

SpringMVC 架构分析_第4张图片

HandlerAdapter 可以理解为使用处理器干活的人。它里面一共有3个方法:

  • supports(Object handler) 判断是否可以使用某个handler;
  • handler方法: 用来具体使用Handler干活的人
  • getLastModified方法: 获取资源的Last-Modified ,Last-Modified 的资源的最后一次修改的时间

下面介绍一个SpringMVC 自己的HandlerAdapter-SimpleHandlerAdapter,代码如下:

public class SimpleControllerHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof Controller);
	}

	@Override
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return ((Controller) handler).handleRequest(request, response);
	}

	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}

}

上面是用来使用实现了Controlelr接口的处理器干活的,干活的方法是直接调用处理器的 handlerRequest 方法。

选择使用哪个HandlerAdapter 的过程在getHandlerAdapter方法中,它的逻辑时遍历所有的Adapter,然后检查哪个可以处理当前的Handler, 找到第一个可以处理的Handler的Adapte后就停止查找并将其返回。getHandlerAdapter方法如下:

	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		for (HandlerAdapter ha : this.handlerAdapters) {
			if (logger.isTraceEnabled()) {
				logger.trace("Testing handler adapter [" + ha + "]");
			}
			if (ha.supports(handler)) {
				return ha;
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

HandlerAdapter 主要注册到Spring MVC的容器里,注册方法和HandlerMapping一样,只要配置一个Bean就可以了。

Handler是从HandlerMapping里返回的。

2.3 .spring 组件之ViewResolver

ViewResolver 实现类图

SpringMVC 架构分析_第5张图片

ViewResolver 用来将String 类型的视图名和Locale解析为 View 类型的视图,ViewResolver 接口如下:

public interface ViewResolver {
	View resolveViewName(String viewName, Locale locale) throws Exception;
}

从接口方法的定义可以看出解析视图所需要的参数是手透明和Locale, 不过一般情况下我们只需要根据视图名找到对应的视图,然后渲染就行,并不需要对不同的区域使用不同的视图进行显示,


View 是用来渲染页面的,通俗来讲就是要将程序返回的参数填入模板中,生成html(也可能是其他类型)文件。这里有两个关键问题:

  • 使用哪个模板?
  • 用什么技术(或者规则) 填入参数

这就是viewResolver 主要要做的工作,ViewSolver需要找到渲染所用的模板和所用的技术(也是视图的类型)进行渲染,

具体的渲染过程则交给不同的视图自己完成。

我们最常使用的UrlBaseViewResolver 系列的解析器都是针对单一视图类型进行解析的,只需要找到使用的模板就可以了。

比如,InternalResourceViewResolver只针对jsp类型的视图,FreeMarkerViewResolver只针对FreeMarker,VelocityViewResolver只针对Velocity。而ResourceBundlerViewResolver、XmlViewResolver、BeanNameViewResolver等解析器可以同时解析多种类型的视图。

ResourceBundleViewResolver 配置如下:

	
		
		
			
	
   1. ViewResolve的作用就是通过解析MdoelAndView,将MdoelAndView中的逻辑视图名变为一个真正的View对象,并将MdoelAndView中的Model取出。

     2.View的作用就是在获取到ViewResolve传来的View和Model,对Model进行渲染,通过View对象找到要展示给用户的物理视图,将渲染后的视图展示给用户。用很直白的话将就是将数据通过request存储起来,找到要展示给用户的页面,将这些数据放在页面中,并将页面呈现给用户。

ViewResolver 源码介绍

  ViewResolve和前面的HandlerMapping,HandlerAdapter一样,首先是在启动服务的时候,IOC容器会根据配置文件里面的ViewResolve相关信息对ViewResolve进行实例化,并存储到DispatcherServlet的 List viewResolvers属性中。当要解析MdoelAndView对象的时候,会遍历viewResolvers,从中取出一个viewResolver对进行解析,要是解析出View对象,就不再进行遍历,要是解析出的View对象是空的,接着从viewResolvers中取出viewResolver对MdoelAndView对象进行解析。

下面是DispatcherServlet类里面取出IOC容器里面的viewResolver相关对象,可以看成是DispatcherServlet对viewResolver的注册。

ViewResolver 的初始化过程:

	private void initViewResolvers(ApplicationContext context) {
		this.viewResolvers = null;
		if (this.detectAllViewResolvers) {
			// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
			Map matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.viewResolvers = new ArrayList(matchingBeans.values());
				// We keep ViewResolvers in sorted order.
				AnnotationAwareOrderComparator.sort(this.viewResolvers);
			}
		}
		else {
			try {
				ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
				this.viewResolvers = Collections.singletonList(vr);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default ViewResolver later.
			}
		}

		if (this.viewResolvers == null) {
			this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default");
			}
		}
	}
再看看 viewResolver拿到了ModelAndView对象后是怎么处理的

DispatcherServlet的doDispatch方法中有一个render(mv, processedRequest, response);render就是对ModelAndView对象进行解析

DispatcherServlet的render方法

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {	
		Locale locale = this.localeResolver.resolveLocale(request);
		response.setLocale(locale);
		View view;
		if (mv.isReference()) {
			// 通过视图名获取到真正的View对象.
			view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
			if (view == null) {
				throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
						"' in servlet with name '" + getServletName() + "'");
			}
		}
		else {
			view = mv.getView();

		}
           //视图渲染
            view.render(mv.getModelInternal(), request, response);
		
	}
protected View resolveViewName(String viewName, Map model, Locale locale,
			HttpServletRequest request) throws Exception {
		for (ViewResolver viewResolver : this.viewResolvers) {
			View view = viewResolver.resolveViewName(viewName, locale);
			if (view != null) {
				return view;
			}
		}
		return null;
	}

我们再看ViewResoler的resolveViewName方法。InternalResourceViewResolver的祖先类AbstractCachingViewResolver中有resolveViewName方法。

    public View resolveViewName(String viewName, Locale locale) throws Exception {  
            if (!isCache()) {  
                return createView(viewName, locale);  
            }  
            else {  
                Object cacheKey = getCacheKey(viewName, locale);  
                synchronized (this.viewCache) {  
                    View view = this.viewCache.get(cacheKey);  
                    if (view == null) {  
                        // Ask the subclass to create the View object.  
                        view = createView(viewName, locale);  
                        this.viewCache.put(cacheKey, view);  
                        if (logger.isTraceEnabled()) {  
                            logger.trace("Cached view [" + cacheKey + "]");  
                        }  
                    }  
                    return view;  
                }  
            }  
        }  

AbstractCachingViewResolver中的resolveViewName方法,该方法首先会判断有没有缓存,要是有缓存,它会先去缓存中通过viewName查找是否有View对象的存在,要是没有,它会通过viewName创建一个新的View对象,并将View对象存入缓存中,这样再次遇到同样的视图名的时候就可以直接在缓存中取出View对象了

AbstractCachingViewResolver中的createView方法

    protected View createView(String viewName, Locale locale) throws Exception {  
        return loadView(viewName, locale);  
    }  
AbstractCachingViewResolver中的loadView方法是一个抽象方法,它通过AbstractCachingViewResolver的子类UrlBasedViewResolver方法实现

UrlBasedViewResolver中的loadView方法

    protected View loadView(String viewName, Locale locale) throws Exception {  
        AbstractUrlBasedView view = buildView(viewName);  
        View result = (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName);  
        return (view.checkResource(locale) ? result : null);  
    }  

UrlBasedViewResolver中的buildView 方法

我觉得buildView方法是创建View对象最核心的方法,buildView方法会获取一个View对象,这个对象会将视图以什么格式呈现给用户,例如如果是jsp显示呈现给用户的话,那这个view对象就是JstlView,默认的是JstlView。在这个方法中我们看到了getPrefix() + viewName + getSuffix()这样一段代码,这就是对视图路径的一个拼接了,getPrefix()方法获取前缀,也就是我们在配置文件中配置的   的value中的值了,getSuffix()方法就是获取后缀值了,也就是我们在配置文件中配置的   的value中的值。这样就将将视图的物理路径找到了,并赋值到View的URL属性中去。

    protected AbstractUrlBasedView buildView(String viewName) throws Exception {  
            AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());  
            view.setUrl(getPrefix() + viewName + getSuffix());  
            String contentType = getContentType();  
            if (contentType != null) {  
                view.setContentType(contentType);  
            }  
            view.setRequestContextAttribute(getRequestContextAttribute());  
            view.setAttributesMap(getAttributesMap());  
            return view;  
        }  

2.4 .spring 组件之 HandlerExceptionResolver

在SpringMVC 中有一个专门的角色 对异常情况进行处理,在SpringMVC 中就是HandlerExceptionResolver。具体来说,此组件的作用就是根据异常设置ModelAndView,之后再交给render方法进行渲染。render只负责 将ModelAndView渲染成页面,

通过doDispatcher的分析可以知道HandlerExceptionResolver只是用于解析对请求做处理的过程中产生的异常,而渲染环节产生的异常不归它管。它是在render之前工作的,先解析出ModeAndView之后render才去渲染,当然它就不能处理render过程中的异常了。HandlerExceptionResolver接口定义如下:

public interface HandlerExceptionResolver {
	ModelAndView resolveException(
			HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);

}

你可能感兴趣的:(SpringMVC)