1. IoC
IoC(Inversion of Control,控制反转) 面向对象的一种设计思想,很多语言的框架都使用了IoC这个设计思想,并非特属于
Spring,其实现为将实例对象交给第三方容器管理,创建实例对象的时候,注入这些实例对象所依赖的实例对象,而不是在内部创建。所谓的内部创建如下所示,连接数据库的DateSource,在默认构造函数直接初始化属性值:
/**
* @author syrdbt
* @date 2020-11-15
*/
public class DateSource {
private String dataBaseUrl;
private String userName;
private String password;
public DateSource() {
}
public DateSource() {
String dateBaseUrl = new String("127.0.0.1:3306");
String userName = new String("syrdbt");
String password = new String("syrdbt");
}
}
使用DateSource如下所示:
public class Main {
public static void main(String[] args) {
DataSource dataSource = new DataSource();
}
}
使用 IoC 之后,你只需定义一个如下的DateSource类:
/**
* @author syrdbt
* @date 2020-11-15
*/
public class DateSource {
private String dataBaseUrl;
private String userName;
private String password;
public DateSource() {
}
public DateSource(String dataBaseUrl, String userName, String password) {
this.dataBaseUrl = dataBaseUrl;
this.userName = userName;
this.password = password;
}
}
获取DataSource实例的时候,使用一个管理这个实例的容器工厂去获取,上面实例化的127.0.0.1:3306、syrdbt等信息,在其他地方配置即可,例如XML文件中,容器会去读取XML文件这些配置:
public class Main {
public static void main(String[] args) {
// 读取第三方配置中的数据、并将DataSource放入容器之中
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取该DataSource实例
DataSource dataSource = Factor.get("dataSource");
}
2. 依赖注入(DI)
依赖注入(Dependency Injection)是Spring框架实现控制反转的方式。
依赖注入有三种方式:构造函数注入、属性注入 和 接口注入。
2.1 构造函数注入
Service类如下所示。
/**
* @author syrdbt
* @date 2020-11-14
*/
public class Service {
private Mapper mapper;
public Service(Mapper mapper) {
this.mapper = mapper;
}
}
Service 使用构造函数注入 Mapper如下所示:
public static void main(String[] args) {
// 注入Mapper实例
Service service = new Service(new Mapper());
}
2.2 属性注入
Service 类如下所示:
/**
* @author syrdbt
* @date 2020-11-14
*/
public class Service {
private Mapper mapper;
public void setMapper(Mapper mapper) {
this.mapper = mapper;
}
}
使用属性注入如下所示:
public static void main(String[] args) {
Service service = new Service();
// 注入Mapper实例
service.setMapper(new Mapper());
}
2.3 接口注入
Service 接口如下所示:
/**
* @author syrdbt
* @date 2020-11-14
*/
public interface Service {
void init(Mapper mapper);
}
需要去实现这个接口:
/**
* @author syrdbt
* @date 2020-11-14
*/
public class ServiceImpl implements Service {
private Mapper mapper;
@Override
public void init(Mapper mapper) {
this.mapper = mapper;
}
}
调用方法注入实例,通过接口注入,需要额外声明接口,增加了类的数量,但效果和属性注入是一样的。
public static void main(String[] args) {
Service service = new ServiceImpl();
// 注入Mapper实例
service.init(new Mapper());
}
2.4 不使用依赖注入的情况
不使用依赖注入就会导致代码耦合,如下所示。
/**
* @author syrdbt
* @date 2020-11-14
*/
public class Service {
private Mapper mapper;
public Service() {
this.mapper = new Mapper();
}
public static void main(String[] args) {
// 无需注入
Service service = new Service();
}
}
通过上述四个例子,你会发现依赖注入其实是一个很简单的概念。
3. Spring IoC 中的依赖注入
3.1 Spring IoC 中的依赖注入实例
Spring 支持属性注入和构造注入两种注入方式构建Bean实例。
People 类如下所示,People 就是一个调用者:
package com.study.syrdbt.entity;
/**
* @author syrdbt
* @date 2020-10-10
*/
public class People {
private String name;
private Integer age;
public People() {
}
public People(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
将 name 和 age 注入到 People 中,可以在 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="syrdbt" class="com.study.syrdbt.entity.People">
<property name="name" value="syrdbt"></property>
<property name="age" value="23"></property>
</bean>
<!-- 构造器注入 -->
<bean id="syrdbt1" class="com.study.syrdbt.entity.People">
<constructor-arg index="0">
<value>syrdbt</value>
</constructor-arg>
<constructor-arg index="1">
<value>233</value>
</constructor-arg>
</bean>
</beans>
测试类:
package com.study.syrdbt;
import com.study.syrdbt.entity.People;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author syrdbt
* @date 2020-10-10
*/
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
People syrdbt = (People) context.getBean("syrdbt");
System.out.println(syrdbt.toString());
People syrdbt1 = (People) context.getBean("syrdbt1");
System.out.println(syrdbt1.toString());
}
}
测试结果如下图所示:
3.2 Spring 如何使用 xml 文件去注入
第一步:读入XML文件;
第二步:解析XML文件,生成Bean对象,并存放在一个容器中(其实就是一个Map接口,key为xml中的id,value为实例)
第三步:通过id去访问这些Bean对象。
Spring 的内部实现当然不是上面三步那么简单,上面只不过是大致流程。
如下创建一个 id 为 syrdbt 的 Bean 实例,你需要指明它是哪一个类,这里指明为 com.study.syrdbt.entity.People,然后就可以通过这个目录位置,通过反射创建实例,并且调用其 set 方法注入设置的两个值 name=‘syrdbt’, age=233 。
<bean id="syrdbt" class="com.study.syrdbt.entity.People">
<property name="name" value="syrdbt"></property>
<property name="age" value="23"></property>
</bean>
做一个小小的测试,我们注释了无参构造函数,无法进行属性注入了,因为无法生成实例,再去调用 set 方法。
在进行如下修改,我们对 setName 方法进行参数打印,并注释掉了 setAge 方法,如下图所示,我们成功调用了 setName 方法。
如下图所示:找不到 age 的 set 方法,无法进行属性注入。
参考文献
- 精通 Spring 4.x 企业应用开发实战 陈雄华老师、林开雄老师、文建国老师
- Spring 实战第 4 版
- Spring 中文官方文档