zoukankan      html  css  js  c++  java
  • Spring(三) Spring IOC 初体验

    Web IOC 容器初体验

    我们还是从大家最熟悉的 DispatcherServlet 开始,我们最先想到的还是 DispatcherServlet 的 init()
    方法。我们发现在 DispatherServlet 中并没有找到 init()方法。但是经过探索,往上追索在其父类
    HttpServletBean 中找到了我们想要的 init()方法,如下:
     
    /**
    * 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.
      PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(),
      this.requiredProperties);
      if (!pvs.isEmpty()) {
        try {
          //定位资源
          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) {
          if (logger.isErrorEnabled()) {
            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");
      }
    }
    在 init()方法中,真正完成初始化容器动作的逻辑其实在 initServletBean()方法中,我们继续跟进
    initServletBean()中的代码在 FrameworkServlet 类中:
    @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");
      }
    }
    在上面的代码中终于看到了我们似曾相识的代码 initWebAppplicationContext(),继续跟进:
     
    从上面的代码中可以看出,,在 configAndRefreshWebApplicationContext()方法中,调用 refresh()方
    法,这个是真正启动 IOC 容器的入口,后面会详细介绍。IOC 容器初始化以后,最后调用了
    DispatcherServlet 的 onRefresh()方法,在 onRefresh()方法中又是直接调用 initStrategies()方法初始
    化 SpringMVC 的九大组件: 
    @Override
    protected 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);
    //handlerMapping
    initHandlerMappings(context);
    //初始化参数适配器
    initHandlerAdapters(context);
    //初始化异常拦截器
    initHandlerExceptionResolvers(context);
    //初始化视图预处理器
    initRequestToViewNameTranslator(context);
    //初始化视图转换器
    initViewResolvers(context);
    //
    initFlashMapManager(context);
    }
     
    /**
    * 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.
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(),
    this.requiredProperties);
    if (!pvs.isEmpty()) {
    try {
    //定位资源
    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) {
    if (logger.isErrorEnabled()) {
    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
    }
    throw ex;
    }
    }
    // Let subclasses do whatever initialization they like.
     
     
     

    IOC 容器初始化小结

    现在通过上面的代码,总结一下 IOC 容器初始化的基本步骤:
    1、初始化的入口在容器实现中的 refresh()调用来完成。
    2、对 Bean 定义载入 IOC 容器使用的方法是 loadBeanDefinition(),
    其中的大致过程如下:通过 ResourceLoader 来完成资源文件位置的定位,DefaultResourceLoader
    是默认的实现,同时上下文本身就给出了 ResourceLoader 的实现,可以从类路径,文件系统,URL 等
    方式来定为资源位置。如果是 XmlBeanFactory 作为 IOC 容器,那么需要为它指定 Bean 定义的资源,
    也 就 是 说 Bean 定 义 文 件 时 通 过 抽 象 成 Resource 来 被 IOC 容 器 处 理 的 , 容 器 通 过
    BeanDefinitionReader 来 完 成 定 义 信 息 的 解 析 和 Bean 信 息 的 注 册 , 往 往 使 用 的 是
    XmlBeanDefinitionReader 来 解 析 Bean 的 XML 定 义 文 件 - 实 际 的 处 理 过 程 是 委 托 给
    BeanDefinitionParserDelegate 来完成的,从而得到 bean 的定义信息,这些信息在 Spring 中使用
    BeanDefinition对象来表示-这个名字可以让我们想到loadBeanDefinition(),registerBeanDefinition()
    这些相关方法。它们都是为处理 BeanDefinitin 服务的,容器解析得到 BeanDefinition 以后,需要把
    它在 IOC 容器中注册,这由 IOC 实现 BeanDefinitionRegistry 接口来实现。注册过程就是在 IOC 容器
    内部维护的一个 HashMap 来保存得到的 BeanDefinition 的过程。这个 HashMap 是 IOC 容器持有
    Bean 信息的场所,以后对 Bean 的操作都是围绕这个 HashMap 来实现的。
    然后我们就可以通过 BeanFactory 和 ApplicationContext 来享受到 Spring IOC 的服务了,在使用 IOC
    容器的时候,我们注意到除了少量粘合代码,绝大多数以正确 IOC 风格编写的应用程序代码完全不用关
    心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已
    知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。Spring
    本身提供了对声明式载入web应用程序用法的应用程序上下文,并将其存储在ServletContext中的框架
    实现。
    以下是容器初始化全过程的时序图: 
  • 相关阅读:
    js Excel导出
    计算机原理
    Sql Server 连接池
    转/ C# 托管资源和非托管资源
    求两个时间相差多少时间
    计算机基础
    MyEclipse CI 2018.9.0正式发布(附下载)
    DevExpress v18.1新版亮点——ASP.NET Bootstrap篇(二)
    DevExpress v18.1新版亮点——ASP.NET Bootstrap篇(一)
    DevExpress v18.1新版亮点——ASP.NET篇(四)
  • 原文地址:https://www.cnblogs.com/flgb/p/12545837.html
Copyright © 2011-2022 走看看