1.从DispatcherServlet开始
与很多使用广泛的MVC框架一样,SpringMVC使用的是FrontController模式,所有的设计都围绕DispatcherServlet
为中心来展开的。见下图,所有请求从DispatcherServlet进入,DispatcherServlet根据配置好的映射策略确定处理的
Controller,Controller处理完成返回ModelAndView,DispatcherServlet根据配置好的视图策略确定处理的
View,由View生成具体的视图返回给请求客户端。
2.初始化
SpringMVC几个核心的配置策略包括:
*HandlerMapping:请求-处理器映射策略处理,根据请求生成具体的处理链对象
*HandlerAdapter:处理器适配器,由于最终的Handler对象不一定是一个标准接口的实现对象,参数也可能非常的灵活复杂,因此所有的对象需要一个合适的适配器适配成标准的处理接口来最终执行请求
*ViewResolver:视图映射策略,根据视图名称和请求情况,最终映射到具体的处理View,由View对象来生成具体的视图。
其他的配置策略包括MultipartResolver、LocaleResolver、ThemeResolver等等,但并不影响我们对整个SpringMVC的工作原理的理解,此处并不具体说明。
1)初始化Context
见下图DispatcherServlet的继承结构,其中,HttpServletBean主要功能是在初始化(init)时将servlet的配置参
数(init-param)转换成Servlet的属性,FrameworkServlet主要功能是与ApplicationContext的集成,因
此Context的初始化工作主要在FrameworkServlet中进行。
Context初始化的过程可以通过如下过程来描述:HttServletBean.init -->
FrameworkServlet.initServletBean -->
FrameworkServlet.initWebApplicationContext。具体的初始化过程可见如下代码:
FrameworkServlet.initWebApplicationContext
Java代码
- protected WebApplicationContext initWebApplicationContext() throws BeansException {
- WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
- WebApplicationContext wac = createWebApplicationContext(parent);
- if (!this.refreshEventReceived) {
- // Apparently not a ConfigurableApplicationContext with refresh support:
- // triggering initial onRefresh manually here.
- onRefresh(wac);
- }
- if (this.publishContext) {
- // Publish the context as a servlet context attribute.
- String attrName = getServletContextAttributeName();
- getServletContext().setAttribute(attrName, wac);
- if (logger.isDebugEnabled()) {
- logger.debug("Published WebApplicationContext of servlet ‘" + getServletName() +
- "‘ as ServletContext attribute with name [" + attrName + "]");
- }
- }
- return wac;
- }
FrameworkServlet.createWebApplicationContext
Java代码
- protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)
- throws BeansException {
- if (logger.isDebugEnabled()) {
- logger.debug("Servlet with name ‘" + getServletName() +
- "‘ will try to create custom WebApplicationContext context of class ‘" +
- getContextClass().getName() + "‘" + ", using parent context [" + parent + "]");
- }
- if (!ConfigurableWebApplicationContext.class.isAssignableFrom(getContextClass())) {
- throw new ApplicationContextException(
- "Fatal initialization error in servlet with name ‘" + getServletName() +
- "‘: custom WebApplicationContext class [" + getContextClass().getName() +
- "] is not of type ConfigurableWebApplicationContext");
- }
- ConfigurableWebApplicationContext wac =
- (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());
- wac.setParent(parent);
- wac.setServletContext(getServletContext());
- wac.setServletConfig(getServletConfig());
- wac.setNamespace(getNamespace());
- wac.setConfigLocation(getContextConfigLocation());
- wac.addApplicationListener(new SourceFilteringListener(wac, this));
- postProcessWebApplicationContext(wac);
- wac.refresh();
- return wac;
- }
2)初始化策略
具体与SpringMVC相关的策略在DispatcherServlet中初始化,DispatcherServlet的类定义被加载时,如下初始化代码段被执行:
Java代码
- 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 ‘DispatcherServlet.properties‘: " + ex.getMessage());
- }
我们可以看到,SpringMVC的策略在与DispatcherServlet同目录的Dispatcher.properties文件中配置,如下是Spring2.5的默认配置策略
Dispatcher.properties 写道
# Default implementation classes for DispatcherServlet‘s
strategy interfaces.
# Used as fallback when no matching beans are found in
the DispatcherServlet context.
# Not meant to be customized by application
developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.throwaway.ThrowawayControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
当然,我们可以变更其处理策略,通过上面部分,我们知道,FrameworkServlet实现了ApplicationListener,并在构建
WebApplicationContext后,将自身(this)向WebApplicationContext注册,因此
WebApplicationContext初始化完毕之后,将发送ContextRefreshedEvent事件,该事件实际上被
DispatcherServlet处理,处理过程如下:
FrameworkServlet.onApplicationEvent -->
DispatcherServlet.onRefresh --> DispatcherServlet.initStrategies
DispatcherServlet.initStrategies代码如下,具体处理过程可参见Spring源代码
Java代码
- protected void initStrategies(ApplicationContext context) {
- initMultipartResolver(context);
- initLocaleResolver(context);
- initThemeResolver(context);
- initHandlerMappings(context);
- initHandlerAdapters(context);
- initHandlerExceptionResolvers(context);
- initRequestToViewNameTranslator(context);
- initViewResolvers(context);
3.请求处理流程
SpringMVC的请求处理从doService-->doDispatch为入口,实际上,我们只要紧紧抓住HandlerMapping、
HandlerAdapter、ViewResolver这三个核心对象,SpringMVC的一整个运行机制看起来将非常简单,其主要处理流程包括:
1)将请求映射到具体的执行处理链,见如下代码
Java代码
- // Determine handler for the current request.
- mappedHandler = getHandler(processedRequest, false);
- if (mappedHandler == null || mappedHandler.getHandler() == null) {
- noHandlerFound(processedRequest, response);
- return;
- }
具体看一下getHandler是如何处理的
Java代码
- protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
- HandlerExecutionChain handler =
- (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
- if (handler != null) {
- if (!cache) {
- request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
- }
- return handler;
- }
- Iterator it = this.handlerMappings.iterator();
- while (it.hasNext()) {
- HandlerMapping hm = (HandlerMapping) it.next();
- if (logger.isDebugEnabled()) {
- logger.debug("Testing handler map [" + hm + "] in DispatcherServlet with name ‘" +
- getServletName() + "‘");
- }
- handler = hm.getHandler(request);
- if (handler != null) {
- if (cache) {
- request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
- }
- return handler;
- }
- }
- return null;
可以看到,仅仅是遍历每一个HandlerMapping,如果能够其能够处理,则返回执行处理链(HandleExecuteChain)
2)执行处理链的拦截器列表的preHandle方法,如果执行时返回false,表示该拦截器已处理完请求要求停止执行后续的工作,则倒序执行所有已执行过的拦截器的afterCompletion方法,并返回
Java代码
- // Apply preHandle methods of registered interceptors.
- HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
- if (interceptors != null) {
- for (int i = 0; i < interceptors.length; i++) {
- HandlerInterceptor interceptor = interceptors[i];
- if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
- return;
- }
- interceptorIndex = i;
- }
- }
3)根据处理对象获得处理器适配器(HandlerAdapter),并由处理适配器负责最终的请求处理,并返回ModelAndView(mv),关于处理器适配器的作用,见第2部分的说明
Java代码
- // Actually invoke the handler.
- HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
具体看getHandlerAdapter如何工作
Java代码
- protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
- Iterator it = this.handlerAdapters.iterator();
- while (it.hasNext()) {
- HandlerAdapter ha = (HandlerAdapter) it.next();
- if (logger.isDebugEnabled()) {
- logger.debug("Testing handler adapter [" + ha + "]");
- }
- if (ha.supports(handler)) {
- return ha;
- }
- }
- throw new ServletException("No adapter for handler [" + handler +
- "]: Does your handler implement a supported interface like Controller?");
- }
非常简单,仅仅是依次询问HandlerAdapter列表是否支持处理当前的处理器对象
4)倒序执行处理链拦截器列表的postHandle方法
Java代码
- // Apply postHandle methods of registered interceptors.
- if (interceptors != null) {
- for (int i = interceptors.length - 1; i >= 0; i--) {
- HandlerInterceptor interceptor = interceptors[i];
- interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
- }
- }
5)根据ViewResolver获取相应的View实例,并生成视图响应给客户端
Java代码
- // Did the handler return a view to render?
- if (mv != null && !mv.wasCleared()) {
- render(mv, processedRequest, response);
- }
- else {
- if (logger.isDebugEnabled()) {
- logger.debug("Null ModelAndView returned to DispatcherServlet with name ‘" +
- getServletName() + "‘: assuming HandlerAdapter completed request handling");
- }
- }
再看看render方法
Java代码
- protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- // Determine locale for request and apply it to the response.
- Locale locale = this.localeResolver.resolveLocale(request);
- response.setLocale(locale);
- View view = null;
- if (mv.isReference()) {
- // We need to resolve the view name.
- 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 {
- // No need to lookup: the ModelAndView object contains the actual View object.
- view = mv.getView();
- if (view == null) {
- throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
- "View object in servlet with name ‘" + getServletName() + "‘");
- }
- }
- // Delegate to the View object for rendering.
- if (logger.isDebugEnabled()) {
- logger.debug("Rendering view [" + view + "] in DispatcherServlet with name ‘" + getServletName() + "‘");
- }
- view.render(mv.getModelInternal(), request, response);
- }
6)倒序执行处理链拦截器列表的afterCompletion方法
Java代码
- private void triggerAfterCompletion(
- HandlerExecutionChain mappedHandler, int interceptorIndex,
- HttpServletRequest request, HttpServletResponse response, Exception ex)
- throws Exception {
- // Apply afterCompletion methods of registered interceptors.
- if (mappedHandler != null) {
- HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
- if (interceptors != null) {
- for (int i = interceptorIndex; i >= 0; i--) {
- HandlerInterceptor interceptor = interceptors[i];
- try {
- interceptor.afterCompletion(request, response, mappedHandler.getHandler(), ex);
- }
- catch (Throwable ex2) {
- logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
- }
- }
- }
- }
- }
4.请求-处理链映射(HandlerMapping)
HandlerMapping定义了请求与处理链之间的映射的策略,见如下接口。Java代码
- public interface HandlerMapping {
- String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
- HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
主要的继承类和继承结构如下
其中
*AbstractHandlerMapping:定义了HandlerMapping实现的最基础的部分内容,包括拦截器列表和默认处理对象
*AbstractUrlHandlerMapping:在AbstractHandlerMapping的基础上,定义了从URL到处理对象的映射关系管理,其主要处理过程如下
getHandlerInternal:Java代码
- protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
- String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
- if (logger.isDebugEnabled()) {
- logger.debug("Looking up handler for [" + lookupPath + "]");
- }
- Object 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 ("/".equals(lookupPath)) {
- rawHandler = getRootHandler();
- }
- if (rawHandler == null) {
- rawHandler = getDefaultHandler();
- }
- if (rawHandler != null) {
- validateHandler(rawHandler, request);
- handler = buildPathExposingHandler(rawHandler, lookupPath);
- }
- }
- return handler;
- }
lookupHandler:
Java代码
- protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
- // Direct match?
- Object handler = this.handlerMap.get(urlPath);
- if (handler != null) {
- validateHandler(handler, request);
- return buildPathExposingHandler(handler, urlPath);
- }
- // Pattern match?
- String bestPathMatch = null;
- for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
- String registeredPath = (String) it.next();
- if (getPathMatcher().match(registeredPath, urlPath) &&
- (bestPathMatch == null || bestPathMatch.length() < registeredPath.length())) {
- bestPathMatch = registeredPath;
- }
- }
- if (bestPathMatch != null) {
- handler = this.handlerMap.get(bestPathMatch);
- validateHandler(handler, request);
- String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPathMatch, urlPath);
- return buildPathExposingHandler(handler, pathWithinMapping);
- }
- // No handler found...
- return null;
- }
另外,其定义了Handler的注册方法registerHandler
*AbstractDetectingUrlHandlerMapping:AbstractDetectingUrlHandlerMapping
通过继承ApplicationObjectSupport实现了ApplicationContextAware接口,在初始化完成之后自动通过
ApplicationObjectSupport.setApplicationContext-->AbstractDetectingUrlHandlerMapping.initApplicationContext-->AbstractDetectingUrlHandlerMapping.detectHandlers
调用detectHandlers函数,该函数将注册到ApplicationContext的所有Bean对象逐一检查,由其子类实现的
determineUrlsForHandler判断每个Bean对象对应的URL,并将URL与Bean的关系通过
AbstractUrlHandlerMapping.registerHandler注册Java代码
- protected void detectHandlers() throws BeansException {
- if (logger.isDebugEnabled()) {
- logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
- }
- String[] beanNames = (this.detectHandlersInAncestorContexts ?
- BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
- getApplicationContext().getBeanNamesForType(Object.class));
- // Take any bean name or alias that begins with a slash.
- for (int i = 0; i < beanNames.length; i++) {
- String beanName = beanNames[i];
- String[] urls = determineUrlsForHandler(beanName);
- if (!ObjectUtils.isEmpty(urls)) {
- // URL paths found: Let‘s consider it a handler.
- registerHandler(urls, beanName);
- }
- else {
- if (logger.isDebugEnabled()) {
- logger.debug("Rejected bean name ‘" + beanNames[i] + "‘: no URL paths identified");
- }
- }
- }
- }
*BeanNameUrlHandlerMapping:非常简单,其实现determineUrlsForHandler函数,如果一个Bean以“/”开头,则认为是一个处理器类,并且以bean的名字作为映射的url,处理过程如下
Java代码
- protected String[] determineUrlsForHandler(String beanName) {
- List urls = new ArrayList();
- if (beanName.startsWith("/")) {
- urls.add(beanName);
- }
- String[] aliases = getApplicationContext().getAliases(beanName);
- for (int j = 0; j < aliases.length; j++) {
- if (aliases[j].startsWith("/")) {
- urls.add(aliases[j]);
- }
- }
- return StringUtils.toStringArray(urls);
- }
*DefaultAnnotationHandlerMapping:实现determineUrlsForHandler函数,检查每个Bean
对象的类或者方法有没有RequestMapping这个Annotation,如果有,则将相应配置的URL作为该Bean对应处理的URL,处理过程
如下Java代码
- protected String[] determineUrlsForHandler(String beanName) {
- ApplicationContext context = getApplicationContext();
- Class<?> handlerType = context.getType(beanName);
- RequestMapping mapping = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
- if (mapping == null && context instanceof ConfigurableApplicationContext &&
- context.containsBeanDefinition(beanName)) {
- ConfigurableApplicationContext cac = (ConfigurableApplicationContext) context;
- BeanDefinition bd = cac.getBeanFactory().getMergedBeanDefinition(beanName);
- if (bd instanceof AbstractBeanDefinition) {
- AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
- if (abd.hasBeanClass()) {
- Class<?> beanClass = abd.getBeanClass();
- mapping = AnnotationUtils.findAnnotation(beanClass, RequestMapping.class);
- }
- }
- }
- if (mapping != null) {
- // @RequestMapping found at type level
- this.cachedMappings.put(handlerType, mapping);
- Set<String> urls = new LinkedHashSet<String>();
- String[] paths = mapping.value();
- if (paths.length > 0) {
- // @RequestMapping specifies paths at type level
- for (String path : paths) {
- addUrlsForPath(urls, path);
- }
- return StringUtils.toStringArray(urls);
- }
- else {
- // actual paths specified by @RequestMapping at method level
- return determineUrlsForHandlerMethods(handlerType);
- }
- }
- else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
- // @RequestMapping to be introspected at method level
- return determineUrlsForHandlerMethods(handlerType);
- }
- else {
- return null;
- }
- }
5.处理器适配器(HandlerAdapter)
HandlerAdapter定义了处理类如何处理请求的策略,见如下接口Java代码
- public interface HandlerAdapter {
- boolean supports(Object handler);
- ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
- long getLastModified(HttpServletRequest request, Object handler);
- }
通过实现特定的策略,可以灵活地将任意对象转换成请求处理对象,主要实现包括:
1)SimpleControllerHandlerAdapter/HttpRequestHandlerAdapter/SimpleServletHandlerAdapter/ThrowawayController
非常简单,面向实现实现了特定接口的处理类的情形,仅仅做一个代理执行处理,看一下其中SimpleControllerHandlerAdapter的代码如下,其特定处理实现了Controller接口的处理类Java代码
- public class SimpleControllerHandlerAdapter implements HandlerAdapter {
- public boolean supports(Object handler) {
- return (handler instanceof Controller);
- }
- public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
- return ((Controller) handler).handleRequest(request, response);
- }
- public long getLastModified(HttpServletRequest request, Object handler) {
- if (handler instanceof LastModified) {
- return ((LastModified) handler).getLastModified(request);
- }
- return -1L;
- }
- }
2)AnnotationMethodHandlerAdapter
通过配置特定的Annotation,定义了该如何注入参数、调用哪个方法、对返回参数如何处理,主要过程如下(AnnotationMethodHandlerAdapter.invokeHandlerMethod)Java代码
- try {
- ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
- Method handlerMethod = methodResolver.resolveHandlerMethod(request);
- ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
- ServletWebRequest webRequest = new ServletWebRequest(request, response);
- ExtendedModelMap implicitModel = new ExtendedModelMap();
- Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
- ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, result, implicitModel, webRequest);
- methodInvoker.updateSessionAttributes(
- handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
- return mav;
- }
- catch (NoSuchRequestHandlingMethodException ex) {
- return handleNoSuchRequestHandlingMethod(ex, request, response);
- }
*ServletHandlerMethodResolver(AnnotationMethodHandlerAdapter内部类):该类通过请求URL、请求Method和处理类的RequestMapping定义,最终确定该使用处理类的哪个方法来处理请求
*ServletHandlerMethodInvoker(AnnotationMethodHandlerAdapter内部类):检查处理类相应处
理方法的参数以及相关的Annotation配置,确定如何转换需要的参数传入调用方法,并最终调用返回ModelAndView
6.视图策略(ViewResolver)
ViewResolver定义了如何确定处理视图的View对象的策略,见如下接口Java代码
- public interface ViewResolver {
- View resolveViewName(String viewName, Locale locale) throws Exception;
- }