作者:京东科技 韩国凯
通过本文,读者可以0源码基础的初步学习spring源码,并能够举一反三从此进入源码世界的大米!
由于是第一次阅读源码,文章之中难免存在一些问题,还望包涵指正!
一、 @Autowired与@Resource的区别
用一句话总结两者的区别就是: @Autowired会先通过类型注入,即byType,当存在有多个类型时会通过名称注入。@Resource则相反,会先通过名称注入,即byName,当名称不存在或有多个名称时会通过类型注入。
那么通过名称注入与通过类型注入有什么区别呢?
//创建接口
interface StuService{
String getName();
}
@Service
//Stu2实现接口并注册bean
class Stu2 implements StuService{
@Override
public String getName() {
return "stu2";
}
}
@Service
//Stu3实现接口并注册bean
class Stu3 implements StuService{
@Override
public String getName() {
return "stu3";
}
}
1.1 @Autowired
那么此时如果我们对 StuService
注入, @Autowired可以选择注入的类型就有两个,分别是 Stu2
与 Stu3
。
需要注意的是,类型有很多种选择:
- 当注册bean与获取bean为同一个类时,类型只有这个类本身。
例如,我们有获取session的工具类,需要将其注入到spring之中,
@Component
class SessionUtil{
public String getSession(){
return "session";
}
}
只有一个类,直接注册bean,使用时可以任意选择
@Autowired
SessionUtil sessionUtil;
此时@Autowired只有一个注册类型,直接注入。
- 当注册bean有多个时,类型为所有注册的bean,实现方式有:实现接口、继承、通过其他方式,例如xml配置注册bean。
例如上述 StuService
有多个实现类,每个实现类都注册了bean,因此@Autowired可以选择的类型就有两个。
@Autowired
StuService stu;
根据上述的@Autowired逻辑,此时有多个类型,那么会根据bean name查找,(即类名首字母小写的),发现 stu
没有对应的实现类,
此时会报错:
Field stu in com.example.demo.spring.Stu1 required a single bean, but 2 were found:
只需要将 stu
替换成 stu2
或 stu3
即可完成注入。
继承和其他方式同时有多个bean注入时同理。
因此,@Autowired中类型的定义可以归结为:当注册bean有多个时,类型为所有注册的bean,实现方式有:实现接口、继承、通过其他方式,例如xml配置注册bean或者@Bean注册。
1.2 @Resource
- 当只有一个bean时,可以直接注册
@Autowired
SessionUtil sessionUtil;
- 当有多个bean注册时,如果未指定名称,则bean name为类名首字母小写,指定了bean名称则注册名称为该名称。
例如上文中 Stu1 Stu2
都未指定bean名称,因此两者的bean名称分别为 stu1 stu2
。
当使用@Bean在方法上注册bean,此时名称为方法名称。
@Bean()
public Student getStudent(){
Student student = new Student();
student.setName("bob");
student.setId(26);
return student;
}
此时该bean名称为 getStudent
。
同样,我们也可以注册bean时自定义bean名称
@Bean("stu1")
public Student getStudent(){
Student student = new Student();
student.setName("bob");
student.setId(26);
return student;
}
@Service("stu2")
class Stu2 implements StuService{
@Override
public String getName() {
return "stu2";
}
}
@Component("stu3")
class Stu3 implements StuService{
@Override
public String getName() {
return "stu3";
}
}
在引用时指定bean:
@Resource(name = "stu2")
private StuService stu1;
1.3 @Autowired
当我们使用@Resource时,会根据名称也就是 stu2
去查询,此时bean名称只有一个,查到返回
@Resource
private Stu3 stu2;
但是在执行时却发现报错:
Bean named 'stu2' is expected to be of type 'com.example.demo.spring.Stu3' but was actually of type 'com.example.demo.spring.Stu2'
这是因为只根据了bean名称去查询,却没有根据bean类型,查到的是Stu2
类型的bean,但是期望的却是Stu3
,因此会发生类型不匹配。
二、SpringIOC的Bean注入流程
spring的注册流程主要包含两个部分:
- 容器的启动阶段及预热工作
- Bean的注入流程
先了解一下几个概念:
2.1 概念介绍
2.1.1 配置元数据
存在于磁盘上的项目中用于描述一个bean的数据,可以是xml、properties、yaml等静态文件,也可以是各种注解描述的对应信息,例如@Service、@Component描述的一个bean的信息。
<bean id="role" class="com.wbg.springxmlbean.entity.Role">
<property name="id" value="1"/>
<property name="roleName" value="高级工程师"/>
<property name="note" value="重要人员"