在咱们第一次学Servlet编程,学java web的时候,尚未那么多框架。咱们开发一个简单的功能要作的事情很简单,就是继承HttpServlet,根据须要重写一下doGet,doPost方法,跳转到咱们定义好的jsp页面。Servlet类编写完以后在web.xml里注册这个Servlet类。java
除此以外,没有其余了。咱们启动web服务器,在浏览器中输入地址,就能够看到浏览器上输出咱们写好的页面。为了更好的理解上面这个过程,你须要学习关于Servlet生命周期的三个阶段,就是所谓的“init-service-destroy”。web
以上的知识,我以为对于你理解SpringMVC的设计思想,已经足够了。SpringMVC固然能够称得上是一个复杂的框架,可是同时它又遵循Servlet世界里最简单的法则,那就是“init-service-destroy”。咱们要分析SpringMVC的初始化流程,其实就是分析DispatcherServlet类的init()方法,让咱们带着这种单纯的观点,打开DispatcherServlet的源码一窥究竟吧。spring
用Eclipse IDE打开DispatcherServlet类的源码,ctrl+T看一下。编程
DispatcherServlet类的初始化入口方法init()定义在HttpServletBean这个父类中,HttpServletBean类做为一个直接继承于HttpServlet类的类,覆写了HttpServlet类的init()方法,实现了本身的初始化行为。设计模式
@Override public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. try { PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment)); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // Let subclasses do whatever initialization they like. initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
这里的initServletBean()方法在HttpServletBean类中是一个没有任何实现的空方法,它的目的就是留待子类实现本身的初始化逻辑,也就是咱们常说的模板方法设计模式。SpringMVC在今生动的运用了这个模式,init()方法就是模版方法模式中的模板方法,SpringMVC真正的初始化过程,由子类FrameworkServlet中覆写的initServletBean()方法触发。浏览器
再看一下init()方法内被try,catch块包裹的代码,里面涉及到BeanWrapper,PropertyValues,ResourceEditor这些Spring内部很是底层的类。要深究具体代码实现上面的细节,须要对Spring框架源码具备至关深刻的了解。咱们这里先避繁就简,从代码效果和设计思想上面来分析这段try,catch块内的代码所作的事情:服务器
这两点,我想经过下面一个例子来讲明一下。mvc
我在web.xml中注册的DispatcherServlet配置以下:app
<!-- springMVC配置开始 --> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- springMVC配置结束 -->
能够看到,我注册了一个名为contextConfigLocation的<init-param>元素,其值为“classpath:spring/spring-servlet.xml”,这也是你们经常用来指定SpringMVC配置文件路径的方法。上面那段try,catch块包裹的代码发挥的做用,一个是将“classpath:spring/spring-servlet.xml”这段字符串转换成classpath路径下的一个资源文件,供框架初始化读取配置元素。在个人工程中是在spring文件夹下面的配置文件spring-servlet.xml。框架
另一个做用,就是将contextConfigLocation的值读取出来,而后经过setContextConfigLocation()方法设置到DispatcherServlet中,这个setContextConfigLocation()方法是在FrameworkServlet类中定义的,也就是上面继承类图中DispatcherServlet的直接父类。
咱们在setContextConfigLocation()方法上面打上一个断点,启动web工程,能够看到下面的调试结果。
HttpServletBean类的做者是大名鼎鼎的Spring之父Rod Johnson。做为POJO编程哲学的大师,他在HttpServletBean这个类的设计中,运用了依赖注入思想完成了<init-param>配置元素的读取。他抽离出HttpServletBean这个类的目的也在于此,就是“以依赖注入的方式来读取Servlet类的<init-param>配置信息”,并且这里很明显是一种setter注入。
明白了HttpServletBean类的设计思想,咱们也就知道能够如何从中获益。具体来讲,咱们继承HttpServletBean类(就像DispatcherServlet作的那样),在类中定义一个属性,为这个属性加上setter方法后,咱们就能够在<init-param>元素中为其定义值。在类被初始化后,值就会被注入进来,咱们能够直接使用它,避免了样板式的getInitParameter()方法的使用,并且还免费享有Spring中资源编辑器的功能,能够在web.xml中,经过“classpath:”直接指定类路径下的资源文件。
注意,虽然SpringMVC自己为了后面初始化上下文的方便,使用了字符串来声明和设置contextConfigLocation参数,可是将其声明为Resource类型,一样可以成功获取。鼓励读者们本身继承HttpServletBean写一个测试用的Servlet类,并设置一个参数来调试一下,这样可以帮助你更好的理解获取配置参数的过程。
上一篇文章中提到过,SpringMVC使用了Spring容器来容纳本身的配置元素,拥有本身的bean容器上下文。在SpringMVC初始化的过程当中,很是关键的一步就是要创建起这个容器上下文,而这个创建上下文的过程,发生在FrameworkServlet类中,由上面init()方法中的initServletBean()方法触发。
@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 { this.webApplicationContext = initWebApplicationContext(); 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"); } }
initFrameworkServlet()方法是一个没有任何实现的空方法,除去一些样板式的代码,那么这个initServletBean()方法所作的事情已经很是明白:
this.webApplicationContext = initWebApplicationContext();
这一句简单直白的代码,道破了FrameworkServlet这个类,在SpringMVC类体系中的设计目的,它是 用来抽离出创建 WebApplicationContext 上下文这个过程的。
initWebApplicationContext()方法,封装了创建Spring容器上下文的整个过程,方法内的逻辑以下:
以上面6点跟踪FrameworkServlet类中的代码,能够比较清晰的了解到整个容器上下文的创建过程,也就可以领会到FrameworkServlet类的设计目的,它是用来创建一个和Servlet关联的Spring容器上下文,并将其注册到ServletContext中的。跳脱开SpringMVC体系,咱们也能经过继承FrameworkServlet类,获得与Spring容器整合的好处,FrameworkServlet和HttpServletBean同样,是一个能够独立使用的类。整个SpringMVC设计中,到处体现开闭原则,这里显然也是其中一点。
初始化流程在FrameworkServlet类中流转,创建了上下文后,经过onRefresh(ApplicationContext context)方法的回调,进入到DispatcherServlet类中。
@Override protected void onRefresh(ApplicationContext context) { initStrategies(context); }
DispatcherServlet类覆写了父类FrameworkServlet中的onRefresh(ApplicationContext context)方法,提供了SpringMVC各类编程元素的初始化。固然这些编程元素,都是做为容器上下文中一个个bean而存在的。具体的初始化策略,在initStrategies()方法中封装。
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
咱们以其中initHandlerMappings(context)方法为例,分析一下这些SpringMVC编程元素的初始化策略,其余的方法,都是以相似的策略初始化的。
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<HandlerMapping>(matchingBeans.values()); // We keep HandlerMappings in sorted order. OrderComparator.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. if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
detectAllHandlerMappings变量默认为true,因此在初始化HandlerMapping接口默认实现类的时候,会把上下文中全部HandlerMapping类型的Bean都注册在handlerMappings这个List变量中。若是你手工将其设置为false,那么将尝试获取名为handlerMapping的Bean,新建一个只有一个元素的List,将其赋给handlerMappings。若是通过上面的过程,handlerMappings变量仍为空,那么说明你没有在上下文中提供本身HandlerMapping类型的Bean定义。此时,SpringMVC将采用默认初始化策略来初始化handlerMappings。
点进去getDefaultStrategies看一下。
@SuppressWarnings("unchecked") protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) { String key = strategyInterface.getName(); String value = defaultStrategies.getProperty(key); if (value != null) { String[] classNames = StringUtils.commaDelimitedListToStringArray(value); List<T> strategies = new ArrayList<T>(classNames.length); for (String className : classNames) { try { Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); 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( "Error loading DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]: problem with class file or dependent class", err); } } return strategies; } else { return new LinkedList<T>(); } }
它是一个范型的方法,承担全部SpringMVC编程元素的默认初始化策略。方法的内容比较直白,就是以传递类的名称为键,从defaultStrategies这个Properties变量中获取实现类,而后反射初始化。
须要说明一下的是defaultStrategies变量的初始化,它是在DispatcherServlet的静态初始化代码块中加载的。
private static final Properties defaultStrategies; 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()); } }
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
这个DispatcherServlet.properties里面,以键值对的方式,记录了SpringMVC默认实现类,它在spring-webmvc-3.1.3.RELEASE.jar这个jar包内,在org.springframework.web.servlet包里面。
# 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.annotation.AnnotationMethodHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
至此,咱们分析完了initHandlerMappings(context)方法的执行过程,其余的初始化过程与这个方法很是相似。全部初始化方法执行完后,SpringMVC正式完成初始化,静静等待Web请求的到来。
回顾整个SpringMVC的初始化流程,咱们看到,经过HttpServletBean、FrameworkServlet、DispatcherServlet三个不一样的类层次,SpringMVC的设计者将三种不一样的职责分别抽象,运用模版方法设计模式分别固定在三个类层次中。其中HttpServletBean完成的是<init-param>配置元素的依赖注入,FrameworkServlet完成的是容器上下文的创建,DispatcherServlet完成的是SpringMVC具体编程元素的初始化策略。