Robin
Robin

Reputation: 615

Spring boot autowiring BeanCreationException for static field

I am having an issue when I am autowiring my properties class.

The following is my properties class which behaves fine when I autowire it in my @service class.

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);
    }

but when I autowire it in my SQLConstants class (which only has static constant variables) I am getting an exception.

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");

The exception I get is the following:

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

EDIT:

I took the static final reference out but I am still getting a null pointer exception.

@Autowired
private SMSHistoryProperties prop;

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

The nullpointer is occurring on prop.getProperty("sms.schema").

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'SMSController': Unsatisfied dependency expressed through field 'smsBuilder'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'SMSBuilder': Unsatisfied dependency expressed through field 'searchHelper'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'smsJdbcSearchHelper': Unsatisfied dependency expressed through field 'cccDao'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'smsSearchDAOImpl' defined in file [..\SmsSearchDAOImpl.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.vzw.onem.search.dao.impl.SmsSearchDAOImpl]: Constructor threw exception; nested exception is java.lang.NullPointerException

Upvotes: 1

Views: 2527

Answers (2)

davidxxx
davidxxx

Reputation: 131496

You could not make your static fields with initializer that depend on a spring bean as beans are initialized only after the instantiation of the class by Spring while the static field is initialized before any instantiation of the class.

Instead make these fields instance fields and use @PostConstruct to init constant values after the dependency injection was done.

@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"); 
   }
}

Consequently, you could not keep them final field. But you can guarantee that these be not updated from client classes by not providing setters for them. Which may be an acceptable workaround.

An alternative 1, inject directly the values in the String fields such as :

private final String schema;   

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

or :

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

The field injection way is shorter but makes the class API less testable without a Spring container and the field cannot be final either.

An alternative 2 (my preference for your use case) make SMSHistoryProperties less generic by providing methods for each value that you need to retrieve. It eliminate the needs to declare String constants and it will make your code more meaningful.
You could also unit test each retrieval method to ensure no regression issues if properties are changed.

@Component
@RefreshScope
public class SMSHistoryProperties {

    @Autowired
    Environment env;

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

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

Upvotes: 4

Amit Bera
Amit Bera

Reputation: 7235

You can't Autowired static field in Spring. So, Your SMSHistoryProperties prop will set to null. And you are using prop to call getProperty("sms.schema"); and obviously, it will give you NullPointerException.

Upvotes: 2

Related Questions