这篇文章,主要介绍一下Spring中的IOC容器和DI依赖注入两个概念。
目录
一、IOC控制反转
1.1、什么是IOC
1.2、两种IOC容器
(1)基于BeanFactory的IOC容器
(2)基于ApplicationContext的IOC容器
二、DI依赖注入
2.1、什么是DI
2.2、四种依赖注入方式
(1)setter方法注入
(2)构造方法注入
(3)工厂方法注入
(4)静态方法注入
一、IOC控制反转
1.1、什么是IOC
IOC,英文全称是:Inversion Of Control,中文含义叫做:控制反转。
那具体什么是控制反转呢???
所谓的控制反转,是指:以前我们创建对象的方式是程序开发人员显式的通过【new】关键字进行创建,而到了Spring框架里面,我们对象的创建全部交给了Spring去创建,程序开发人员不需要知道对象是如何创建的,开发人员只需要从Spring里面获取需要的对象实例就可以使用了,这个过程我们就叫做控制反转。
控制反转,是一种主从换位的思想,就是说以前是开发人员创建对象,现在是Spring框架替我们创建对象,这就是将主从关系颠倒过来了。
控制反转的好处是什么呢???
我们可以看到,使用控制反转,可以不用显式的通过【new】创建对象,这样就可以减少很多的代码编写,提高一下开发效率了呗。
1.2、两种IOC容器
IOC容器主要作用就是:统一管理Bean的生命周期,以及Bean和Bean之间的依赖关系。
Spring框架里面,给我们提供了两种IOC容器,分别是:【BeanFactory】和【ApplicationContext】两大类。
(1)基于BeanFactory的IOC容器
BeanFactory位于spring-beans的jar包里面,是一个接口,它是Spring提供的最简单的IOC容器,基于BeanFactory接口实现的IOC容器只具备IOC和DI的功能,也就是说,BeanFactory的IOC容器只能够管理Bean的生命周期,以及Bean之间的依赖关系。
BeanFactory接口常见的实现类是:【XmlBeanFactory】类,这是一个通过读取XML配置文件的IOC容器。
BeanFactory特点:
功能简单,只具备IOC和DI的实现。
Bean实例化采用懒加载的方式(懒加载:只能主动调用getBean()方法获取Bean实例的时候才会去new创建对象)。
BeanFactory接口常用方法:
getBean():获取一个Bean对象。
BeanFactory常见实现类:
XmlBeanFactory类,这个类目前已经废弃,不推荐使用了。
BeanFactory代码案例:
public class ApplicationTest {
public static void main(String[] args) throws FileNotFoundException {
beanFactory();
}
private static void beanFactory() throws FileNotFoundException {
// 1、获取 BeanFactory 容器
ClassPathResource resource = new ClassPathResource("spring.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
// 2、获取 bean 对象
User user = beanFactory.getBean("user", User.class);
}
}
(2)基于ApplicationContext的IOC容器
ApplicationContext是BeanFactory的一个子接口,基于ApplicationContext实现的IOC容器不仅具备BeanFactory的所有功能,并且还提供了额外的一些功能,例如:事件监听、国际化等等企业级功能。
ApplicationContext特点:
容器启动时候,就会实例化所有配置的Bean对象。
提供更多的功能,例如:事件传播,国际化等等功能。
ApplicationContext常见实现类:
ClassPathXmlApplicatinContext实现类:加载类路径下的配置文件(即:加载【src/main/resources】目录下路径)。
FileSystemXmlApplicationContext实现类:加载系统路径下中的配置文件(这个必须写完整的XML文件路径)。
ApplicationContext案例代码:
public class ApplicationTest {
public static void main(String[] args) {
applicationContext();
}
private static void applicationContext() {
// 1、获取 ApplicationContext 容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
// 2、获取 Bean 对象
User user = context.getBean("user", User.class);
}
}
IOC是一种程序设计思想,并不是一种技术,而要实现IOC则需要通过DI依赖注入的方式,下面介绍一下什么是DI依赖注入???
二、DI依赖注入
2.1、什么是DI
DI,英文全称是:Dependency Injection,中文含义叫做:依赖注入。
依赖注入:这里的依赖是指Bean之间的依赖关系(引用关系,例如:A类使用B类对象,C类使用A类对象),注入是指给某个类中的属性赋值,依赖注入可以理解为当某个A类中引用了B类的实例对象,Spring会动态的将B类对象赋值到A类的属性里面,Spring框架这个动态赋值的操作我们就称为依赖注入。
在Spring框架里面,如果我们需要使用某个Bean对象的时候,我们不用自己创建就可以直接使用这个Bean对象,这是因为Spring框架已经给我们注入了所有的Bean。
什么是意思呢,举个栗子看看:
如果我们在某个A类里面使用到了B类的一个实例,此时Spring会自动将B类的实例注入到当前A类里面,而不需要我们再次通过new创建B的对象实例。
简单理解,依赖注入就是Spring框架自动帮我们把某个类中的属性已经赋值了,所以我们在使用的时候,就可以不用再次赋值,也就不会出现空指针的情况啦。
依赖注入的好处:通过依赖注入,可以极大程度上面降低类之间的耦合度,实现类与类之间的松耦合。
2.2、四种依赖注入方式
Spring框架中提供了四种依赖注入的方式,分别是如下所示:
- 通过setter方法实现依赖注入
- 通过工厂方法实现依赖注入
- 通过静态工厂方法实现依赖注入
- 通过构造方法实现依赖注入
(1)setter方法注入
Spring框架可以通过调用属性的【setter】方法进行属性赋值,从而实现依赖注入。Spring通过反射机制调用属性的【setXXX()】方法,将所需要的对象赋值给对应的类属性(注意:和具体的属性名称没有关系,只不过我们习惯将属性名称和setter方法名称命名相同)。
创建【Setter】测试类
public class Setter {
private String uid;
private String name;
private String pass;
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return pass;
}
// 注意:这里的 setPassword 方法名称和 Pass属性不同
public void setPassword(String pass) {
this.pass = pass;
}
}
配置spring.xml文件
setter方法注入,对应XML配置中的【<property>】标签。我们通过【<property>】标签就是采用的setter方法进行属性注入。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置 Setter类 -->
<bean id="setter" class="com.spring.demo.pojo.Setter">
<!-- 属性赋值 -->
<property name="uid" value="1001"/>
<property name="name" value="朱友斌"/>
<!-- 属性名称要和 setXxx() 对应的名称相同 -->
<property name="password" value="123456"/>
</bean>
</beans>
测试setter方法注入
public class SetterTest {
public static void main(String[] args) {
// 1、获取 ApplicationContext 容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
// 2、获取 Bean 对象
Setter setter = context.getBean("setter", Setter.class);
System.out.println(setter.getUid());
System.out.println(setter.getName());
System.out.println(setter.getPassword());
}
}
以上,就是通过setter方法进行属性注入的一个案例,下一篇文章会详细的介绍各种依赖注入的方式以及各种数据类型的依赖注入,这里先大致有个了解。
(2)构造方法注入
Spring也支持通过构造方法进行属性注入(属性赋值),主要是调用Bean相应的构造方法。
Spring里面通过在XML配置文件中使用【<constructor-arg>】标签,就可以实现构造方法注入属性。
创建【Constructor】测试类
public class Constructor {
public String uid;
public String name;
public String pass;
// 构造方法注入属性
public Constructor(String uid, String name, String pass) {
this.uid = uid;
this.name = name;
this.pass = pass;
}
}
添加XML配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置 Constructor 类 -->
<bean id="constructor" class="com.spring.demo.pojo.Constructor">
<!-- 构造方法属性赋值 -->
<constructor-arg name="uid" value="1001"/>
<constructor-arg name="name" value="朱友斌"/>
<constructor-arg name="pass" value="123456"/>
</bean>
</beans>
测试构造方法注入
public class ConstructorTest {
public static void main(String[] args) {
// 1、获取 ApplicationContext 容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
// 2、获取 Bean 对象
Constructor constructor = context.getBean("constructor", Constructor.class);
System.out.println(constructor.uid);
System.out.println(constructor.name);
System.out.println(constructor.pass);
}
}
构造方法注入,有两种方式,分别是:
- 第一种方式:指定具体的参数名称进行赋值。
- 第二种方式:根据构造方法中的参数位置进行赋值。
上面我们就是通过【<constructor-arg>】标签的参数名称进行赋值,这里我再给出通过参数位置(参数位置从0开始编号)进行赋值的代码配置,如下所示:
<bean id="constructor" class="com.spring.demo.pojo.Constructor">
<!-- 构造方法属性赋值 -->
<constructor-arg index="0" value="1001"/>
<constructor-arg index="1" value="朱友斌"/>
<constructor-arg index="2" value="123456"/>
</bean>
(3)工厂方法注入
Spring框架实例化Bean是调用无参构造方法实现的,所以如果某个Bean没有无参构造方法,并且没有通过有参构造方法进行注入,那么在Spring实例化的时候就会失败,并且抛出异常。如何解决这个Bean无法实例化的问题呢???
Spring框架提供了实例工厂方法,我们可以通过工厂方法进行实例化操作。
如何使用实例工厂方法进行依赖注入???
- 使用实例工厂方法,需要创建一个实例工厂,然后在工厂类中提供一个创建实例的方法。
- 然后在目标Bean里面,配置【factory-bean】和【factory-method】两个属性。
- 【factory-bean】作用:告诉Spring,当前这个Bean是从哪个实例工厂bean获取。
- 【factory-method】作用:告诉Spring,当前这个Bean是从工厂类中哪个method方法进行获取。
创建【Person】实体类
public class Person {
public Integer pid;
public String name;
// 无参构造方法
public Person(Integer pid, String name) {
this.pid = pid;
this.name = name;
}
}
创建【FactoryMethod】工厂类
public class FactoryMethod {
// 工厂方法
public Person getInstance() {
return new Person(1002, "朱友斌");
}
}
添加XML配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置工厂类 -->
<bean id="factory" class="com.spring.demo.pojo.FactoryMethod"/>
<!-- 配置 Person 类 -->
<bean id="person" class="com.spring.demo.pojo.Person" factory-bean="factory" factory-method="getInstance"/>
</beans>
在XML配置文件里面,需要配置【factory-bean】和【factory-method】两个属性,这是告诉Spring,当前这个Person类实例化,是去找一个叫做【factory】的bean里面的【getInstance()】方法进行获取。
编写测试类,查看Person是否实例化成功
public class FactoryMethodTest {
public static void main(String[] args) {
// 1、获取 ApplicationContext 容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring02.xml");
// 2、获取 Bean 对象
Person person = context.getBean("person", Person.class);
System.out.println(person.pid);
System.out.println(person.name);
}
}
以上,就是通过实例工厂方法进行Bean的依赖注入。
(4)静态方法注入
从(3)里面,我们学习了通过实例工厂方法进行Bean的依赖注入,基本分为两个步骤,第一步是创建工程类的Bean,第二步是根据工程类的Bean创建目标Bean的实例。
这里我们可以发现,我们创建目标Bean的时候依赖于工厂类的Bean对象,所以这里有个问题,如果我们的工厂类的Bean也没有办法实例化,那实例工厂方法注入不就没有办法实现了吗???
为了解决实例工厂方法的缺陷,Spring也提供了静态工厂方法进行Bean的注入。既然实例工厂没办法实例化,那我们就采用静态工厂,因为静态工厂是可以直接通过类进行访问的,和实例对象没有关系,所以我们就可以通过一个静态方法,这个静态方法就是用于创建目标Bean的实例对象。
静态工厂方法依赖注入
- 创建静态工厂类,提供一个静态工厂方法,用于获取目标Bean的对象。
- 然后在配置文件里面通过【factory-method】属性配置获取bean对象的静态方法。
创建【StaticFactoryMethod】静态工厂类
public class StaticFactoryMethod {
// 静态工厂方法
public static Person getInstance() {
return new Person(1002, "朱友斌");
}
}
添加XML配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 通过静态工厂方法获取 Person 类实例 -->
<bean id="person" class="com.spring.demo.pojo.StaticFactoryMethod" factory-method="getInstance"/>
</beans>
创建测试类,测试是否依赖注入成功
public class StaticFactoryMethodTest {
public static void main(String[] args) {
// 1、获取 ApplicationContext 容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring03.xml");
// 2、获取 Bean 对象
Person person = context.getBean("person", Person.class);
System.out.println(person.pid);
System.out.println(person.name);
}
}
以上,就是通过静态工厂方法进行依赖注入。
综上,讲述了Spring框架中的IOC容器,DI依赖注入以及四种依赖注入的方式。依赖注入的四种方式主要记住setter方法和构造方法注入,因为实例工厂方法、静态工厂方法在实际开发中不常用