@Autowired 注解 -【Spring 底层原理
@Autowired:默认按类型装配,如果我们想使用按名称装配,可以结合 @Qualifier 注解一起使用(Spring 提供)
@Qualifier():指定装配的 bean,多个实例时可以结合 @Autowired 一起使用
@Primary:自动装配时当出现多个 bean 候选者时,被注解为
@Primary
的 bean 将作为首选者
@Resource:默认按名称装配,当找不到与名称匹配的 bean 才会按类型装配(不支持
@Primary
和@Autowired(required = false)
功能,JDK 提供)
@Inject:需要导入 javax.inject 的包,和 Autowired 功能一样,但是没有
required=false
功能(JDK 提供)
【2】自动装配
Spring 利用依赖注入(DI)完成对 IOC 容器中各个组件的依赖关系赋值
@Autowired
自动注入(Spring 提供的):
默认优先按照去容器中找对应的组件:applicationContext.getBean()
如果找到多个相同类型的组件,再将属性的名称作为组件的 ID 去容器中查找
@Qualifier()注解:该注解指定需要装配的组件 ID,而不是使用属性名
自动装配默认必须要对属性赋值,没有就会报错,可以使用
@Autowired(required = false)
指定非必须就不会报错@Primary 注解:自动装配时当出现多个 bean 候选者时,被注解为
@Primary
的 bean 将作为首选者,否则将抛出异常,如果使用了@Qualifier()
指定装配的 bean,则还是使用明确指定装配的 bean
@Resource(JSR250)和 @Inject(JSR330)(JDK 提供的)
@Resource:
默认按照组件名称进行装配,也可以指定名称进行装配
当找不到与名称匹配的 bean 会按类型装配
不支持
@Primary
和@Autowired(required = false)
功能如果同时指定了 name 和 type,则从 Spring 上下文中找到唯一匹配的 bean 进行装配,找不到则抛出异常。
如果指定了 name,则从上下文中查找名称(id)匹配的 bean 进行装配,找不到则抛出异常。
如果指定了 type,则从上下文中找到类似匹配的唯一 bean 进行装配,找不到或是找到多个,都会抛出异常。
如果既没有指定 name,又没有指定 type,则自动按照 byName 方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
Java 开源项目【ali1024.coding.net/public/P7/Java/git】@Inject:
需要导入 javax.inject 的包,和 Autowired 功能一样,但是没有
required=false
功能
【3】@Autowired 和 @Resource 注解的区别
@Autowired 由 Spring 提供,只按照 byType 注入;@Resource 由 J2EE 提供,默认按照 byName 自动注入,当找不到与名称匹配的 bean 会按类型装配
@Autowired 默认按类型装配,默认情况下必须要求依赖对象存在,如果要允许 null 值,可以设置它的 required 属性为 false。如果想使用名称装配可以结合 @Qualifier 注解进行使用。
@Resource,默认按照名称进行装配,名称可以通过 name 属性进行指定,如果没有指定 name 属性,当注解写在字段上时,默认取字段名进行名称查找。如果注解写在 setter 方法上默认取属性名进行装配。当找不到与名称匹配的 bean 时才按照类型进行装配。但是需要注意的是,如果 name 属性一旦指定,就只会按照名称进行装配。
二、实例分析
这里只对 @Autowired 注解标注在属性位置进行实例分析
【1】@Autowired 注解
// 启动类
@Test
public void TestMain() {
// 创建 IOC 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
System.out.println("userService:" + userService);
}
// Service
@Service
public class UserService {
@Autowired(required = false) // 指定非必须
@Qualifier("userDao2") // 指定装配 bean
private UserDao userDao;
@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
'}';
}
}
// Dao
@Repository
public class UserDao {
private String label = "1";
public void setLabel(String label) {《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 this.label = label;
}
@Override
public String toString() {
return "UserDao{" +
"label='" + label + ''' +
'}';
}
}
// 配置类
@Configuration
@ComponentScan({"dao","service","controller"})
public class AppConfig {
@Primary // 首选装配 bean
@Bean("userDao2")
public UserDao userDao(){
UserDao userDao = new UserDao();
userDao.setLabel("2");
return userDao;
}
}
输出结果如下,由于上面使用@Qualifier("userDao2")
指定了要装配的 bean,所以这里输出的是 label=’2‘:
如果将
@Qualifier("userDao2")
改为@Qualifier("userDao")
,则装配的是label=’1‘
【2】@Resource 注解
@Service
public class UserService {
@Resource(name = "userDao2",type = UserDao.class)
private UserDao userDao;
@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
'}';
}
}
默认按照组件名称进行装配,也可以指定名称进行装配
当找不到与名称匹配的 bean 会按类型装配
不支持
@Primary
和@Autowired(required = false)
功能
如果同时指定了 name 和 type,则从 Spring 上下文中找到唯一匹配的 bean 进行装配,找不到则抛出异常。
如果指定了 name,则从上下文中查找名称(id)匹配的 bean 进行装配,找不到则抛出异常。
如果指定了 type,则从上下文中找到类似匹配的唯一 bean 进行装配,找不到或是找到多个,都会抛出异常。
如果既没有指定 name,又没有指定 type,则自动按照 byName 方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
三、源码追踪
这里对 @Autowired 注解底层进行源码分析
参考:[https://blog.csdn.net/topdeveloperr/article/details/87971446](()
@Autowired 是用来装配 bean 的,肯定和 bean 的实例化有关,先经过了 refresh 方法,在 finishBeanFactoryInitialization 方法中 getBean,然后走 getObject 的时候触发 bean 的初始化。bean 的初始化是一个很复杂地方,在 AbstractAutowireCapableBeanFactory#doCreateBean 方法中,先创建一个 BeanWrapper,它的内部成员变量 wrappedObject 中存放的就是实例化的 MyService 对象,[Spring Bean 的生命周期源码详解 - 【Spring 底层原理】]((),再往后进入 populateBean 方法进行属性注入
Spring 对 autowire 注解的实现逻辑位于类:AutowiredAnnotationBeanPostProcessor#postProcessProperties
之中,——>findAutowiringMetadata——>buildAutowiringMetadata,核心代码就在 buildAutowiringMetadata 方法里面
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
} else {
List<InjectedElement> elements = new ArrayList();
// 需要处理的目标类
Class targetClass = clazz;
do {
List<InjectedElement> currElements = new ArrayList();
// 通过反射获取该类所有的字段,并遍历每一个字段,并通过方法 findAutowiredAnnotation 遍历每一个字段的所用注解,并如果用 autowired 修饰了,则返回 auotowired 相关属性
ReflectionUtils.doWithLocalFields(targetClass, (field) -> {
MergedAnnotation<?> ann = this.findAutowiredAnnotation(field);
if (ann != null) {
// 校验 autowired 注解是否用在了 static 方法上
if (Modifier.isStatic(field.getModifiers())) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// 判断是否指定了 required
boolean required = this.determineRequiredStatus(ann);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required));
最后
做任何事情都要用心,要非常关注细节。看起来不起眼的、繁琐的工作做透了会有意想不到的价值。当然要想成为一个技术大牛也需要一定的思想格局,思想决定未来你要往哪个方向去走, 建议多看一些人生规划方面的书籍,多学习名人的思想格局,未来你的路会走的更远。
更多的技术点思维导图我已经做了一个整理,涵盖了当下互联网最流行 99%的技术点,在这里我将这份导图分享出来,以及为金九银十准备的一整套面试体系,上到集合,下到分布式微服务
评论