Spring Boot IoC(四)依赖注入DI

上一篇 Spring Boot IoC(三)控制反转IoC

四、依赖注入DI

简介

Bean之间的依赖称为依赖注入。

例:人穿不同的鞋子去完成不同的活动。比如,人穿篮球鞋去打篮球,穿跑步鞋去跑步锻炼,穿皮鞋去上班等等。所以人和鞋子就是依赖关系。

我们用代码来展现依赖,定义两个接口,一个事人类(Person),一个是鞋子(Shoes)

package com.lay.ioc.pojo.definiion;

public interface Person {
	
	public void activity();

	public void setShoes(Shoes shoes);
}

package com.lay.ioc.pojo.definiion;

public interface Shoes {
	
	public void put();
}

Person实现类

package com.lay.ioc.pojo;

import org.springframework.beans.factory.annotation.Autowired;

import com.lay.ioc.pojo.definiion.Person;
import com.lay.ioc.pojo.definiion.Shoes;
@Component
public class Programmer implements Person{
	@Autowired
	private Shoes shoes=null;
	
	@Override
	public void activity() {
		this.shoes.put();
	}

	@Override
	public void setShoes(Shoes shoes) {
		this.shoes=shoes;
	}
}

Shoes实现类

package com.lay.ioc.pojo;

import com.lay.ioc.pojo.definiion.Shoes;
@Component
public class BasketShoes implements Shoes{

	@Override
	public void put() {
		System.out.println("穿篮球鞋【"+BasketShoes.class.getSimpleName()+"】去打球");
	}
}

@Autowired:根据属性的类型(by type)找到对应的Bean进行注入。

这里BasketShoes是Shoes接口的一种,所以Spring IoC容器会把BasketShoes的实例注入Programmer中。这样通过IoC容器获取Programmer实例的时候就能够使用BasketShoes实例来提供服务了。

测试代码如下

public class IocApplication {
	private static Logger log=LoggerFactory.getLogger(IocApplication.class);
	
	public static void main(String[] args) {
		ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
		Person person=ctx.getBean(Programmer.class);
		person.activity();
	}
}

输出

穿篮球鞋【BasketShoes】去打球

注解@Autowired

IoC容器是通过顶级接口BeanFactory的方法getBean方法来获取对应的bean的,而getBean又支持根据类型的(by type)和根据名称的(by name)。下面我们定义另外一个跑鞋RunShoes

RunShoes实现类

package com.lay.ioc.pojo;

import org.springframework.stereotype.Component;

import com.lay.ioc.pojo.definiion.Shoes;

@Component
public class RunShoes implements Shoes {

	@Override
	public void put() {
		System.out.println("穿跑鞋【"+BasketShoes.class.getSimpleName()+"】去跑步");
	}
}

然后再测试项目,会看到如下错误日志打印

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'programmer': Unsatisfied dependency expressed through field 'shoes'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.lay.ioc.pojo.definiion.Shoes' available: expected single matching bean but found 2: basketShoes,runShoes

由于Shoes有两个实现类,IoC并不知道你需要什么鞋子进行注入。但是我们将依赖属性名称从shoes变为runShoes,做如下改动:

	@Autowired
	private Shoes runShoes=null;

打印日志

穿跑鞋【BasketShoes】去跑步

并没有报错。

原因是@Autowired注解默认是根据属性的类型(by type)找到对应的Bean进行注入。如果对应的类型Bean并不是唯一的,它会根据属性名称和Bean的名称进行匹配。如果匹配的上,就会使用该Bean,如果还是无法匹配,就会抛出异常。

注意Autowired是一个默认必须找到对应的Bean的注解,如果不能确定其标注的属性一定会存在并允许这个被标注的属性为null,那么你可以配置@Autowired属性requiredfalse

@Autowired(required=false)

消除歧义性

上面我们把依赖名称改为runShoes虽然可以让Ioc找到注入的Bean,但是这样是不合理的,为了解决这个问题我们需要消除歧义性。主要用到两个注解@Primary@Quelifier

@Primary:告诉Spring Ioc容器,当发现有多个同样类型的Bean时,请优先使用这个Bean进行注入。

@Component
@Primary
public class RunShoes implements Shoes {
	/*--------*/
}

@Quelifier:与@Autowired组合指定value,通过类型和名称一起找到Bean。

@Autowired
@Quelifier("runShoes")

带参数的构造方法类的装配

在上面我们都基于一个默认的情况,那就是不带参数的构造方法下实现依赖注入。但是事实上有些类只有带有参数的构造方法,上面的方法就会失效。为了实现带参数的构造方法类的装配,我们可以使用@Autowired注解对构造方法的参数进行注入。

我们对Programmer程序员这个类进行改造

@Component
public class Programmer implements Person{
	private Shoes shoes=null;
	
    public Programmmer(@Autowired @Quelifier("runShoes") Shoes shoes){
        this.shoes=shoes;
    }
    
	@Override
	public void activity() {
		this.shoes.put();
	}

	@Override
	public void setShoes(Shoes shoes) {
		this.shoes=shoes;
	}
}

可以看到,代码中取消了@Autowired对属性和方法的标注。在参数上加入了@Autowired和@Qualifier注解,使得它能够注入进来。这里使用@Qualifier是为了避免歧义性。当然,如果环境中只有一个实现类,则可以完全不用@Qualifier,仅使用@Autowired就可以了。

下一篇 Spring Boot IoC(五)Bean生命周期

你可能感兴趣的:(Spring,Boot,IoC)