spring mvc 源码(一)web容器创建
spring mvc是一个mvc开源框架,由于与spring是父子关系,所以无缝兼容
?
spring mvc入口:
?
<!-- spring MVC --> <servlet> <servlet-name>spring-mvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/spring-mvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
?spring mvc在web.xml中的配置如上,这是一个标准的servlet的配置,入口为:DispatcherServlet;
?
DispatcherServlet源码为:
?
public class DispatcherServlet extends FrameworkServlet {//省略实现代码...}public abstract class FrameworkServlet extends HttpServletBean {//省略实现代码...s}public abstract class HttpServletBean extends HttpServletimplements EnvironmentCapable, EnvironmentAware {//省略实现代码}
?由上面可知DispatcherServlet实现了HttpServlet接口,所以配置也使用servlet配置;
?
?
一个servlet的初始化时通过init()方法完成,那我们先从最顶层的类HttpServletBean看起,下面为HttpServletBean的init()方法的实现:
?
@Overridepublic 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, getEnvironment()));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()被FrameworkServlet重写,下面是FrameworkServlet中initServletBean()的实现:
?
@Overrideprotected 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");}} //被调用方法实现,创建spring mvc容器的实现protected WebApplicationContext initWebApplicationContext() { //获取父容器,即spring的容器WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {// A context instance was injected at construction time -> use itwac = 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, etcif (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 //设置父容器,父容器为spring的容器cwac.setParent(rootContext);}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 idwac = findWebApplicationContext();}if (wac == null) {// No context instance is defined for this servlet -> create a local onewac = 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.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;}
有上面代码可知:spring mvc上下文和spring 上下文的关系,spring mvc容器为子容器, spring的容器为父容器,所以spring mvc里在配置controller时,可以直接引用在spring中配置的service类;所以在web.xml配置中,要先配置spring的配置,然后配置spring mvc的配置,否则spring mvc的子容器无法使用父容器中的资源
?
在FrameworkServlet类中onRefresh()方法是一个空方法,并没有具体实现,而是被子类重写了,在子类DispatcherServlet中onRefresh()方法实现如下:
?
@Overrideprotected void onRefresh(ApplicationContext context) {initStrategies(context);}/** * Initialize the strategy objects that this servlet uses. * <p>May be overridden in subclasses in order to initialize further strategy objects. */protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);}
由上面的代码可知在DispatcherServlet中刷新容器的时候,做了大量的初始化工作,初始化了Resolver,HandlerMapping,Adapter
?
初始化Resolver:
?
private void initLocaleResolver(ApplicationContext context) {try { //先从上下文里查找 this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);if (logger.isDebugEnabled()) {logger.debug("Using LocaleResolver [" + this.localeResolver + "]");}}catch (NoSuchBeanDefinitionException ex) {// We need to use the default. //上下文里没有则使用默认的this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);if (logger.isDebugEnabled()) {logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +"': using default [" + this.localeResolver + "]");}}}
?关于默认的resolver:在DispatcherServlet刚开始有一段静态初始化的代码:
?
?
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());}}
?默认加载一个配置文件:与DispatcherServlet在同一个路径下DEFAULT_STRATEGIES_PATH;即DispatcherServlet.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.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolverorg.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMappingorg.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterorg.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
?配置文件中配置了默认的Resolver, HandlerMapping, HandlerAdapter,? onRefresh()方法中要初始化的组件都有一个默认的配置。
?
上面的组件初始化基本都遵从了一个主要逻辑:先从容器或者资源里找,找不到就使用配置文件中默认配置的。
?
通过上面的代码spring mvc的初始化工作就完成了,上下文容器也就建立了!
?
?