Spring学习笔记(五)关于@Autowired寻找bean的顺序以及bean的歧义性问题

这篇博客探讨了Spring中@Autowired注解的工作原理,首先通过一个例子展示了当按类型匹配bean时,Spring如何选择合适的依赖进行注入。接着,当存在多个相同类型的bean时,Spring会按照bean的id进行匹配。如果按照名称和类型都无法找到唯一bean,将会导致歧义错误。博主通过添加注解和自定义限定注解的方式,详细解释了如何解决这种歧义性问题。

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

  • 创建xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.zj.ioc.demo"></context:component-scan>
</beans>
  • 创建People接口
package com.zj.ioc.demo;

interface People {
}
  • 创建Student类
package com.zj.ioc.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Student implements People{
	@Value("8888")
	private Integer id;
	@Value("学生")
	private String name;
	public Student(Integer id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	public Student() {
		super();
	}
	
	public Integer getId() {
		return id;
	}
	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + "]";
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

}

  • 创建Teacher类
package com.zj.ioc.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Teacher implements People{
	@Value("1001")
	private Integer id;
	@Value("李四")
	private String name;
	public Teacher(Integer id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	public Teacher() {
		super();
	}
	
	@Override
	public String toString() {
		return "Teacher [id=" + id + ", name=" + name + "]";
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

}

  • 创建Clazz类
package com.zj.ioc.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Clazz {
	@Autowired
	private Teacher student;

	@Override
	public String toString() {
		return "Clazz [person=" + student + "]";
	}
}

  • 创建测试类
package com.zj.ioc.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Demo {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		Clazz p = ac.getBean("clazz",Clazz.class);
		System.out.println(p);
	}
}

首先我们完成上述步骤,在这里我首先在Clazz类中使用@Autowired注入一个老师对象,

	@Autowired
	private Teacher student;

我们注意看一下,这个名为student的对象他是Teacher类的对象,而我们在创建Student类的时候也将Student类生成的bean交给Spring管理(bean的默认名为student),那现在有个问题就是@Autowired它是先byTarget(bean类型)还是byName(bean名字)完成依赖注入呢?

五月 13, 2020 1:49:05 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7e6cbb7a: startup date [Wed May 13 13:49:05 CST 2020]; root of context hierarchy
五月 13, 2020 1:49:05 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
Clazz [person=Teacher [id=1001, name=李四]]

从上面运行结果可以看出它是先寻找到对应类型的bean注入,那又又一个问题了,如果有多个类型满足条件@Autowired会怎么处理呢?
我们将Clazz代码改一下

	@Autowired
	private People student;

这里Teacher和Student都实现了People接口,意味着Spring容器中有两个bean,一个名为teacher的Teacher类的bean,还有一个名为student的Student类的bean

五月 13, 2020 2:09:20 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7e6cbb7a: startup date [Wed May 13 14:09:20 CST 2020]; root of context hierarchy
五月 13, 2020 2:09:20 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
Clazz [person=Student [id=8888, name=学生]]

从上面结果看出,如果存在多个bean的类型满足条件,那@Autowired会根据bean的id进行寻找。那如果根据name也没找到,那就会报错了喔!就像下面这样
修改Clazz中的代码

@Autowired
	private People people;
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.zj.ioc.demo.People' available: expected single matching bean but found 2: student,teacher

报错为找到的bean不唯一。那这就涉及Bean的歧义性问题。这一篇有写

  1. 我们可以修改Student或者Teacher中某一个的代码,添加@Primary注解
@Component
@Primary
public class Student implements People{...}

修改之后再来看一下运行效果:

五月 13, 2020 2:23:11 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7e6cbb7a: startup date [Wed May 13 14:23:11 CST 2020]; root of context hierarchy
五月 13, 2020 2:23:12 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
Clazz [person=Student [id=8888, name=学生]]

可以看到成功运行,它找到的bean是我们加上@Primary注解的Student的bean。

  1. 我们可以使用限定注解@Qualifier,我们修改Clazz中代码:
	@Autowired
	@Qualifier("student")
	private People people;

运行效果如下:

五月 13, 2020 2:26:58 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7e6cbb7a: startup date [Wed May 13 14:26:57 CST 2020]; root of context hierarchy
五月 13, 2020 2:26:58 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
Clazz [person=Student [id=8888, name=学生]]

从上面的运行结果看出,这种方法同样可以解决歧义性。
当然我们也可以自定义限定注解:

package com.zj.ioc.demo;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

@Target({ElementType.FIELD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Qualifier
public @interface QualifierStudent {
}

修改Student中的代码

@Component
@QualifierStudent
public class Student implements People{...}

修改Clazz中的代码

	@Autowired
	@QualifierStudent
	private People people;

运行结果如下:

五月 13, 2020 2:40:34 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7e6cbb7a: startup date [Wed May 13 14:40:34 CST 2020]; root of context hierarchy
五月 13, 2020 2:40:34 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
Clazz [person=Student [id=8888, name=李四]]

可以看到我们同样可以解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值