Java依赖注入(Dependency Injection,DI )

本文介绍了Java依赖注入的概念,以解决代码中频繁修改依赖实现的问题。文章通过实例展示了依赖注入的重要性,并详细讲解了包括构造器、setter方法和field注入在内的三种实现方式。此外,还讨论了@Autowired注解的使用及其潜在问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java依赖注入(Dependency Injection,DI )

引言

一个老板雇了一个程序员,为他编写一个看电影的方法。
老板最开始家境贫寒,只有走路去,程序员将方法实现为

public void watchMovie(){
	Ststem.out.println("go to cinema on foot;" );
}

突然有一天,老板一夜暴富,买了一辆车,那么以后就可以开车去看电影了,程序员将方法实现改为

public void watchMovie(){
	Ststem.out.println("go to cinema by car;" );
}

又过了一段时间,老板发现身边潮流变成了自行车出行,要求程序员继续修改,程序员又将方法实现改为

public void watchMovie(){
	Ststem.out.println("go to cinema by bike;" );
}

长此以往,每次都要改,程序员觉得实在太麻烦了,既然刁老板这么爱改,怎么去电影院干脆不写死了,留给老板定。这在写方法的时候很容易想到解决方案:定义一个形参,通过传参的方法确定去电影院的方式

public void watchMovie(String transportation){
	Ststem.out.println("go to cinema " + transportation);
}

如此一来,以后就不用每次修改代码了

依赖

一个类依赖于另一个类的定义
举例
在SpringBoot中,Controller和ServiceImpl之间就是一种依赖关系,前者需要使用后者提供的服务
假如没有依赖注入,Controller想要使用ServiceImpl最简单直接的方式是new一个实例

class PeopleController{
	PeopleServiceImpl p = new PeopleServiceImpl;
	p.watchMovie();
}
class PeopleServiceImpl{
	public void watchMovie(){
		Ststem.out.println("go to cinema by car;" );
    }
}

问题

那么问题来了,就像引言里提到的那样,看电影可以有多种实现方式,开车、骑车等等,根据单一职责原则,这些不同的方法实现应该写在不同的PeopleServiceImpl类里,按照上面的写法,Controller每次选择不同的出行方式都要实例化一个新的类,这就需要修改PeopleController里的源代码。

这个问题该如何解决呢?

依赖注入

同样的,跟引言里设置形参的思路相同,在PeopleController里也不把实例化哪个类直接定死,而是交给外部决定。或者说把实例化依赖的方式更改为外部注入依赖。(这也是实现控制反转IoC的重要方式,将对类的控制交给外部,也就是Spring容器),这就是依赖注入

具体实现

依赖注入有三种常见的方式,在介绍这三种方式之前,首先介绍另一个遗留的问题。

上述包含看电影方法的不同类都只有一个看电影方法,根据面向接口编程的原则,首先可以抽象出一个PeopleService接口,它提供了看电影的声明,以后想要添加别的实现方式只用实现这个接口就行了。

@Service
interface PeopleService{
	public void watchMovie();
}

这个接口也是后续实现依赖注入必不可少的组成。因为在上面的看电影方法设置形参时,我们默认交通工具是一种String。但在依赖注入时,由于不知道将来会将哪个类注入进Controller,所以无法在形参中确定数据类型。有了这个接口之后,就可以将数据类型统一声明为PeopleService。因为实现了这个接口的类都可以用PeopleService声明

基于构造器

class PeopleController{
	private PeopleService p;
	PeopleController(PeopleService peopleService){
		this.p = peopleService;
	}
}

基于setter的依赖注入

class PeopleController{
	private PeopleService p;
	public void setService(PeopleService peopleService){
	this.p = peopleService;
	}
}

基于field的依赖注入

使用 @Autowired注解进行依赖注入

public class PeopleController {
    @Autowired
    private PeopleService p;
}

这种方法看起来更加简单,但不推荐使用,因为有下列缺陷

  • 该方法使用反射来注入依赖项,这比基于构造函数或基于 setter 的注入成本更高。
  • 使用这种方法继续添加多个依赖项真的很容易。如果我们使用构造函数注入,有多个参数会让我们认为类做不止一件事,这可能违反单一职责原则
  • 当同一个接口有多个实现时,需要进一步配置

扩展阅读JavaGuide

  • Autowired 属于 Spring 内置的注解,默认的注入方式为byType(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类)。

  • 这会有什么问题呢? 当一个接口存在多个实现类的话,byType这种方式就无法正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个。

  • 这种情况下,注入方式会变为 byName(根据名称进行匹配),这个名称通常就是类名(首字母小写)。就比如说下面代码中的 smsService 就是我这里所说的名称,这样应该比较好理解了吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值