0302Bean属性注入注解- IOC-spring

1 概述

  • Spring属性注入使用注解的目的是简化属性注入的配置,提高开发效率和代码可读性。使用注解可以将属性注入的信息直接写在类的成员变量、setter方法或构造函数上,从而避免了繁琐的XML配置,并且可以使代码更加简洁、清晰。同时,注解可以帮助开发人员更好地理解对象之间的依赖关系,增强代码的可读性。

    在使用注解进行属性注入时,通常使用的注解包括:

    1. @Autowired:自动注入Bean对象,可以根据类型或名称进行注入。
    2. @Value:注入配置文件中的属性值。
    3. @Resource:根据名称注入Bean对象。
    4. @Inject:与@Autowired类似,可以自动注入Bean对象。
    5. @Qualifier:与@Autowired配合使用,指定Bean对象的名称。

    通过使用这些注解,开发人员可以更加便捷地进行属性注入,避免了冗长的XML配置,提高了开发效率和代码质量。同时,注解的使用也使得代码更加易于理解和维护。

2 @Value

@Value是Spring框架中的一个注解,用于将属性值注入到组件中。

通常情况下,我们需要将一些配置信息注入到应用程序中,这时可以使用@Value注解来完成这个任务。@Value注解可以将属性值注入到组件的构造方法、成员变量、Setter方法中,例如:

lessCopy code
@Component
public class MyComponent {
    
    @Value("${app.name}")
    private String appName;
    
    private int appVersion;
    
    @Autowired
    public MyComponent(@Value("${app.version}") int appVersion) {
        this.appVersion = appVersion;
    }
    
    public void setAppName(@Value("${app.name}") String appName) {
        this.appName = appName;
    }
    
    // ...
}

在上面的代码中,我们使用@Value注解将应用程序的名称和版本号注入到MyComponent组件中。其中, a p p . n a m e 和 {app.name}和 app.name{app.version}是配置文件中的属性占位符,Spring会自动将这些占位符替换为对应的属性值。

需要注意的是,@Value注解可以用于注入任意类型的属性值,包括字符串、数值、布尔值、数组、集合等。如果需要将属性值注入到集合中,可以使用SpEL表达式来表示集合的值,例如:

typescriptCopy code
@Value("#{${app.properties}}")
private Map<String, String> appProperties;

在上面的代码中,我们使用SpEL表达式将应用程序的属性列表注入到一个Map集合中。

3 @Autowired和@Qualifier

@Autowired是Spring框架中的一个注解,用于自动装配组件之间的依赖关系。默认根据类型自动注入,如果需要按照名称注入,需要@Qualifier注解配合使用。

在一个Spring应用程序中,通常会有很多组件之间存在依赖关系,这时就需要使用@Autowired注解来自动装配这些组件。@Autowired注解可以用于构造方法、成员变量、Setter方法上,例如:

typescriptCopy code
@Component
public class MyComponent {
    
    private MyDependency myDependency;
    
    @Autowired
    public MyComponent(MyDependency myDependency) {
        this.myDependency = myDependency;
    }
    
    @Autowired
    public void setMyDependency(MyDependency myDependency) {
        this.myDependency = myDependency;
    }
    
    @Autowired
    public void init(MyDependency myDependency) {
        this.myDependency = myDependency;
    }
    
    // ...
}

在上面的代码中,我们使用@Autowired注解将MyDependency组件注入到MyComponent组件中。@Autowired注解会自动扫描应用程序上下文中所有的组件,并根据类型和名称来自动装配依赖关系。如果存在多个同类型的组件,可以使用@Qualifier注解指定要装配的组件名称,例如:

kotlinCopy code
@Component
public class MyComponent {
    
    private MyDependency myDependency;
    
    @Autowired
    public MyComponent(@Qualifier("myDependency2") MyDependency myDependency) {
        this.myDependency = myDependency;
    }
    
    // ...
}

在上面的代码中,我们使用@Qualifier注解指定要装配的组件名称为myDependency2,这样就可以避免由于同类型组件的存在而导致的装配错误。

需要注意的是,@Autowired注解可以用于构造方法、Setter方法、成员变量上,但通常情况下,最好将@Autowired注解放在构造方法上,这样可以保证组件依赖关系的完整性,也可以避免在Setter方法或成员变量上出现多次装配的情况。

4 @Resource

@Resource注解是JavaEE中的注解,Spring框架也支持这个注解用于Bean的属性注入。

@Resource注解可以用来注入一个Bean或者其他的资源到一个Bean的属性中,具体使用方式如下:

  1. 通过名称注入

可以使用@Resource注解并指定名称来注入一个Bean,例如:

kotlinCopy code
@Component
public class MyComponent {
    
    @Resource(name = "myDependency")
    private MyDependency myDependency;
    
    // ...
}

在上面的代码中,我们使用@Resource注解将名称为"myDependency"的Bean注入到myDependency属性中。

  1. 通过类型注入

如果只有一个Bean类型与被注入属性类型匹配,可以省略@Resource注解中的name属性,例如:

kotlinCopy code
@Component
public class MyComponent {
    
    @Resource
    private MyDependency myDependency;
    
    // ...
}

在上面的代码中,Spring会自动查找与myDependency属性类型匹配的Bean进行注入。

  1. 通过限定符注入

如果存在多个同类型的Bean,可以使用@Resource注解的name属性和Qualifier注解的value属性来指定要注入的Bean,例如:

lessCopy code
@Component
public class MyComponent {
    
    @Resource(name = "myDependency2")
    @Qualifier("myQualifier")
    private MyDependency myDependency;
    
    // ...
}

在上面的代码中,我们使用@Resource注解指定要注入的Bean的名称为"myDependency2",并使用@Qualifier注解指定要注入的Bean的限定符为"myQualifier"。

总之,@Resource注解可以用于Bean的属性注入,可以根据具体的场景和需求来选择合适的注入方式。需要注意的是,@Resource注解需要配合JavaEE容器一起使用,不建议在纯Spring应用中使用。

5 自动注入执行

AutowiredAnnotationBeanPostProcessor是一个BeanPostProcessor,用于在Bean实例化之后、初始化之前,自动注入Bean的依赖关系。它会扫描容器中所有的Bean,检查其中是否存在@Autowired、@Resource或@Inject注解,并自动注入它们所依赖的其他Bean。

具体来说,AutowiredAnnotationBeanPostProcessor类会在Bean实例化后,执行postProcessProperties()方法,用于自动注入Bean的依赖关系。在该方法中,会遍历Bean的所有属性,检查其中是否存在@Autowired、@Resource或@Inject注解,并将其依赖的Bean注入到当前的Bean中。

当@Autowired注解出现在成员变量上时,AutowiredAnnotationBeanPostProcessor类会调用resolveAutowiredFieldValue()方法,使用类型匹配的方式来注入Bean的依赖关系。当@Autowired注解出现在setter方法上时,AutowiredAnnotationBeanPostProcessor类会调用resolveAutowiredMethod()方法,使用方法匹配的方式来注入Bean的依赖关系。

除了自动注入Bean的依赖关系,AutowiredAnnotationBeanPostProcessor类还提供了一些配置选项,如required属性、autowiredAnnotationType属性等,用于控制自动注入的行为。

总之,AutowiredAnnotationBeanPostProcessor类是Spring中常用的依赖注入的实现方式之一,它能够自动注入Bean的依赖关系,并提高代码的灵活性和可维护性。

我来看下AutowiredAnnotationBeanPostProcessor#postProcessProperties()方法,继承自InstantiationAwareBeanPostProcessor,之前我们在 ,==0105bean实例化-Bean生命周期详解-spring== 有介绍。源代码如下:

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
   InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
   try {
      metadata.inject(bean, beanName, pvs);
   }
   catch (BeanCreationException ex) {
      throw ex;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
   }
   return pvs;
}
  • findAutowiringMetadata():查找自动注入元数据
  • metadata.inject():方法底层通过反射实现,这个暂时不详述,感兴趣自行查阅源码或者相关文档。

findAutowiringMetadata()代码如下:

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
   // Fall back to class name as cache key, for backwards compatibility with custom callers.
   String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
   // Quick check on the concurrent map first, with minimal locking.
   InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
   if (InjectionMetadata.needsRefresh(metadata, clazz)) {
      synchronized (this.injectionMetadataCache) {
         metadata = this.injectionMetadataCache.get(cacheKey);
         if (InjectionMetadata.needsRefresh(metadata, clazz)) {
            if (metadata != null) {
               metadata.clear(pvs);
            }
            metadata = buildAutowiringMetadata(clazz);
            this.injectionMetadataCache.put(cacheKey, metadata);
         }
      }
   }
   return metadata;
}
  • 先从缓存获取注入元数据,默认缓存没有,执行buildAutowiringMetadata()构建注入元数据。

buildAutowiringMetadata()方法源代码如下:

private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
   if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
      return InjectionMetadata.EMPTY;
   }

   List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
   Class<?> targetClass = clazz;

   do {
      final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

      ReflectionUtils.doWithLocalFields(targetClass, field -> {
         MergedAnnotation<?> ann = findAutowiredAnnotation(field);
         if (ann != null) {
            if (Modifier.isStatic(field.getModifiers())) {
               if (logger.isInfoEnabled()) {
                  logger.info("Autowired annotation is not supported on static fields: " + field);
               }
               return;
            }
            boolean required = determineRequiredStatus(ann);
            currElements.add(new AutowiredFieldElement(field, required));
         }
      });

      ReflectionUtils.doWithLocalMethods(targetClass, method -> {
         Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
         if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
            return;
         }
         MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
         if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
            if (Modifier.isStatic(method.getModifiers())) {
               if (logger.isInfoEnabled()) {
                  logger.info("Autowired annotation is not supported on static methods: " + method);
               }
               return;
            }
            if (method.getParameterCount() == 0) {
               if (logger.isInfoEnabled()) {
                  logger.info("Autowired annotation should only be used on methods with parameters: " +
                        method);
               }
            }
            boolean required = determineRequiredStatus(ann);
            PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
            currElements.add(new AutowiredMethodElement(method, required, pd));
         }
      });

      elements.addAll(0, currElements);
      targetClass = targetClass.getSuperclass();
   }
   while (targetClass != null && targetClass != Object.class);

   return InjectionMetadata.forElements(elements, clazz);
}

这代码有点“眼熟”,是地在我们前面==0108Bean销毁-Bean生命周期详解-spring==在处理@Predestroy和PostConstruct注解的时候,使用相同的处理逻辑:

  • 分别遍历Class的字段和方法,检查是否有相关注解,加入集合构建元数据,放入缓存。

我们看下这里处理的注解类型,在构造方法中代码如下:

public AutowiredAnnotationBeanPostProcessor() {
   this.autowiredAnnotationTypes.add(Autowired.class);
   this.autowiredAnnotationTypes.add(Value.class);

   try {
      this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
            ClassUtils.forName("jakarta.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
      logger.trace("'jakarta.inject.Inject' annotation found and supported for autowiring");
   }
   catch (ClassNotFoundException ex) {
      // jakarta.inject API not available - simply skip.
   }

   try {
      this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
            ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
      logger.trace("'javax.inject.Inject' annotation found and supported for autowiring");
   }
   catch (ClassNotFoundException ex) {
      // javax.inject API not available - simply skip.
   }
}
  • 我们常用的有@Value和@Autowired

结语

如果小伙伴什么问题或者指教,欢迎交流。

❓QQ:806797785

⭐️源代码仓库地址:https://gitee.com/gaogzhen/spring6-study

参考:

[1]Spring框架视频教程[CP/OL].P76-80.

[2]ChatGPT

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaog2zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值