写点什么

@Autowired 注解 -【Spring 底层原理

  • 2022 年 4 月 17 日
  • 本文字数:3281 字

    阅读完需:约 11 分钟

  • @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%的技术点,在这里我将这份导图分享出来,以及为金九银十准备的一整套面试体系,上到集合,下到分布式微服务






用户头像

还未添加个人签名 2022.04.13 加入

还未添加个人简介

评论

发布
暂无评论
@Autowired注解 -【Spring底层原理_Java_爱好编程进阶_InfoQ写作平台