静态字段的 Spring Boot 自动装配 BeanCreationException

Posted

技术标签:

【中文标题】静态字段的 Spring Boot 自动装配 BeanCreationException【英文标题】:Spring boot autowiring BeanCreationException for static field 【发布时间】:2018-12-19 21:22:31 【问题描述】:

我在自动装配属性类时遇到问题。

以下是我的属性类,当我在 @service 类中自动装配它时表现良好。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@Component
@RefreshScope
public class SMSHistoryProperties 

    @Autowired
    Environment env;

    /**
     * Return the property value associated with the given key, or null if the
     * key cannot be resolved.
     * 
     * @param propName
     * @return
     */ 
    public String getProperty(String propName)     
        return env.getProperty(propName);
    

但是当我在我的 SQLConstants 类(只有静态常量变量)中自动装配它时,我遇到了一个异常。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.vzw.onem.search.properties.SMSHistoryProperties;

@Component
public class SQLConstants 

    @Autowired
    private static SMSHistoryProperties prop;

    //~ Static fields/initializers ------------------------------------------

    private static final String SMS_SCHEMA = prop.getProperty("sms.schema");

我得到的异常如下:

org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'SQLConstants' defined in file
[...\SQLConstants.class]: Instantiation of bean failed; nested
exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [com.search.constants.SQLConstants]: Constructor
threw exception; nested exception is java.lang.NullPointerException

编辑:

我取出了静态最终引用,但仍然出现空指针异常。

@Autowired
private SMSHistoryProperties prop;

private String BATCH_SMS_SQL = "SELECT ACK_CODE FROM "
            + prop.getProperty("sms.schema");

nullpointer 发生在 prop.getProperty("sms.schema")

org.springframework.beans.factory.UnsatisfiedDependencyException: 创建名为“SMSController”的 bean 时出错:不满足的依赖关系 通过字段“smsBuilder”表示;嵌套异常是 org.springframework.beans.factory.UnsatisfiedDependencyException: 创建名为“SMSBuilder”的 bean 时出错:不满足的依赖关系 通过字段“searchHelper”表示;嵌套异常是 org.springframework.beans.factory.UnsatisfiedDependencyException: 创建名为“smsJdbcSearchHelper”的 bean 时出错:不满意 通过字段 'cccDao' 表达的依赖关系;嵌套异常是 org.springframework.beans.factory.BeanCreationException:错误 创建文件中定义的名称为“smsSearchDAOImpl”的bean [..\SmsSearchDAOImpl.class]:bean 实例化失败;嵌套的 例外是 org.springframework.beans.BeanInstantiationException: 无法实例化 [com.vzw.onem.search.dao.impl.SmsSearchDAOImpl]: 构造函数抛出异常;嵌套异常是 java.lang.NullPointerException

【问题讨论】:

你能发布更多的堆栈跟踪吗?以及相关代码 您最好将自动装配的项目作为构造函数参数提供(更不会出错),在这种情况下,只需使用 @Value("$sms.schema") String schema 作为构造函数参数,而不是手动将其从环境中拉出. @chrylis 但是把它从环境中拉出来更理想不是吗?如果您使用 Value 注释来执行此操作,那么您将使用属性注释淹没您的应用程序 @Robin 您已经在您的getProperty 调用中使用相同的信息“泛滥”了您的应用程序,您只需将其移得更远并使其难以覆盖(例如,用于测试)。最好明确地公开“bean foo 需要这两个值”,而不是说“bean foo 获取 env 并从中提取一些未指定的信息”。 @chrylis 我可能误解了你,你能详细说明一下。如果我坚持使用环境方法,那么我将只需要使用getProperty(value),而不是使用值注释的字段变量。为什么环境方式不如使用值注释显式调用它理想。 【参考方案1】:

您不能使用依赖于 Spring bean 的初始化程序来创建 static 字段,因为 bean 仅在 Spring 实例化类之后初始化,而 static 字段在类的任何实例化之前初始化。

在依赖注入完成后,将这些字段设为实例字段并使用@PostConstruct 来初始化常量值。

@Component
public class SQLConstants 

   @Autowired
   private SMSHistoryProperties prop;

   //~ Static fields/initializers ------------------------------------------

   private String SMS_SCHEMA; 

   @PostConstruct
   public void init()
        SMS_SCHEMA = prop.getProperty("sms.schema"); 
   

因此,您无法保留它们 final 字段。但是您可以通过不为它们提供设置器来保证这些不会从客户端类更新。这可能是一个可接受的解决方法。

另一种选择1,直接在String字段中注入值,例如:

private final String schema;   

public SQLConstants(@Value("$sms.schema")String schema)
    this.schema = schema;

或:

@Value("$sms.schema")
private String schema;   

字段注入方式较短,但在没有 Spring 容器的情况下会使类 API 的可测试性降低,并且字段也不能是 final

另一种选择 2(我对您的用例的偏好)通过为您需要检索的每个值提供方法来使 SMSHistoryProperties 不那么通用。它消除了声明字符串常量的需要,它将使您的代码更有意义。 您还可以对每种检索方法进行单元测试,以确保在更改属性时不会出现回归问题。

@Component
@RefreshScope
public class SMSHistoryProperties 

    @Autowired
    Environment env;

    public String getSchema()     
        return env.getProperty("sms.schema");
    

    public String getOtherValue()     
        return env.getProperty("sms.otherValue");
    

【讨论】:

谢谢,我很可能会按照您在底部的建议使用吸气剂 实际上,即使我删除了静态引用,我仍然会得到空指针。 FWIW,您不能将final 与字段注入一起使用(更喜欢构造函数的另一个原因)。 @chrylis 完全正确(我不确定)。感谢您的改进。 @Robin 我认为您可能还有另一个问题。 NullPointerException 是指源代码中的同一行吗?如果是这种情况,请确保您发布的正是您的实际代码。否则异常指的是哪个语句?如果有意义,您可以发布到 pastebin.com。【参考方案2】:

您不能在Spring 中使用Autowired 静态字段。因此,您的 SMSHistoryProperties prop 将设置为 null。而您正在使用prop 调用getProperty("sms.schema");,显然,它会给您NullPointerException

【讨论】:

即使我删除了静态引用,我仍然得到空指针。

以上是关于静态字段的 Spring Boot 自动装配 BeanCreationException的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot 自动装配

Spring boot 自动装配

Spring Boot 自动装配定义与自定义starter原理,及如何实现自定义装配

Spring Boot 自动装配定义与自定义starter原理,及如何实现自定义装配

Spring Boot核心特性之组件自动装配

关于 Spring Boot 自动装配你知道多少?