购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.1 IoC容器简介

IoC容器是一个管理Bean的容器,在Spring的定义中,所有IoC容器都需要实现接口BeanFactory,它是一个顶级容器接口。为了增加对它的理解,我们首先阅读其源码,并讨论几个重要的方法,其源码如代码清单3-1所示。

代码清单3-1 BeanFactory接口源码
package org.springframework.beans.factory;
 
/**** imports ****/
public interface BeanFactory {
    // 前缀
   String FACTORY_BEAN_PREFIX = "&";
 
   // 多个getBean()方法
   Object getBean(String name) throws BeansException;
 
   <T> T getBean(String name, Class<T> requiredType) throws BeansException;
 
   <T> T getBean(Class<T> requiredType) throws BeansException;
 
   Object getBean(String name, Object... args) throws BeansException;
 
   <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
 
   // 两个获取Bean的提供器
   <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
 
   <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
 
   // 是否包含Bean
   boolean containsBean(String name);
 
   // Bean是否为单例
   boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
 
   // Bean是否为原型
   boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
 
   // 是否类型匹配
   boolean isTypeMatch(String name, ResolvableType typeToMatch) 
      throws NoSuchBeanDefinitionException;
 
   boolean isTypeMatch(String name, Class<?> typeToMatch) 
      throws NoSuchBeanDefinitionException;
 
   // 获取Bean的类型
   Class<?> getType(String name) throws NoSuchBeanDefinitionException;
 
   Class<?> getType(String name, boolean allowFactoryBeanInit) 
      throws NoSuchBeanDefinitionException;
 
   // 获取Bean的别名
   String[] getAliases(String name);
}

上述源码中加入了中文注释,通过这些中文注释就可以理解这些方法的含义了,下面我们再介绍一些重要的方法。

getBean(): 这是IoC容器最重要的方法之一,它的作用是从IoC容器中获取Bean。从多个getBean()方法中可以看到,有 按名称 (by name)获取Bean的,也有 按类型 (by type)获取Bean的,这就意味着在IoC容器中,允许我们按名称或者类型获取Bean,这对理解3.3节将讲到的Spring的 依赖注入 (dependency injection,DI)是十分重要的。

isSingleton(): 判断Bean是否在IoC容器中为单例。这里需要记住的是,在IoC容器中,Bean默认都是以单例存在的,也就是使用getBean()方法根据名称或者类型获取的对象,在默认的情况下,返回的都是同一个对象。

isPrototype(): 与isSingleton()方法是相反的,如果它返回的是true,那么当我们使用getBean()方法获取Bean的时候,IoC容器就会创建一个新的Bean返回给调用者,这些与3.7节将讨论的Bean的作用域相关。

由于BeanFactory接口定义的功能还不够强大,因此Spring在BeanFactory的基础上,还设计了一个更为高级的接口ApplicationContext,它是BeanFactory的子接口之一。在Spring的体系中,BeanFactory和ApplicationContext是最为重要的接口设计,在现实中我们使用的大部分IoC容器是ApplicationContext接口的实现类。BeanFactory和ApplicationContext的关系如图3-1所示。

图3-1 IoC容器的接口设计

在图3-1中可以看到,ApplicationContext接口通过扩展上级接口,进而扩展了BeanFactory接口,但是在BeanFactory的基础上,扩展了消息国际化接口(MessageSource)、环境可配置化接口(EnvironmentCapable)、应用事件发布接口(ApplicationEventPublisher)和资源模式解析器接口(ResourcePatternResolver),所以ApplicationContext的功能会更为强大。

Spring Boot主要通过注解来将Bean装配到IoC容器中,为了贴近Spring Boot的需要,这里不再介绍与XML相关的IoC容器,而主要介绍一个基于注解的IoC容器——AnnotationConfigApplicationContext。从这个类的名称就可以看出,它是一个基于注解的IoC容器,之所以研究它,是因为Spring Boot装配和获取Bean的方式如出一辙。

下面来看一个最为简单的例子。首先定义一个Java 简单对象 (plain ordinary Java object,POJO)文件User.java,如代码清单3-2所示。

代码清单3-2 User.java
package com.learn.chapter3.pojo;
/**** imports ****/
public class User {
 
   private Long id; // 编号
   private String userName; // 用户名
   private String note; // 备注
   
   /**setters and getters **/
}

然后定义一个Java配置文件AppConfig.java,如代码清单3-3所示。

代码清单3-3 定义Java配置文件
package com.learn.chapter3.config;
/**** imports ****/
// 标注为Java配置类
@Configuration
public class AppConfig {
   
   // @Bean表示将initUser()方法返回的对象装配到IoC容器中,该方法的属性name表示Bean的名称
   @Bean(name = "user")
   public User initUser() {
      var user = new User();
      user.setId(1L);
      user.setUserName("user_name_1");
      user.setNote("note_1");
      return user;
   }
}

这里需要注意加粗的注解。@Configuration表示这是一个Java配置类,Spring的容器会根据它来生成IoC容器,从而去装配Bean;@Bean表示将initUser()方法返回的对象装配到IoC容器中,该方法的属性name表示这个Bean的名称,如果没有配置它,则将方法名称initUser作为Bean的名称保存到IoC容器中。

做好了这些,就可以使用AnnotationConfigApplicationContext来构建自己的IoC容器了,如代码清单3-4所示。

代码清单3-4 使用AnnotationConfigApplicationContext
package com.learn.chapter3.main;
/**** imports ****/
public class IoCTest {
 
   public static void main(String[] args) {
      // 使用配置文件AppConfig.java创建IoC容器
      var ctx = new AnnotationConfigApplicationContext(AppConfig.class);
      try {
         // 通过getBean()方法获取Bean
         var user = ctx.getBean(User.class);
         System.out.println(user.getUserName());
      } finally {
         // 关闭IoC容器
         ctx.close();
      }
   }
}

上述代码将Java配置文件AppConfig.java传递给AnnotationConfigApplicationContext的构造方法,这样就能创建IoC容器了。IoC容器会根据AppConfig创建Bean,然后将Bean装配进来,这样就可以使用getBean()方法获取对应的Bean了。注意,Spring在默认的情况下,扫描到Bean后就创建Bean并将Bean装配到IoC容器中,而不是使用getBean()方法后才创建和装配Bean。运行代码后,可以打印出下面的日志:

...... 
16:11:04.110 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory –
Creating shared instance of singleton bean 'appConfig'
16:11:04.115 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory –
Creating shared instance of singleton bean 'user'
user_name_1

从日志中可以看到,配置文件中的名称为user的Bean已经被装配到IoC容器中,我们可以通过getBean()方法获取对应的Bean,并将Bean的属性信息输出出来。这个例子比较简单,注解@Bean也不是唯一装配Bean的方法,还有其他的方法可以让IoC容器装配Bean,Bean之间的依赖关系也需要进一步处理,这就是本章后面的主要内容了。 jOa5QLIjvIx0ZVet1Q8xW922CVzzmuv4Ar8G795TmN/h/NZZidamrvT8t2jpyb3h

点击中间区域
呼出菜单
上一章
目录
下一章
×