Java中的依赖注入

简介: Java中的依赖注入

依赖注入,看起来好像是一个高大上的词语。不过事实上,依赖注入也应用到了我们日常开发中的很多很多地方。可以说,依赖注入(Dependency Injection)是一个很巧妙的思想,今天就来简单地做一个总结。

1,依赖注入的三种方式

在Java中,通常有三种依赖注入的方式:

  • 构造器注入
  • setter注入
  • 接口注入

通常来说,构造器注入和setter注入可以说是最常用的方式,而接口注入是从别的地方注入。先主要来讲解构造器注入setter注入

在此之前,我们先新建一个电脑类和打印机类作为例子讲解,如下:

打印机类:

packagecom.example.di.model;
/*** 打印机类*/publicclassPrinter {
/*** 打印机的打印方法** @param content 打印内容*/publicvoidprint(Stringcontent) {
System.out.println("打印了:"+content);
   }
}

电脑类:

packagecom.example.di.model;
/*** 电脑类*/publicclassComputer {
/*** 电脑的打印机*/privatePrinterprinter;
// 省略getter和setter}

可见,电脑类中有一个打印机类字段,这说明组成电脑类中有打印机类成员,那么很显然:打印机是电脑的依赖

大家一定先要理解依赖这个词。

也很显然,电脑类中的打印机还没有实例化,如果只实例化电脑,其打印机还是用不了的

那有的同学把电脑类改装如下:

packagecom.example.di.model;
/*** 电脑类*/publicclassComputer {
/*** 电脑的打印机*/privatePrinterprinter=newPrinter();
}

这样显然是不行的,基本上好像也没有人这么写。在电脑类中实例化打印机实例,这会导致电脑类和打印机类高度耦合,不利于扩展。譬如说现在打印机只是一个接口或者是抽象类,接口(抽象类)的实现类有彩色打印机类和黑白打印机类,结果不同电脑实例要用不同的打印机,那显然就不能再电脑类里面先实例化打印机了。

因此我们之所以使用依赖注入,是为了减少依赖性,增强可重用性,增加可读性、可扩展性等等。

(1) 构造器注入

在电脑类中写一个带参构造函数,用于给其字段打印机赋值:

/*** 电脑类带参构造器* @param printer 传入打印机实例*/publicComputer(Printerprinter) {
// 构造器注入this.printer=printer;
}

然后我们在主方法中,实例化电脑类,并注入打印机实例试试:

Printerprinter=newPrinter();
// 实例化电脑类,通过构造器注入了打印机实例(依赖)Computercomputer=newComputer(printer);
computer.getPrinter().print("构造器注入");

结果:

网络异常,图片无法展示
|

利用构造器传参,赋值,这就完成了构造器注入,很简单。

可见依赖注入也很好理解:不是让一个对象自己生成或者创造自己的依赖而是从外部注入

(2) setter注入

在讲这个方法之前,请好好看看你的setter方法,相信你已经对它再熟悉不过了:

publicvoidsetPrinter(Printerprinter) {
this.printer=printer;
}

没错,我们平时创建类,离不开gettersetter,那么其实setter就是利用了依赖注入的思想。

利用专门的一个setter方法实现依赖注入,就是setter注入的方式,也可以说是方法注入。

我们再在主方法中,来实例化一下试试:

Printerprinter=newPrinter();
// 实例化电脑类Computercomputer=newComputer();
// 通过setter方法注入了打印机实例(依赖)computer.setPrinter(printer);
computer.getPrinter().print("setter注入");

网络异常,图片无法展示
|

到此,相信大家知道依赖注入到底是什么了。通俗地讲,依赖注入就是在实例化一个类对象的时候不要同时把其中的依赖也给在类的内部实例化了,而是要先实例化这个类,再在外部实例化其依赖,然后把依赖通过构造器或者setter方法传入进去。

2,Spring中的自动装配

相信大家无论是开发Spring还是Spring Boot工程,对@Autowired这个注解已经再熟悉不过了,这其实就是用注解的方式实现了依赖注入。

只不过,利用这个注解,Spring框架就会自动帮你实例化对应的对象赋值帮你完成注入这个过程。例如上面在实例化电脑类时我们还要实例化它的依赖,也就是打印机类,最后再注入。而在Spring中利用@Autowired注解,就不需要我们去手动实例化依赖了,框架会帮我们实例化好。这就是自动装配

还是以一个例子开始,这里定义了一个服务类接口CharacterService及其对应的实现类CharacterServiceImpl,这个服务类可以调用MyBatis DAO查询数据库中所有的游戏角色数据。几个类代码以及Mapper XML如下:

DAO:

packagecom.example.firstssm.dao;
importcom.example.firstssm.dataobject.Character;
importorg.apache.ibatis.annotations.Mapper;
importjava.util.List;
@MapperpublicinterfaceCharacterDAO {
List<Character>getAll();
}

Mapper XML:

<?xmlversion="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.example.firstssm.dao.CharacterDAO"><resultMapid="characterResultMap"type="com.example.firstssm.dataobject.Character"><idcolumn="id"property="id"/><resultcolumn="name"property="name"/><resultcolumn="nickname"property="nickname"/><resultcolumn="type"property="type"/><resultcolumn="guild"property="guild"/><resultcolumn="gmt_created"property="gmtCreated"/><resultcolumn="gmt_modified"property="gmtModified"/></resultMap><selectid="getAll"resultMap="characterResultMap">      select *
      from `character`
</select></mapper>

服务类接口:

packagecom.example.firstssm.service;
importcom.example.firstssm.dataobject.Character;
importorg.springframework.stereotype.Service;
importjava.util.List;
@ServicepublicinterfaceCharacterService {
/*** 查询全部角色** @return 全部角色列表*/List<Character>queryAll();
}

实现类:

packagecom.example.firstssm.service.impl;
importcom.example.firstssm.dao.CharacterDAO;
importcom.example.firstssm.dataobject.Character;
importcom.example.firstssm.service.CharacterService;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Component;
importjava.util.List;
@ComponentpublicclassCharacterServiceImplimplementsCharacterService {
@AutowiredprivateCharacterDAOcharacterDAO;
@OverridepublicList<Character>queryAll() {
returncharacterDAO.getAll();
   }
}

角色实体类:

packagecom.example.firstssm.dataobject;
importlombok.Getter;
importlombok.NoArgsConstructor;
importlombok.Setter;
importjava.io.Serializable;
importjava.time.LocalDateTime;
@Getter@Setter@NoArgsConstructorpublicclassCharacterimplementsSerializable {
/*** 主键id*/privateintid;
/*** 角色名*/privateStringname;
/*** 外号*/privateStringnickname;
/*** 角色定位*/privateStringtype;
/*** 公会*/privateStringguild;
/*** 创建时间*/privateLocalDateTimegmtCreated;
/*** 修改时间*/privateLocalDateTimegmtModified;
@OverridepublicStringtoString() {
return"主键id:"+id+" 角色名:"+name+" 角色外号:"+nickname+" 角色定位:"+type+" 角色公会:"+guild;
   }
}

同样,@Autowired也有如下的注入方式。

(1) 字段注入

这应该是我们实际开发很常见的搞法了,我们创建一个测试类试一试:

packagecom.example.firstssm;
importcom.example.firstssm.dataobject.Character;
importcom.example.firstssm.service.CharacterService;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Component;
importjavax.annotation.PostConstruct;
importjava.util.List;
@ComponentpublicclassTest {
// 字段注入@AutowiredprivateCharacterServicecharacterService;
@PostConstructpublicvoidinitTest() {
// 调用一下试试List<Character>characterList=characterService.queryAll();
for (Charactercharacter : characterList) {
System.out.println(character);
      }
   }
}

结果:

网络异常,图片无法展示
|

可见,我们并没有手动new一个实例赋值给字段characterService,但是我们仍然可以在这里正常使用它,因为我们对这个字段标注了自动装配,那么启动的时候,框架就会去找CharacterService的实现类,并自动实例化一个对应的实例赋值给这个字段,就实现了依赖注入。

(2) 构造器注入

@Autowired还可以标注在构造函数上面或者构造函数中的参数上:

packagecom.example.firstssm;
importcom.example.firstssm.dataobject.Character;
importcom.example.firstssm.service.CharacterService;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Component;
importjavax.annotation.PostConstruct;
importjava.util.List;
@ComponentpublicclassTest {
privateCharacterServicecharacterService;
// 标注在构造函数上@AutowiredpublicTest(CharacterServicecharacterService) {
this.characterService=characterService;
   }
@PostConstructpublicvoidinitTest() {
// 调用一下试试List<Character>characterList=characterService.queryAll();
for (Charactercharacter : characterList) {
System.out.println(character);
      }
   }
}

还可以:

packagecom.example.firstssm;
importcom.example.firstssm.dataobject.Character;
importcom.example.firstssm.service.CharacterService;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Component;
importjavax.annotation.PostConstruct;
importjava.util.List;
@ComponentpublicclassTest {
privateCharacterServicecharacterService;
// 标注在构造函数参数上publicTest(@AutowiredCharacterServicecharacterService) {
this.characterService=characterService;
   }
@PostConstructpublicvoidinitTest() {
// 调用一下试试List<Character>characterList=characterService.queryAll();
for (Charactercharacter : characterList) {
System.out.println(character);
      }
   }
}

运行,效果一样。

这样,就通过构造器完成自动装配,这就是Spring中构造器注入方式。

(3) setter注入

同样,自动装配也可以标注在setter方法上:

packagecom.example.firstssm;
importcom.example.firstssm.dataobject.Character;
importcom.example.firstssm.service.CharacterService;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Component;
importjavax.annotation.PostConstruct;
importjava.util.List;
@ComponentpublicclassTest {
privateCharacterServicecharacterService;
// 方法注入@AutowiredpublicvoidsetCharacterService(CharacterServicecharacterService) {
this.characterService=characterService;
   }
@PostConstructpublicvoidinitTest() {
// 调用一下试试List<Character>characterList=characterService.queryAll();
for (Charactercharacter : characterList) {
System.out.println(character);
      }
   }
}

3,总结

可见依赖注入并不难,依赖注入的思想,也贯穿于我们日常的开发中。不过大家至少还是要熟悉几种常见方式。在Spring框架中,也可见依赖注入更加方便,不需要我们实例化依赖,自动装配就能够搞定。

相关文章
|
22天前
|
设计模式 Java 测试技术
Java 中的依赖注入和控制反转(IoC)
【4月更文挑战第19天】Java编程中的依赖注入和控制反转(IoC)是提升代码可维护性、可测试性和灵活性的关键设计模式。依赖注入通过外部注入对象依赖,减少硬编码,而IoC则是将控制权交给外部框架或容器。两者带来松耦合、可测试性、代码重用和灵活性。实现方式包括Spring框架和手动注入。使用框架能提供额外功能并简化管理,但要注意明确依赖关系、避免过度依赖和选择合适注入方式。依赖注入和IoC是构建健壮系统的有效工具。
|
XML 设计模式 存储
Java Spring IOC容器与依赖注入DI实现原理
本文主要讲解Spring IOC机制和实现过程,依赖注入DI和面向切面编程AOP是Spring框架的核心概念,几乎后续使用到Spring Boot框架的地方都有这两个概念的影子。也是Java面试的考察点,我们会结合实际的例子演示说明 。
815 1
|
12月前
java202304java学习笔记第六十天-ssm-spring配置文件-依赖注入4
java202304java学习笔记第六十天-ssm-spring配置文件-依赖注入4
39 0
|
12月前
java202304java学习笔记第五十九天员工管理-ssm-spring配置文件-依赖注入2
java202304java学习笔记第五十九天员工管理-ssm-spring配置文件-依赖注入2
31 0
|
12月前
java202304java学习笔记第六十天-ssm-spring配置文件-依赖注入5
java202304java学习笔记第六十天-ssm-spring配置文件-依赖注入5
35 0
|
Java 自动驾驶 小程序
java面试题:哪种依赖注入方式你建议使用,构造器注入,还是Setter方法 注入?
java面试题:哪种依赖注入方式你建议使用,构造器注入,还是Setter方法 注入?
java面试题:哪种依赖注入方式你建议使用,构造器注入,还是Setter方法 注入?
|
容器 Java 自动驾驶
java面试题:有哪些不同类型的IOC(依赖注入)方式?
java面试题:有哪些不同类型的IOC(依赖注入)方式?
java面试题:有哪些不同类型的IOC(依赖注入)方式?
|
Java Spring 容器
java面试题:什么是Spring的依赖注入?
java面试题:什么是Spring的依赖注入?
java面试题:什么是Spring的依赖注入?
|
达摩院 安全 Java
Java:控制反转(IoC)与依赖注入(DI)
今天,正式介绍一下Java极客技术知识星球 SpringBoot 精髓之 SpringBoot-starter 跟我学spring security系列文章第一章 实现一个基本的登入 Spring 源码分析:不得不重视的 Transaction 事务 我们谈谈面试技巧(初入职场年轻人该学的)
|
前端开发 Java Android开发
ABAP模拟Java Spring依赖注入(Dependency injection)的一个尝试
ABAP模拟Java Spring依赖注入(Dependency injection)的一个尝试
ABAP模拟Java Spring依赖注入(Dependency injection)的一个尝试