SpringMVC 初始化

1、HttpServletBean

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware

HttpServletBean 继承了HttpServlet,所以它只是在功能上对 HttpServlet 进行了一些扩展。

init() 方法,覆盖了她的祖先类GenericServlet 中定义的空实现,并且这个方法会在容器初始化每个servlet的时候调用一次。

HttpServletBean 是在HttpServlet 的基础上提供了把 servlet 配置中相关的一些属性、参数设置到成员变量上的功能,这么做的好处是可以很方便的通过 getter/setter 方法获取参数值,而不是通过一个通用的 Map 去获取。

/**
 * Map config parameters onto bean properties of this servlet, and
 * invoke subclass initialization.
 * @throws ServletException if bean properties are invalid (or required
 * properties are missing), or if subclass initialization fails.
 */
@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, 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");
   }
}

2、FrameworkServlet

为什么是到这个类的 initServletBean() 方法呢?
因为根据继承关系,DispatcherServlet 继承 FrameworkServlet,DispatcherServlet是在配置文件中配置的。貌似不对,initServletBean() 方法有注释,Let subclasses do whatever initialization they like。

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware

/**
 * Overridden method of {@link HttpServletBean}, invoked after any bean properties
 * have been set. Creates this servlet's WebApplicationContext.
 */
@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");
   }
}

protected WebApplicationContext initWebApplicationContext()

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent)

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac)

public String getNamespace() {
return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX);
}

然后回到 FrameworkServlet 的 initWebApplicationContext() 方法,继续向下执行
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);
}

3、DispatcherServlet

public class DispatcherServlet extends FrameworkServlet
这个实现调用初始化策略

/**
 * This implementation calls {@link #initStrategies}.
 */
@Override
protected void onRefresh(ApplicationContext context) {
   initStrategies(context);
}

/**
 * Initialize the strategy objects that this servlet uses.
 * 

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); }

initHandlerMappings 默认会先探测 ApplicationContext 对象中已经设置好的任何HandlerMapping对象,如果有就使用定义好的,如果没有则调用方法getDefaultStrategies,使用默认配置。DispathcerServlet.properties 文件中默认的 BeanNameUrlHandlerMapping 和 DefaultAnnotationHandlerMapping 两个HandlerMapping 对象。

/**
 * Initialize the HandlerMappings used by this class.
 * 

If no HandlerMapping beans are defined in the BeanFactory for this namespace, * we default to BeanNameUrlHandlerMapping. */ private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList(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"); } } }

protected List getDefaultStrategies(ApplicationContext context, Class strategyInterface)

protected Object createDefaultStrategy(ApplicationContext context, Class clazz) {
return context.getAutowireCapableBeanFactory().createBean(clazz);
}

4、AbstractAutowireCapableBeanFactory

public  T createBean(Class beanClass) throws BeansException {
   // Use prototype bean definition, to avoid registering bean as dependent bean.
   RootBeanDefinition bd = new RootBeanDefinition(beanClass);
   bd.setScope(SCOPE_PROTOTYPE);
   bd.allowCaching = ClassUtils.isCacheSafe(beanClass, getBeanClassLoader());
   return (T) createBean(beanClass.getName(), bd, null);
}

看下 DefaultAnnotationHandlerMapping 的层次关系,BeanNameUrlHandlerMapping 类层次关系是一样的。

public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping
public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
        implements HandlerMapping, Ordered
public abstract class WebApplicationObjectSupport extends ApplicationObjectSupport implements ServletContextAware
public abstract class ApplicationObjectSupport implements ApplicationContextAware

5、ApplicationObjectSupport

public abstract class ApplicationObjectSupport implements ApplicationContextAware
抽象类 ApplicationObjectSupport 实现了 ApplicationContextAware 接口,spring容器的后置处理器会调用 setApplicationContext 方法。

DefaultAnnotationHandlerMapping 实例化,父类同样实例化。

public final void setApplicationContext(ApplicationContext context) throws BeansException {
   if (context == null && !isContextRequired()) {
      // Reset internal context state.
      this.applicationContext = null;
      this.messageSourceAccessor = null;
   }
   else if (this.applicationContext == null) {
      // Initialize with passed-in context.
      if (!requiredContextClass().isInstance(context)) {
         throw new ApplicationContextException(
               "Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
      }
      this.applicationContext = context;
      this.messageSourceAccessor = new MessageSourceAccessor(context);
      initApplicationContext(context);
   }
   else {
      // Ignore reinitialization if same context passed in.
      if (this.applicationContext != context) {
         throw new ApplicationContextException(
               "Cannot reinitialize with different application context: current one is [" +
               this.applicationContext + "], passed-in one is [" + context + "]");
      }
   }
}

initApplicationContext(context); 会先执行子类 WebApplicationObjectSupport 的 initApplicationContext 方法,然后执行自己的 initApplicationContext 方法,ApplicationObjectSupport 本身的该方法没有任何处理,只是调用了一个空的 initApplicationContext 方法,这个无参的重载方法被当作一个钩子供子类方法来实现。

6、AbstractDetectingUrlHandlerMapping

spring 默认的两个处理器映射类都继承自 AbstractDetectingUrlHandlerMapping,并且类初始化将被执行的 initApplicationContext 方法也在这个类中得到实现。

public void initApplicationContext() throws ApplicationContextException {
   super.initApplicationContext();
   detectHandlers();
}

先执行超类的 initApplicationContext 方法,这个超类的方法完成的任务就是对定义好的累计恶气改装并放入到 adaptedInterceptors 数组中供以后使用。
detectHandlers(); 请求与处理器映射的关键方法。

7、WebApplicationObjectSupport

protected void initApplicationContext(ApplicationContext context) {
    super.initApplicationContext(context);
    if (this.servletContext == null && context instanceof WebApplicationContext) {
        this.servletContext = ((WebApplicationContext)context).getServletContext();
        if (this.servletContext != null) {
            this.initServletContext(this.servletContext);
        }
    }
}

8、回到 AbstractDetectingUrlHandlerMapping

/**
 * Register all handlers found in the current ApplicationContext.
 * 

The actual URL determination for a handler is up to the concrete * {@link #determineUrlsForHandler(String)} implementation. A bean for * which no such URLs could be determined is simply not considered a handler. * @throws org.springframework.beans.BeansException if the handler couldn't be registered * @see #determineUrlsForHandler(String) */ 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 that we can determine URLs for. for (String beanName : beanNames) { String[] urls = determineUrlsForHandler(beanName); if (!ObjectUtils.isEmpty(urls)) { // URL paths found: Let's consider it a handler. // 注册处理器,即添加一些映射关系到 handlerMap中,LinkedHashMap registerHandler(urls, beanName); } else { if (logger.isDebugEnabled()) { logger.debug("Rejected bean name '" + beanName + "': no URL paths identified"); } } } }

9、AbstractUrlHandlerMapping

protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
   Assert.notNull(urlPaths, "URL path array must not be null");
   for (String urlPath : urlPaths) {
      registerHandler(urlPath, beanName);
   }
}

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
   Assert.notNull(urlPath, "URL path must not be null");
   Assert.notNull(handler, "Handler object must not be null");
   Object resolvedHandler = handler;

   // Eagerly resolve handler if referencing singleton via name.
   if (!this.lazyInitHandlers && handler instanceof String) {
      String handlerName = (String) handler;
      if (getApplicationContext().isSingleton(handlerName)) {
         resolvedHandler = getApplicationContext().getBean(handlerName);
      }
   }

   Object mappedHandler = this.handlerMap.get(urlPath);
   if (mappedHandler != null) {
      if (mappedHandler != resolvedHandler) {
         throw new IllegalStateException(
               "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
               "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
      }
   }
   else {
      if (urlPath.equals("/")) {
         if (logger.isInfoEnabled()) {
            logger.info("Root mapping to " + getHandlerDescription(handler));
         }
         setRootHandler(resolvedHandler);
      }
      else if (urlPath.equals("/*")) {
         if (logger.isInfoEnabled()) {
            logger.info("Default mapping to " + getHandlerDescription(handler));
         }
         setDefaultHandler(resolvedHandler);
      }
      else {
         this.handlerMap.put(urlPath, resolvedHandler);
         if (logger.isInfoEnabled()) {
            logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
         }
      }
   }
}

在容器初始化时会建立所有 url 和 controller 的对应关系,保存到 Map 中。Tomcat启动时会通知spring初始化容器(加载bean定义信息和初始化所有单例bean),然后springmvc会遍历容器中的bean,获取每一个controller‘中的所有方法访问的url,然后将url和controller保存到一个Map中。
1、ApplicationContext 初始化时建立所有url和controller类的对应管理(用Map保存)
2、根据请求url找到对应的controller,并从controller中找到处理请求的方法
3、request参数绑定到方法的形参,执行方法处理结果,返回结果视图

但在某些特殊情况下,bean需要实现某个功能,但该功能必须借助于spring容器才能实现,此时就必须让该bean获取它所在的spring容器,可以让该bean实现ApplicationContextAwire接口。

必须在spring配置文件中指定该类,@Component

spring容器会检测容器中的所有bean,如果发现某个bean实现了ApplicationContextAwire接口,spring容器会在创建该bean之后,自动调用该bean的setApplicationContextAware() 方法,调用该方法时,会将容器本身作为参数传递给该方法,该方法的实现部分将spring传入的参数(容器本身)赋给对象的实例变量applicationContext,因此接下来可以通过该applicationContext实例变量访问容器本身。

注解 @Component
为什么ApplicationObjectSupport 没有@Component注解,而自定义的SpringContextUtis 却必须有注解?
因为DefaultAnnotationHandlerMapping 实例化,父类同样实例化。

10、DefaultAnnotationHandlerMapping

determineUrlsForHandler 方法,查看定义的 RequestMapping 注解,然后把符合要求的urls返回。

你可能感兴趣的:(SpringMVC 初始化)