IOC(Inversion of Control)
其思想是反转资源获取的方向。传统的资源查找方式要求组件向容器发起请求查找资源。作为回应,容器适时的返回资源。而使用了IOC之后,是容器主动将资源推送给它管理的组件,组件所需要做的则是选择一种合适的方式来接收资源。这种行为也被称为查找的被动形式。
DI(Dependency Injection)
IOC的另一种表述方式,即组件以一些预先定义好的方式(例如Setter)接收来自容器的资源注入。相比于IOC而言,这种表述更为直接。
通过XML的方式进行IOC有一个缺陷就是必须要有一个无参的构造器。如果要创建的Bean中的构造器是有参的则抛出异常。
DI实例
<bean id="helloWorld" class="com.spring.beans.HelloWorld">
<property name="name" value="Spring"></property>
</bean>
class: bean的全类名,通过反射的方法在IOC容器中创建bean。所以要求Bean中必须有无参构造函数。
id: 标识容器中的bean, id唯一。
Spring容器
ApplicationContext代表IOC容器。但是它实际上是一个接口。
在Spring IOC容器读取Bean,配置Bean,创建Bean实例之前必须要对它进行实例化。只有在容器实例化之后才可以从IOC容器中获取Bean实例并使用。
Spring提供了两种类型的IOC容器的实现:
- BeanFactory:IOC容器的基本实现
- ApplicationContext:提供了更多的高级特性。是BeanFactory的子接口。
BeanFactory是Spring框架的基础设施,面向Spring本身。ApplicationContext面向使用Spring框架的开发者本身。几乎所有的应用场合都使用ApplicationContext而非BeanFactory。
无论使用何种方式,配置文件时都是相同的。
ApplicationContext
ApplicationContext继承关系ApplicationContext的主要实现类有
- ClassPathXmlApplicationContext:从类路径下加载配置文件。
- FileSystemXmlApplicationContext:从文件系统中加载配置文件。
ConfigurableApplicationContext扩展于ApplicationContext。新增两个主要的方法refresh()和close()。让ApplicationContext具有启动,关闭和刷新上下文的能力。
ApplicationContext在初始化上下文时就实例化所有的单例的Bean。
WebApplicationContext是专门为Web准备的,它允许从Web根目录的路径中完成初始化的工作。
也可以通过类名来获取Bean,例如
HelloWorld hello2 = applicationContext.getBean(HelloWorld.class);
但是该方法有一个缺陷。就是当IOC容器中配置了两个来自同一个类的bean的时候,系统不知道要创建的是那一个bean就会抛出异常。所以要求IOC容器中只能有一个该类型的bean。
依赖注入的方式
- 属性注入
- 构造器注入
- 工厂方法注入(很少使用,不推荐)
属性注入
通过setter方法注入Bean的属性值或依赖的对象。
属性名注入使用的是<property/>标签,使用name属性指定Bean的属性名称。value属性或<value/>子结点指定属性值。
属性注入是开发中最常用的注入方式。
构造方法注入
通过构造方法注入Bean的属性值或依赖的对象。它保证了Bean实例在实例化之后就可以使用
构造器注入在<constructor-agr>元素里声明属性。<constructor-arg>中没有name属性。
使用构造器注入属性值可以指定参数的位置和参数的类型,以区分重载的构造器。
构造器注入示例
创建一个新的类
public class Car {
private String brand;
private String corp;
private int price;
private int maxSpeed;
public Car(String brand, String corp, int price, int maxSpeed) {
super();
this.brand = brand;
this.corp = corp;
this.price = price;
this.maxSpeed = maxSpeed;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", corp='" + corp + '\'' +
", price=" + price +
", maxSpeed=" + maxSpeed +
'}';
}
}
配置文件中注册
<bean id="car" class="com.spring.beans.Car">
<constructor-arg value="Audi"></constructor-arg>
<constructor-arg value="Shanghai"></constructor-arg>
<constructor-arg value="300000"></constructor-arg>
<constructor-arg value="10000"></constructor-arg>
</bean>
主函数中调用
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Car car = applicationContext.getBean(Car.class);
System.out.println(car);
}
问题来了?如果类中有两个构造方法,如下所示:
public class Car {
private String brand;
private String corp;
private int price;
private double maxSpeed;
public Car(String brand, String corp, int price) {
super();
this.brand = brand;
this.corp = corp;
this.price = price;
}
public Car(String brand, String corp, double maxSpeed) {
super();
this.brand = brand;
this.corp = corp;
this.maxSpeed = maxSpeed;
}
...
我们想要使用的是第二个构造函数。此时我们可以在配置文件中指定构造参数的类型。例如
<bean id="car" class="com.spring.beans.Car">
<constructor-arg value="Audi" type="java.lang.String"></constructor-arg>
<constructor-arg value="Shanghai" type="java.lang.String"></constructor-arg>
<constructor-arg value="10000" type="double"></constructor-arg>
</bean>
网友评论