Last updated: November 15, 2020 8:44 PM

Spring IoC 依赖查找

初始准备工作

创建用户类

创建用户类作为示例

@Data
public class User {
    private Long id;
    private String name;
}

创建xml配置文件

src/main/resources/META-INF目录下创建dependency-lookup-context.xml配置文件,并配置属性:

<bean id="user" class="ioc.overview.domain.User">
    <property name="id" value="1"/>
    <property name="name" value="John"/>
</bean>

代码配置xml配置文件,并启动Spring应用上下文

// 配置 XML 配置文件
// 启动 Spring 应用上下文
BeanFactory beanFactory =
    new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");

根据Bean 名称查找

实时查找

启动Spring应用上下文后,直接通过Bean名称查找就可以查找到相应的Bean:

User user = (User) beanFactory.getBean("user");

这种方式是实时查找,整理代码可得:

public static void main(String[] args) {
    // 配置 XML 配置文件
    // 启动 Spring 应用上下文
    BeanFactory beanFactory =
        new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
    lookupInRealTime(beanFactory);
}

private static void lookupInRealTime(BeanFactory beanFactory) {
    User user = (User) beanFactory.getBean("user");
    System.out.println("实时查找:" + user);
}

运行得到的结果为:

实时查找:User{id=1, name='John'}

延迟查找

org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBeanorg.springframework.beans.factory.ObjectFactory 的一个具体实现。

在xml配置文件中,增加其配置,并在其属性中增加其所关联的bean(在这里,我们直接关联上面的 bean id)作为targetBeanName

<bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
    <property name="targetBeanName" value="user"/>
</bean>

通过objectFactory这个bean,就可以获得user这个bean,这种方式是延迟查找

private static void lookupInLazy(BeanFactory beanFactory) {
    // 必须进行通过强转来适配
    ObjectFactory<User> objectFactory = (ObjectFactory<User>) beanFactory.getBean("objectFactory");
    User user = objectFactory.getObject();
    System.out.println("延迟查找:" + user);
}

运行得到的结果为:

延迟查找:User{id=1, name='John'}

根据Bean 类型查找

根据类型查找的原理与根据名称查找类似。直接使用Class Type进行查找即可。

单个Bean 对象

// 按照类型查找
private static void lookupByType(BeanFactory beanFactory) {
    User user = beanFactory.getBean(User.class);
    System.out.println("实时查找:" + user);
}

集合Bean 对象

需要使用org.springframework.beans.factory.ListableBeanFactory进行类型转化。

// 按照类型查找结合对象
private static void lookupCollectionByType(BeanFactory beanFactory) {
    if (beanFactory instanceof ListableBeanFactory) {
        ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
        Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
        System.out.println("查找到的所有的 User 集合对象:" + users);
    }
}

运行结果为:

查找到的所有的 User 集合对象:{user=User{id=1, name='John'}}

根据Bean 名称+ 类型查找

根据Java 注解查找

首先准备一个SuperUser,增加一个字段,同时为了区分SuperUserUser, 需要定义一个@Super注解来进行区分。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Super {}

使用这个注解,进行区分,User没有@Super注解,而SuperUser@Super注解:

@Data @Super
public class SuperUser extends User {
    private String address;
}

在xml文件中,配置SuperUser的bean:

<!-- 普通 beanDefinition GenericBeanDefinition -->
<!-- 合并后 GenericBeanDefinition 变成 RootBeanDefinition,并且覆盖 parent 相关配置-->
<!-- primary = true , 增加了一个 address 属性 -->
<bean id="superUser" class="ioc.overview.domain.SuperUser" parent="user" primary="true">
    <property name="address" value="Toronto"/>
</bean>

如果不使用primary="true",那么Spring会抛出错误,因为会查找到两个bean:UserSuperUser

单个Bean 对象

查找使用了@Super这个注解的对象:

private static void lookupByAnnotationType(BeanFactory beanFactory) {
    if (beanFactory instanceof ListableBeanFactory) {
        ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
        Map<String, User> users = (Map) listableBeanFactory.getBeansWithAnnotation(Super.class);
        System.out.println("查找标注 @Super 所有的 User 集合对象:" + users);
    }
}

集合Bean 对象

与根据Bean名称查找类似。

Spring IoC 依赖注入

准备工作

创建Repository

public class UserRepository {
    private Collection<User> users; // 自定义 Bean
    public Collection<User> getUsers() {
        return users;
    }

    public void setUsers(Collection<User> users) {
        this.users = users;
    }
}

创建xml配置文件

创建src/main/resources/META-INF/dependency-injection-context.xml,可以通过导入复用之前的xml配置文件,并通过手动配置bean list(注意,需要引入http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd才可以使用util:list

<!-- 通过导入复用 dependency-lookup-context.xml -->
<import resource="dependency-lookup-context.xml"/>

<bean id="userRepository" class="ioc.overview.repository.UserRepository">
    <!-- 手动配置 --> 
    <property name="users">
        <util:list>
            <ref bean="superUser" />
            <ref bean="user" />
        </util:list>
    </property>
</bean>

使用上述方法,输出为 SuperUser 和 User。

使用autowire进行自动配置,推荐
<bean id="userRepository" class="ioc.overview.repository.UserRepository" autowire="byType"></bean>

使用autowire进行自动配置,其Bean出现的顺序,是按照bean的定义顺序来进行执行的。使用上述方法,输出必定为 User 和 SuperUser , 因为我们先定义了User, 然后定义了SuperUser。

代码配置xml配置文件,并启动Spring应用上下文

// 配置 XML 配置文件
// 启动 Spring 应用上下文
BeanFactory beanFactory =
    new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");

根据Bean 名称/类型 注入

示例代码:

UserRepository userRepository = applicationContext.getBean("userRepository", UserRepository.class);
System.out.println(userRepository.getUsers());

注入容器內建Bean 对象

注入非Bean 对象

增加內建非 Bean 对象

在Repository中增加內建非 Bean 对象

private BeanFactory beanFactory; // 內建非 Bean 对象(依赖)
public BeanFactory getBeanFactory() {
       return beanFactory;
   }

使用Repository中的get方法,userRepository.getBeanFactory() 获得的结果,并不是Spring 应用上下文取得的beanFactory

BeanFactory beanFactory =
    new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");  
UserRepository userRepository = applicationContext.getBean("userRepository", UserRepository.class);
System.out.println(userFactory.getBeanFactory() == beanFactory); // 输出结果为false

上述代码的输出结果为false

实际上,userFactory.getBeanFactory()的输出结果为:

org.springframework.beans.factory.support.DefaultListableBeanFactory@42e99e4a: defining beans [user,superUser,objectFactory,userRepository]; root of factory hierarchy

获得了4个bean: user,superUser,objectFactory,userRepository

验证內建对象是非 Bean 对象

但是,我们在UserRepository中增加的beanFactory并不是一个Bean对象,而是一个内建对象。

如果通过

System.out.println(beanFactory.getBean(BeanFactory.class));

查看,则会抛出错误:

No qualifying bean of type 'org.springframework.beans.factory.BeanFactory' available

注入类型

实时注入

以上的代码均为实时注入。

延迟注入

在Repository中增加Field:

private ObjectFactory<User> objectFactory;

public ObjectFactory<User> getObjectFactory() {
    return objectFactory;
}

public void setObjectFactory(ObjectFactory<User> objectFactory) {
    this.objectFactory = objectFactory;
}

通过下述代码可以得到SuperUser对象。

ObjectFactory userFactory = userRepository.getObjectFactory();
System.out.println(userFactory.getObjext());

输出结果为:

SuperUser{address='Toronro'} User{id=1, name='John'}

如果将UserRepository中的objectFactory类型更改为ApplicationContext

private ObjectFactory<ApplicationContext> objectFactory;

则上述代码输出结果为:

org.springframework.context.support.ClassPathXmlApplicationContext@245f6ad

System.out.println(userFactory.getObject() == beanFactory)的输出结果为true。完整代码如下:

BeanFactory beanFactory =
    new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");  
UserRepository userRepository = applicationContext.getBean("userRepository", UserRepository.class);
ObjectFactory userFactory = userRepository.getObjectFactory();
System.out.println(userFactory.getObject() == beanFactory); // 结果为true

Spring IoC 依赖来源

来源于下述三个地方:

  • 自定义Bean (业务上的Bean)

    在上面的代码中,UserRepository是一个自定义bean

    // 依赖来源一:自定义 Bean
    UserRepository userRepository = applicationContext.getBean("userRepository", UserRepository.class);
  • 容器內建Bean 对象

    // 依赖来源三:容器內建 Bean
    Environment environment = applicationContext.getBean(Environment.class);
    System.out.println("获取 Environment 类型的 Bean:" + environment);
  • 容器內建依赖(非Spring Bean)

    BeanFactory则是容器内建依赖

    // 依赖来源二:依赖注入(內建依赖)
    System.out.println(userRepository.getBeanFactory());

Spring IoC 配置元信息

Bean 定义配置

基于XML 文件

基于Properties 文件

基于Java 注解

基于Java API(专题讨论)

IoC 容器配置

基于XML 文件

基于Java 注解

基于Java API (专题讨论)

I外部化属性配置

基于Java 注解

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.

Spring 应用上下文

ApplicationContext 除了IoC 容器角色,还有提供:
• 面向切面(AOP)
• 配置元信息(Configuration Metadata)
• 资源管理(Resources)
• 事件(Events)
• 国际化(i18n)
• 注解(Annotations)
• Environment 抽象(Environment Abstraction)

使用Spring IoC 容器

BeanFactory 是Spring 底层IoC 容器

采用 BeanFactory,提供了基本的IoC功能。

// 创建 BeanFactory 容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// XML 配置文件 ClassPath 路径
String location = "classpath:/META-INF/dependency-lookup-context.xml";
// 加载配置
int beanDefinitionsCount = reader.loadBeanDefinitions(location);
System.out.println("Bean 定义加载的数量:" + beanDefinitionsCount); // 输出为 3
// 依赖查找集合对象
lookupCollectionByType(beanFactory);

ApplicationContext 是具备应用特性的BeanFactory 超集

首先,通过 Java 注解的方式,定义了一个 Bean。

// 通过 Java 注解的方式,定义了一个 Bean
@Bean
public User user() {
    User user = new User();
    user.setId(1L);
    user.setName("John");
    return user;
}

然后,采用ApplicationContext :

// 创建 ApplicationContext  容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类 AnnotationApplicationContextAsIoCContainerDemo 作为配置类(Configuration Class)
applicationContext.register(AnnotationApplicationContextAsIoCContainerDemo.class);
// 启动应用上下文
applicationContext.refresh();
// 依赖查找集合对象
lookupCollectionByType(applicationContext);
// 关闭应用上下文
applicationContext.close();

Spring IoC 容器生命周期

BeanFactory 是IoC 底层容器
FactoryBean 是创建Bean 的一种方式,帮助实现复杂的初始化逻辑