Spring中的字段注入是如何实现的

Posted hardyzhou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring中的字段注入是如何实现的相关的知识,希望对你有一定的参考价值。

spring中属性的注入方式

Spring中属性注入的方式包括以下几种:

  1. Setter 方法注入:通过在 Bean 类中定义对应的属性setter方法,然后在 XML 配置文件或 Java 配置类中使用 或 @Value 注解来为这些属性设置数值。

  2. 构造函数注入:通过在 Bean 的构造函数中定义对应的参数来实现。在 XML 配置文件或 Java 配置类中使用 元素或 @Autowired 注解来指定参数的值。

  3. 字段注入:通过在 Bean 类中添加对应的字段,并添加 @Autowired 或 @Resource 等注解,让 Spring 自动将需要的依赖注入到该字段中。

  4. 接口回调注入:通过实现一个特定的接口,如 ApplicationContextAware 接口、BeanNameAware 接口等,并重写该接口中的对应方法,使得 Spring 在创建 Bean 时调用这些方法来完成依赖注入。

除了以上四种方式外,还可以通过自定义注入器来实现属性注入,用户可以自定义一个 Injector 实现类,并在 XML 配置文件或 Java 配置类中注册该实现类,以实现更加灵活的注入方式。

总的来说,Spring 中的属性注入主要就是通过 Bean 容器来管理各个 Bean 对象之间的依赖关系,而 Spring 提供的各种注入方式,都是为了提供更加灵活、方便地实现属性注入,以满足不同场景下的需求。

字段注入的原理

Spring 中的字段注入会使用到 Java 反射技术。

具体来说,在 Spring 对象创建时,会先通过构造函数或工厂方法等方式实例化 Bean 对象,然后再进行属性注入。对于字段注入,Spring 内部维护了一个 AutowiredAnnotationBeanPostProcessor 后置处理器,在对象创建完成后,会检查 Bean 中标记了 @Autowired 或 @Resource 注解的字段,并根据字段类型和名称在容器中寻找匹配的 Bean 对象,最终通过反射将依赖注入到对应的字段中。

下面是字段注入的具体源码实现:

public class AutowiredAnnotationBeanPostProcessor implements BeanPostProcessor 
    // ...

    @Nullable
    private Object autowiredProperty(
            String beanName, Object bean, PropertyDescriptor pd, @Nullable Object propertyValue) 

        // 获取当前属性的注解信息
        Method writeMethod = pd.getWriteMethod();
        Annotation[] annotations = bridge.getAnnotations(writeMethod);
        for (Annotation ann : annotations) 
            if (ann instanceof Autowired) 
                // 如果是 @Autowired 注解,则从容器中查找并注入依赖
                Object value = autowireByType(
                        beanName, bean, pd.getPropertyType(), pd.getName(), ann);
                if (value != null) 
                    propertyValue = value;
                
            

            else if (ann instanceof Inject) 
                // ...
            

            else if (ann instanceof Value) 
                // ...
            
        

        return propertyValue;
    

    @Nullable
    protected Object autowireByType(
            String beanName, Object bean, Class<?> type, String fieldName, @Nullable Annotation autowiredAnnotation) 

        // 获取 AutowireCapableBeanFactory 对象
        AutowireCapableBeanFactory factory = getAutowiredCapableBeanFactory();

        // 根据类型和名称在容器中查找 Bean 对象
        Object res = null;
        if (factory != null) 
            DependencyDescriptor desc = new AutowireUtils.TypeDependencyDescriptor(
                    type, autowiredAnnotation, true, getNonSelfDeclaredPropertiesOnly());
            res = factory.resolveDependency(desc, beanName, null, null);
        
        return res;
    

    // ...

在上述代码中,autowiredProperty 方法用于注入单个属性值,并依次遍历该属性上的各种注解信息。当检测到 @Autowired 注解时,则调用 autowireByType 方法对该属性进行自动装配。在 autowireByType 方法中,会调用 AutowireCapableBeanFactory 的 resolveDependency 方法,通过 type 和名称等信息在 Spring 容器中查找匹配的 Bean 对象,并返回对象实例。

总的来说,Spring 中的字段注入使用 Java 反射技术实现,可以更加灵活地管理对象之间的依赖关系。当然,反射也可能导致性能瓶颈,在实际开发中需要适度使用,并结合其他技术手段对其进行优化。

如何将 application.yml 中的地图注入 Spring Boot 中的字段?

【中文标题】如何将 application.yml 中的地图注入 Spring Boot 中的字段?【英文标题】:How to inject a map from application.yml into a field in Spring Boot? 【发布时间】:2020-03-01 05:00:35 【问题描述】:

这是参考这个问题。

Spring Boot - inject map from application.yml

在那个问题上,他们有

info:
   build:
      artifact: $project.artifactId
      name: $project.name
      description: $project.description
      version: $project.version

要将其映射到他们的 java 代码中,他们建议使用 @ConfigurationProperties 注释。这个注解的问题是你必须创建一个New Class 然后在它上面添加这个注解。

但是我可以像@Value 注释那样直接将它放到一个字段中吗?

在这里说我需要它:

// Someannotation or any other lean and clean way to get this
@Someannotation("$info")
private Map<String, Object> map;

上面的代码应该给我一个映射,其中键是build,值是some Object with fields like artifact, name etc(当然,如果这可行,我会为此创建一个POJO,而不是将其保留为普通对象)。

注意:@ConfigurationProperties 不适用于字段,这就是为什么我想看看是否有更精简和更清洁的方法来做到这一点。

附:我也可以使用@ConfigurationProperties 并创建一个新类,但我只是想看看我是否是missing out,以一种更简单的方式来执行此操作,而无需完全创建一个新类。

【问题讨论】:

我当然知道,所以这个问题。我想看看是否有另一种方法similar to @Value,而不必创建一个新类然后在@ConfigurationProperties 上拍打。 【参考方案1】:

我知道这样做的唯一方法是以 JSON 格式定义值部分:

app.collection.map.string.to.integer=one:"1", two:"2", three:"3"

然后注入

@Value("#$app.collection.map.string.to.integer")
private Map<String, Integer> mapStringToInteger;

来自https://relentlesscoding.com/2018/09/09/spring-basics-dynamically-inject-values-with-springs-value/ 部分 Inject Maps With Spring's @Value

【讨论】:

以上是关于Spring中的字段注入是如何实现的的主要内容,如果未能解决你的问题,请参考以下文章

Spring:如何向静态字段注入值?

spring 抽象类注入问题

Spring是如何自动注入多类型

Spring是如何自动注入多类型

Spring是如何自动注入多类型

如何将记录器注入示例 Spring Boot 应用程序的字段中?