前言

这一讲我们继续来学习 IoC 的两个常用知识点:IoC 通过工厂方法创建对象、IoC 自动装载(autowire)。

IoC 通过工厂方法创建对象

之前说过 IoC 是典型的工厂模式,下面我们就来学习如何使用工厂模式来创建 bean,IoC 通过工厂模式创建 bean 有两种方式:

  • 静态工厂方法
  • 实例工厂方法

按照惯例,我们还是通过代码来带大家去学习工厂方法,先来学习静态工厂方法。

(1)创建 Car 实体类

public class Car {
    private int num;
    private String brand;
    public Car(int num, String brand) {
        super();
        this.num = num;
        this.brand = brand;
    }
}

(2)创建静态工厂类、静态工厂方法

public class StaticCarFactory {
    private static Map<Integer,Car> cars;
    static{
        cars = new HashMap<Integer,Car>();
        cars.put(1, new Car(1,"奥迪"));
        cars.put(2, new Car(2,"奥拓"));
    }
    public static Car getCar(int num){
        return cars.get(id);
    }
}

(3)在 spring.xml 中配置静态工厂

<!-- 配置静态工厂创建 car 对象 -->
<bean id="car1" class="com.southwind.entity.StaticCarFactory" factory-method="getCar">
   <constructor-arg value="1"></constructor-arg>
</bean>
  • factory-method 指向静态方法;
  • constructor-arg 的 value 属性为调用静态方法所传的参数。

(4)在测试类中直接获取 car1 对象。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Car car = (Car) applicationContext.getBean("car1");
System.out.println(car);

运行结果如下图所示。

点击这里了解《Spring 全家桶》

实例工厂方法

(1)创建实例工厂类、工厂方法

public class InstanceCarFactory {
    private Map<Integer,Car> cars;
    public InstanceCarFactory() {
        cars = new HashMap<Integer,Car>();
        cars.put(1, new Car(1,"奥迪"));
        cars.put(2, new Car(2,"奥拓"));
    }
    public Car getCar(int num){
        return cars.get(num);
    }
}

(2)在 spring.xml 中配置 bean

<!-- 配置实例工厂对象 -->
<bean id="carFactory" class="com.southwind.entity.InstanceCarFactory"></bean>
<!-- 通过实例工厂对象创建 car 对象 -->
<bean id="car2" factory-bean="carFactory" factory-method="getCar">
    <constructor-arg value="2"></constructor-arg>
</bean>

(3)在测试类中直接获取 car2 对象

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Car car2 = (Car) applicationContext.getBean("car2");
System.out.println(car2);

运行结果如下图所示。

对比两种方式的区别,静态工厂方法的方式创建 car 对象,不需要实例化工厂对象,因为静态工厂的静态方法,不需要创建对象即可调用。所以 spring.xml 只需要配置一个 Car bean,而不需要配置工厂 bean。

实例工厂方法创建 car 对象,必须先实例化工厂对象,因为调用的是非静态方法,必须通过对象调用,不能直接通过类来调用,所以 spring.xml 中需要先配置工厂 bean,再配置 Car bean。

其实这里考察的就是静态方法和非静态方法的调用。

IoC 自动装载(autowire)

我们通过前面的学习,掌握了 IoC 创建对象的方式,以及 DI 完成依赖注入的方式,通过配置 property 的 ref 属性,可将 bean 进行依赖注入。

同时 Spring 还提供了另外一种更加简便的方式:自动装载,不需要手动配置 property,IoC 容器会自动选择 bean 完成依赖注入。

自动装载有两种方式:

  • byName,通过属性名自动装载;
  • byType,通过属性对应的数据类型自动装载。

通过代码来学习。

(1)创建 Person 实体类

public class Person {
    private int id;
    private String name;
    private Car car;
}

(2)在 spring.xml 中配置 Car bean 和 Person bean,并通过自动装载进行依赖注入。

创建 person 对象时,没有在 property 中配置 car 属性,因此 IoC 容器会自动进行装载,autowire="byName" 表示通过匹配属性名的方式去装载对应的 bean,Person 实体类中有 car 属性,因此就将 id="car" 的 bean 注入到 person 中。

注意:通过 property 标签手动进行 car 的注入优先级更高,若两种方式同时配置,以 property 的配置为准。

<bean id="person" class="com.southwind.entity.Person" autowire="byName"> 
   <property name="id" value="1"></property>
   <property name="name" value="张三"></property>
</bean>
<bean id="car" class="com.southwind.entity.StaticCarFactory" factory-method="getCar">
   <constructor-arg value="2"></constructor-arg>
</bean>

(3)测试类中获取 person 对象

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);

运行结果如下图所示。

同理,byType 即通过属性的数据类型来配置。

(1)spring.xml 配置如下

<bean id="person" class="com.southwind.entity.Person" autowire="byType"> 
   <property name="id" value="1"></property>
   <property name="name" value="张三"></property>
</bean>
<bean id="car" class="com.southwind.entity.StaticCarFactory" factory-method="getCar">
   <constructor-arg value="1"></constructor-arg>
</bean>
<bean id="car2" class="com.southwind.entity.StaticCarFactory" factory-method="getCar">
   <constructor-arg value="2"></constructor-arg>
</bean>

(2)测试类中获取 person 对象

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);

运行结果如下图所示。

可以看到控制台直接打印异常信息,这是因为使用了 byType 进行自动装载,但是 spring.xml 中配置了两个 Car bean,IoC 容器不知道应该将哪一个 bean 装载到 person 对象中,所以抛出异常。使用 byType 进行自动装载时,spring.xml 中只能配置一个被装载的 bean。

(3)现修改 spring.xml 配置,只保留一个 bean,如下所示:

<bean id="person" class="com.southwind.entity.Person" autowire="byType"> 
    <property name="id" value="1"></property>
    <property name="name" value="张三"></property>
</bean>
<bean id="car" class="com.southwind.entity.StaticCarFactory" factory-method="getCar">
    <constructor-arg value="1"></constructor-arg>
</bean>

(4)测试类中获取 person 对象,运行结果如下图所示:

总结

本讲我们讲解了 Spring IoC 的工厂方法与自动装载,工厂方式是 Spring 框架对工厂模式的实现。自动装载是对上一讲内容的依赖注入优化,在维护对象关联关系的基础上,进一步简化配置。

分享交流

我们为本课程付费读者创建了微信交流群,以方便更有针对性地讨论课程相关问题。入群方式请到第 1-4 课末尾添加小助手的微信号,并注明「全家桶」。

阅读文章过程中有任何疑问随时可以跟其他小伙伴讨论,或者直接向作者提问(作者看到后抽空回复)。你的分享不仅帮助他人,更会提升自己。