重识Spring IoC 容器

766 阅读5分钟

重识 IoC

IoC是什么

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递(注入)给它。 ------维基百科

这是维基百科给出的解释。简洁明了,在Spring中也是通过依赖注入和依赖查找来完成Bean的初始化相关的工作的。通常IoC也会被叫做 "Hollywood Principle:Don't call us,we'll call you".就是说,你所需要的资源会直接给你,你不需要知道这些资源是怎么来的。IoC的主要目的有以下几点:

  • To decouple the execution of a task from implementation (实现跟执行的任务之间要解耦)
  • To focus a module on the task it is designed for. (关注你所设计的,不需要关注实现)
  • To free modules from assumptions about how other systems do what they do and instead rely on contracts.(使用契约的方式把模块从猜测其他系统的实现方式与具体做法中解脱出来)
  • To prevent side effects when replacing a module.(模块替换的时候,减少副作用)

Ioc容器的实现

举几个例子说明下:

  • SPI

    通过 ServiceLoader.load(interface)将META-INF/services 下面定义的Bean自动加载进来

    • Servlet

      doPost(HttpServletRequest request, HttpServletResponse response) ,这里的request和response就是Tomcat容器注入进来的

  • JNDI

    熟知的数据库连接,开发中也是无需知道后台数据库是什么,只需要根据文件目录名lookup,就能够把对应的数据源对象加载进来。

Spring IoC

IoC 两种策略:依赖查找、依赖注入

  • 依赖查找(Dependency Lookup)

    • 根据Bean名称查找

      • getBean()

        beanFactory.getBean("user");
        

        这个源码就先不分析了,会在后面的文章中讲述getBean的整个流程。

      • objectFactory

        ObjectFactory<User> objectFactory = (ObjectFactory<User>)beanFactory.getBean("objectFactory");
        User user = objectFactory.getObject();
        
        <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
            <property name="targetBeanName" value="user"/>
        </bean>
        

        使用 objectFactory 获取 bean 的方式,其实是实现了 FactoryBean 的接口。这里拿到的 objectFactory 对象相当于是获取 bean 的代理,再通过调用 objectFactory.getObject(),才会返回已经实例化的 bean ,在此方法没有调用之前,bean 实际上是没有被创建的。当你在使用构造器注入的时候,构造器需要某个Bean,但是这个Bean的加载时机是不确定的,这里用ObjectFactory或者ObjectProvider(extends ObjectFactory) 会更好一点。属于延迟依赖查找,区别于 @Lazy 的延迟依赖注入。

        #AbstractFactoryBean.java
          
        public final T getObject() throws Exception {
        		if (isSingleton()) {
        			return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
        		}
        		else {
        			return createInstance();
        		}
        	}			
        
        #ObjectFactoryCreatingFactoryBean.java
          
        @Override
        protected ObjectFactory<Object> createInstance() {
          BeanFactory beanFactory = getBeanFactory();
          Assert.state(beanFactory != null, "No BeanFactory available");
          Assert.state(this.targetBeanName != null, "No target bean name specified");
          return new TargetBeanObjectFactory(beanFactory, this.targetBeanName);
        }
        
        @Override
        public Object getObject() throws BeansException {
          return this.beanFactory.getBean(this.targetBeanName);
        }
        
    • 根据Bean类型查找

      • 单个Bean对象

        beanFactory.getBean(User.class);
        

        后面讲 Bean 生命周期的时候再分析

      • 集合Bean对象

        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
        }
        

        我们这里是使用的 ClassPathXmlApplicationContext 加载 xml 的方式获取 bean 对象。ClassPathXmlApplicationContext 实现了 ListableBeanFactory , 但是 ClassPathXmlApplicationContext#getBeansOfType 方法内部是调用了 DefaultListableBeanFactory#getBeansOfType

        #DefaultListableBeanFactory
          
        public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
              throws BeansException {
           String[] beanNames = getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
           Map<String, T> result = new LinkedHashMap<>(beanNames.length);
           for (String beanName : beanNames) {
              try {
                 Object beanInstance = getBean(beanName);
                 if (!(beanInstance instanceof NullBean)) {
                    result.put(beanName, (T) beanInstance);
                 }
              }
              catch (BeanCreationException ex) {
                	....
                 }
                 throw ex;
              }
           }
           return result;
        }
        
      • 根据Java注解查

        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> users = (Map) listableBeanFactory.getBeansWithAnnotation(Super.class);
        }
        

        Spring 官方只提供了根据注解获取集合对象的方法,没有提供获取单个Bean的方法。

  • 依赖注入(Dependency Injection)

    • 构造器注入
    • Setter注入

    自动配置:autowire="byType"

    手动配置:<property name="" ref=""/>

  • 依赖来源

    • 自定义Bean

      自己用xml配置或注解配置的bean

    • 容器内建Bean对象

      非自定义的Bean , Spring容器初始化的Bean(Environment等)

    • 容器内建依赖

      不属于Spring Bean ,不可通过依赖查找Bean(getBean)的方式获取,如BeanFactory,但是可通过依赖注入的方式获取。主要是因为,内建依赖,是通过 AutowireCapableBeanFactory 中的 resolveDependency 方法来注册的,保存在 DefaultListableBeanFactory 属性 resolvableDependencies。getBean() 不会去这个集合中查找,只会去查找 BeanDefinitions 和 Singleton Objects。@AutoWired的时候会去 resolvableDependencies 集合里面查找。

  • Spring IoC 容器到底是什么

    The org.springframework.beans and org.springframework.context packages are the basis for Spring Framework’s IoC container. The BeanFactory interface provides an advanced configuration mechanism capable of managing any type of object. ApplicationContext is a sub-interface of BeanFactory. It adds:

    • Easier integration with Spring’s AOP features
    • Message resource handling (for use in internationalization)
    • Event publication
    • Application-layer specific contexts such as the WebApplicationContext for use in web applications.

    In short, the BeanFactory provides the configuration framework and basic functionality, and the ApplicationContext adds more enterprise-specific functionality. The ApplicationContext is a complete superset of the BeanFactory and is used exclusively in this chapter in descriptions of Spring’s IoC container. For more information on using the BeanFactory instead of the ApplicationContext, see The BeanFactory.

    上面这段英文是Spring 文档上面的一段。简单来说,就是 ApplicationContext 继承了 BeanFactory , 除了 BeanFactory的特性之外,还有一些 App 的支持、消息资源的处理(用于国际化)、事件的支持、应用级别的上下文像 WebApplicationContext. BeanFactory 提供了基本的 IoC 容器的功能,ApplicationContext 是一个超集,提供了更多的企业级的功能。

AbstractApplicationContext#prepareBeanFactory
.....
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
.....

以上代码表示 register 可依赖注入的 Bean , 无法依赖查找。指定了 BeanFactory 类型的对象是 ApplicationContext#getBeanFactory()方法的内容。因为注入类型为 BeanFactory.class 是 beanFactory 也就是 通过ApplicationContext#getBeanFactory()获得的 DefaultListableBeanFactory。

#AbstractRefreshableApplicationContext.java
  
/** Bean factory for this context. */
@Nullable
private DefaultListableBeanFactory beanFactory;
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
   synchronized (this.beanFactoryMonitor) {
      if (this.beanFactory == null) {
         throw new IllegalStateException("BeanFactory not initialized or already closed - " +
               "call 'refresh' before accessing beans via the ApplicationContext");
      }
      return this.beanFactory;
   }
}

大多数情况下,我们拿到的 BeanFactory 是 DefaultListableBeanFactory 。所以真正的 IoC 容器是 BeanFactory 的实现类,ApplicationContext 采用委派的模式,将各种方法都委派给 DefaultListableBeanFactory 去实现的。ApplicationContext 区分 XML 和注解,BeanFactory 通常只管理 BeanDefinition 和其 Bean 实例

  • Spring 作为 IoC容器的优势

    • 典型的IoC管理,依赖查找和依赖注入
    • AOP
    • 事务抽象
    • Event机制
    • SPI 扩展
    • 三方整合
    • 更好的面相对象

感谢:time.geekbang.org/course/intr…