Spring IoC(控制反转)、DI(依赖注入)

╰半夏微凉° 2022-12-21 03:28 286阅读 0赞

1. IoC

IoC(Inversion of Control,控制反转) 面向对象的一种设计思想,很多语言的框架都使用了IoC这个设计思想,并非特属于
Spring,其实现为将实例对象交给第三方容器管理,创建实例对象的时候,注入这些实例对象所依赖的实例对象,而不是在内部创建。所谓的内部创建如下所示,连接数据库的DateSource,在默认构造函数直接初始化属性值:

  1. /**
  2. * @author syrdbt
  3. * @date 2020-11-15
  4. */
  5. public class DateSource {
  6. private String dataBaseUrl;
  7. private String userName;
  8. private String password;
  9. public DateSource() {
  10. }
  11. public DateSource() {
  12. String dateBaseUrl = new String("127.0.0.1:3306");
  13. String userName = new String("syrdbt");
  14. String password = new String("syrdbt");
  15. }
  16. }

使用DateSource如下所示:

  1. public class Main {
  2. public static void main(String[] args) {
  3. DataSource dataSource = new DataSource();
  4. }
  5. }

使用 IoC 之后,你只需定义一个如下的DateSource类:

  1. /**
  2. * @author syrdbt
  3. * @date 2020-11-15
  4. */
  5. public class DateSource {
  6. private String dataBaseUrl;
  7. private String userName;
  8. private String password;
  9. public DateSource() {
  10. }
  11. public DateSource(String dataBaseUrl, String userName, String password) {
  12. this.dataBaseUrl = dataBaseUrl;
  13. this.userName = userName;
  14. this.password = password;
  15. }
  16. }

获取DataSource实例的时候,使用一个管理这个实例的容器工厂去获取,上面实例化的127.0.0.1:3306、syrdbt等信息,在其他地方配置即可,例如XML文件中,容器会去读取XML文件这些配置:

  1. public class Main {
  2. public static void main(String[] args) {
  3. // 读取第三方配置中的数据、并将DataSource放入容器之中
  4. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  5. // 获取该DataSource实例
  6. DataSource dataSource = Factor.get("dataSource");
  7. }

2. 依赖注入(DI)

依赖注入(Dependency Injection)是Spring框架实现控制反转的方式。

依赖注入有三种方式:构造函数注入、属性注入 和 接口注入。

2.1 构造函数注入

Service类如下所示。

  1. /**
  2. * @author syrdbt
  3. * @date 2020-11-14
  4. */
  5. public class Service {
  6. private Mapper mapper;
  7. public Service(Mapper mapper) {
  8. this.mapper = mapper;
  9. }
  10. }

Service 使用构造函数注入 Mapper如下所示:

  1. public static void main(String[] args) {
  2. // 注入Mapper实例
  3. Service service = new Service(new Mapper());
  4. }

2.2 属性注入

Service 类如下所示:

  1. /**
  2. * @author syrdbt
  3. * @date 2020-11-14
  4. */
  5. public class Service {
  6. private Mapper mapper;
  7. public void setMapper(Mapper mapper) {
  8. this.mapper = mapper;
  9. }
  10. }

使用属性注入如下所示:

  1. public static void main(String[] args) {
  2. Service service = new Service();
  3. // 注入Mapper实例
  4. service.setMapper(new Mapper());
  5. }

2.3 接口注入

Service 接口如下所示:

  1. /**
  2. * @author syrdbt
  3. * @date 2020-11-14
  4. */
  5. public interface Service {
  6. void init(Mapper mapper);
  7. }

需要去实现这个接口:

  1. /**
  2. * @author syrdbt
  3. * @date 2020-11-14
  4. */
  5. public class ServiceImpl implements Service {
  6. private Mapper mapper;
  7. @Override
  8. public void init(Mapper mapper) {
  9. this.mapper = mapper;
  10. }
  11. }

调用方法注入实例,通过接口注入,需要额外声明接口,增加了类的数量,但效果和属性注入是一样的。

  1. public static void main(String[] args) {
  2. Service service = new ServiceImpl();
  3. // 注入Mapper实例
  4. service.init(new Mapper());
  5. }

2.4 不使用依赖注入的情况

不使用依赖注入就会导致代码耦合,如下所示。

  1. /**
  2. * @author syrdbt
  3. * @date 2020-11-14
  4. */
  5. public class Service {
  6. private Mapper mapper;
  7. public Service() {
  8. this.mapper = new Mapper();
  9. }
  10. public static void main(String[] args) {
  11. // 无需注入
  12. Service service = new Service();
  13. }
  14. }

通过上述四个例子,你会发现依赖注入其实是一个很简单的概念。

3. Spring IoC 中的依赖注入

3.1 Spring IoC 中的依赖注入实例

Spring 支持属性注入和构造注入两种注入方式构建Bean实例。

People 类如下所示,People 就是一个调用者:

  1. package com.study.syrdbt.entity;
  2. /**
  3. * @author syrdbt
  4. * @date 2020-10-10
  5. */
  6. public class People {
  7. private String name;
  8. private Integer age;
  9. public People() {
  10. }
  11. public People(String name, Integer age) {
  12. this.name = name;
  13. this.age = age;
  14. }
  15. public String getName() {
  16. return name;
  17. }
  18. public void setName(String name) {
  19. this.name = name;
  20. }
  21. public Integer getAge() {
  22. return age;
  23. }
  24. public void setAge(Integer age) {
  25. this.age = age;
  26. }
  27. @Override
  28. public String toString() {
  29. return "People{" +
  30. "name='" + name + '\'' +
  31. ", age=" + age +
  32. '}';
  33. }
  34. }

将 name 和 age 注入到 People 中,可以在 xml 文件中配置:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  5. <!-- 属性注入 -->
  6. <bean id="syrdbt" class="com.study.syrdbt.entity.People">
  7. <property name="name" value="syrdbt"></property>
  8. <property name="age" value="23"></property>
  9. </bean>
  10. <!-- 构造器注入 -->
  11. <bean id="syrdbt1" class="com.study.syrdbt.entity.People">
  12. <constructor-arg index="0">
  13. <value>syrdbt</value>
  14. </constructor-arg>
  15. <constructor-arg index="1">
  16. <value>233</value>
  17. </constructor-arg>
  18. </bean>
  19. </beans>

测试类:

  1. package com.study.syrdbt;
  2. import com.study.syrdbt.entity.People;
  3. import org.springframework.context.ApplicationContext;
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;
  5. /**
  6. * @author syrdbt
  7. * @date 2020-10-10
  8. */
  9. public class Main {
  10. public static void main(String[] args) {
  11. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  12. People syrdbt = (People) context.getBean("syrdbt");
  13. System.out.println(syrdbt.toString());
  14. People syrdbt1 = (People) context.getBean("syrdbt1");
  15. System.out.println(syrdbt1.toString());
  16. }
  17. }

测试结果如下图所示:
在这里插入图片描述

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 。

  1. <bean id="syrdbt" class="com.study.syrdbt.entity.People">
  2. <property name="name" value="syrdbt"></property>
  3. <property name="age" value="23"></property>
  4. </bean>

做一个小小的测试,我们注释了无参构造函数,无法进行属性注入了,因为无法生成实例,再去调用 set 方法。
在这里插入图片描述
在进行如下修改,我们对 setName 方法进行参数打印,并注释掉了 setAge 方法,如下图所示,我们成功调用了 setName 方法。
在这里插入图片描述
如下图所示:找不到 age 的 set 方法,无法进行属性注入。
在这里插入图片描述

参考文献

  • 精通 Spring 4.x 企业应用开发实战 陈雄华老师、林开雄老师、文建国老师
  • Spring 实战第 4 版
  • Spring 中文官方文档

发表评论

表情:
评论列表 (有 0 条评论,286人围观)

还没有评论,来说两句吧...

相关阅读