依赖注入ID
IOC 的两个重要功能中的 Bean 装配,已在4.2节通过实例演示,另一个功能——依赖注入,将通过本节进行演示讲解。
依赖注入主要有三种方式:构造函数注入、SET 方法注入、注解的方式注入。考虑到 Spring Boot 注重注解,我们不再讲解前两种方式。相关的注解有 @Component、@Service、@Controller、@Repository、@Autowired、@Resource、@Qualifier、@Autowired 等。
常用注解
-
@Component:参考4.2.1小节说明。
-
@Service:用于标注业务层组件。
-
@Controller:用于标注控制层组件。
-
@Repository:用于标注数据访问组件,即 Dao 组件。
-
@Resource:用来指定名称注入。
@Service、@Controller、@Repository、@Resource 使用方式相同,这里仅展示一个实例,即 @Repository,不列举全部。
首先,在 ioc 包下,新建 dao 包,方便注解扫描,如图4.5所示。

然后,新建一个类并用 @Repository 进行注解,如下所示。
package com.springBoot.ioc.dao;
import org.springframework.stereotype.Repository;
@Repository(value = "myRepository")
public class MyRepository {
public void getDao(){
System.out.print("get a new dao");
}
}
修改测试类代码,方便测试,如下所示。
package com.springBoot.ioc.test;
/**
* @Desciption IOC实例测试类
*/
public class IocTestDemo {
private static final Logger log=LoggerFactory.getLogger(IocTestDemo. class);
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationCo ntext(MySpringBootConfig.class);
//MyRepository
MyRepository myRepository=context.getBean(MyRepository. class);
myRepository.getDao();
}
}
在代码中,通过 context 获取 MyRepository 的 Bean,然后调用这个对象中的 getDao 方法。最后,展示效果如下所示。
11:04:53.332[main]DEBUGorg.springframework.beans.factory.support. DefaultListableBeanFactory - Returning cached instance of singleton bean 'myRepository'
get a new dao
@Autowired注解
@Autowired 注解,做 Java 开发的都不会陌生,是依赖注入需要使用的,文中有些细节还需要注意。
@Autowired使用实例
通过一个常见的场景来描述。我们会养宠物,每个宠物都会有自己的特性,例如猫吃鱼、狗吃肉等。这里新建接口并新建实现包。首先知道程序的结构,以方便实现,如图4.6所示。

首先,新建 Animal 与 People 接口,People 接口代码如下所示。
package com.springBoot.ioc.pojo;
/**
* People的父接口
*/
public interface People {
//使用动物
public void use();
//获取动物
public void setAnimal(Animal animal);
}
Animal 接口代码如下所示。
package com.springBoot.ioc.pojo;
/**
* Animal的父接口
*/
public interface Animal {
//动物的特性
public void eat();
}
我们已经把接口写好,通过上面的代码可以看到,People 的接口中有一个 setAnimal 方法,将 People 与 Animal 联系起来。现在开始在 pojo 下新建 impl 包,用于存放实现接口的类,即 boss 与 cat。代码如下所示。
package com.springBoot.ioc.pojo.impl;
@Component("boss")
public class Boss implements People {
//依赖注入animal
@Autowired
private Animal animal=null;
@Override
public void use() {
animal.eat();
}
@Override
public void setAnimal(Animal animal) {
this.animal=animal;
}
}
又如以下代码。
package com.springBoot.ioc.pojo.impl;
import com.springBoot.ioc.pojo.Animal;
import org.springframework.stereotype.Component;
@Component("cat")
public class Cat implements Animal {
@Override
public void eat() {
System.out.print("猫吃鱼");
}
}
从上面的代码可以看到,我们使用 @Compoent 将 boss 与 cat 注入 IOC 容器中。在 Boss 类中,可以看到有一个新的注解 @Autowired,它会自动导入对应的 Bean。Animal 接口的实现类是 cat,所以在 boss 注入 IOC 时,就依赖注入 cat。最后,看看测试类,代码如下所示。
package com.springBoot.ioc.test;
/**
* @Desciption IOC实例测试类
*/
public class IocTestDemo {
private static final Logger log=LoggerFactory.getLogger(IocTestDemo. class);
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationCo ntext(MySpringBootConfig.class);
People people=context.getBean(Boss.class);
people.use();
}
}
执行代码,查看效果。
14:36:53.600[main]DEBUGorg.springframework.beans.factory. annotation.AutowiredAnnotationBeanPostProcessor - Autowiring by type from bean name 'boss' to bean named 'cat'
14:36:53.600 [main] DEBUG org.springframework.beans.factory.support. DefaultListableBeanFactory - Finished creating instance of bean 'boss'
14:36:53.600 [main] DEBUG org.springframework.beans.factory.support. DefaultListableBeanFactory - Returning cached instance of singleton bean 'cat'
……
14:36:53.671 [main] DEBUG org.springframework.beans.factory.support. DefaultListableBeanFactory - Returning cached instance of singleton bean 'boss'
猫吃鱼
Process finished with exit code 0
从上面的执行日志中可以看到两处加粗的字,第一处表示根据类型 type 进行依赖注入,第二处表示 boss 已经依赖注入 cat。
@Autowired的匹配规则
在上面的实例中,我们只有一个动物,所以依赖注入不会存在问题。但是如果 boss 又买了一个 dog 宠物,这个如何注入?
如果不确定,我们就使用代码来验证。在 impl 包下再新建 dog 类,以实现 Animal 接口,这样就出现了两个实现类,代码如下所示。
package com.springBoot.ioc.pojo.impl;
import com.springBoot.ioc.pojo.Animal;
import org.springframework.stereotype.Component;
@Component("dog")
public class Dog implements Animal {
@Override
public void eat() {
System.out.print("狗吃肉");
}
}
执行测试类,将会报错。
Caused by:
org.springframework.beans.factory.NoUniqueBeanDefinitionException:No qualifying bean of type 'com.springBoot.ioc.pojo.Animal' available:expected single matching bean but found 2: cat,dog
在上面的报错中,我们可以看到加粗的字体。这里的意思是,在依赖注入的时候,不知道具体的 Bean。下面有一种做法,可以解决出现的这个问题。代码如下所示。
@Autowired
private Animal animal=null;
将其修改如下代码。
@Autowired
private Animal dog=null;
当然重点是把 animal 修改为 dog。其实在代码中,我们需要把所有的 animal 都修改为 dog。为什么要如此修改?
因为 @Autowired 先根据类型查找对应的 Bean,如果出现了多个符合情况的 Bean,会再根据属性名称和 Bean 的名称规则进行查找。如果在查找的过程中,没有找到合适的 Bean,则抛出异常。
消除歧义
在 @Autowired 的匹配规则中,可以列举多个符合 type 的处理方式,但这种方式并不是太好,因为依赖注入的 animal 被我们具体化了。
对于这种有歧义的问题,我们能继续通过注解的方式解决吗?答案为是的。可以使用 Spring Boot 中的 @Primary 与 @Qualifier 进行解决,下面,我们针对这个做一个讲解。
@Primary:优先权的注解,当有多个符合情况的 Bean 时,我们在类上加了这个注解就会优先装配。代码如下所示。
package com.springBoot.ioc.pojo.impl;
@Component("dog")
@Primary
public class Dog implements Animal {
@Override
public void eat() {
System.out.print("狗吃肉");
}
}
效果如下所示。
15:47:03.793[main]DEBUGorg.springframework.beans.factory.support. DefaultListableBeanFactory - Returning cached instance of singleton bean 'boss'
狗吃肉
很显然, @Primary 可以解决歧义的问题。那么问题又来了,如果场景中有很多符合条件的 Bean,但有两个 Bean 都被 @Primary 注解过,这时又出现了歧义,还可以使用新的注解解决吗?
此时,我们可以将 @Qualifier 与 @Autowired 结合使用,通过 Bean 类型与 Bean 名称找到需要的 Bean。首先,修改的代码如下所示。
package com.springBoot.ioc.pojo.impl;
@Component("boss")
public class Boss implements People {
//依赖注入animal
@Autowired
@Qualifier("cat")
private Animal animal=null;
@Override
public void use() {
animal.eat();
}
@Override
public void setAnimal(Animal animal) {
this.animal=animal;
}
}
在代码中,我们可以看到加粗的声明。通过添加这样的注解,依赖注入时,就可以通过类型 Animal 与名称 dog 找到对应的 Bean。效果如下所示。
16:14:29.802[main]DEBUGorg.springframework.beans.factory.support. DefaultListableBeanFactory - Returning cached instance of singleton bean 'boss'
猫吃鱼