wandermonk
wandermonk

Reputation: 7356

Spring Boot @Autowired Environment throws NullPointerException

I have a simple SpringBoot application in which i am using the Environment.class to access the properties under application.properties file. The Environment bean works when it is accessed in the main method of the Application.class

@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = "com.cisco.sdp.cdx.consumers")
public class StreamingConsumerApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(StreamingConsumerApplication.class, args);
        Environment env = context.getBean(Environment.class);
        StreamingConsumerFactory factory = context.getBean(StreamingConsumerFactory.class);
        StreamingConsumer streamingConsumer = factory.createStreamingConsumer(StreamType.valueOf(env.getRequiredProperty("streaming.application.type")));
        streamingConsumer.consume();
    }
}

When the same is used in a different class it throws NullPointerException. I tried annotating the class with @Configuration,@Component,@Repository,@Service annotations but did not work.

I tried @Autowired as well as @Resource annotations. But, it didn't work.

@Component 
public class InventoryStreamingConsumer implements StreamingConsumer {

    @Autowired
    private Environment env;
    @Autowired
    private JavaSparkSessionSingleton sparksession;
    @Autowired
    private StreamingContext _CONTEXT;
    private final Map<String, String> kafkaParams = new HashMap<String, String>();

    @Override
    public void consume() {
        if(env == null) {
            System.out.println("ENV is NULL");
        }
        System.out.println(env.getRequiredProperty("kafka.brokerlist"));
        kafkaParams.put("metadata.broker.list", env.getRequiredProperty("kafka.brokerlist"));
        Set<String> topics = Collections.singleton(env.getRequiredProperty("kafka.topic"));

    // Unrelated code.
}

I tried following the answers provided in the below questions

Spring Boot - Environment @Autowired throws NullPointerException

Autowired Environment is null

I am looking for suggestions on solving the issue.

Upvotes: 1

Views: 9216

Answers (4)

Atul
Atul

Reputation: 3357

I have similar issue but read the properties from different file and different location like common/jdbc.properties. I solved this issue by doing this:

import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;

@Configuration
@PropertySource(value = {"classpath:common/jdbc.properties"})
public class ExternalConfig implements EnvironmentAware {

private Environment environment;

public void setEnvironment(Environment environment) {
    this.environment = environment;
}

@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
    return new PropertySourcesPlaceholderConfigurer();
}

public String getJdbcUrl() {
    return environment.getProperty("jdbc.url");
}
}

Upvotes: 1

John
John

Reputation: 1704

The @Configuration annotation is misused here for InventoryStreamingConsumer. Try @Component, @Repository or @Service.


UPDATE

Another misuse is

StreamingConsumer streamingConsumer = factory.createStreamingConsumer(StreamType.valueOf(env.getRequiredProperty("streaming.application.type")));

@Autowired or @Resource can only work in bean created by Spring. the streamingConsumer created by your StreamingConsumerFactory factory cannot use @Autowired for injection of its properties.

You should create an @Configuration class, to tell Spring to create streamingConsumer from your factory. Like this

@Configuration
public class ConsumerCreator {

    @Autowired
    StreamingConsumerFactory factory;

    @Bean
    public StreamingConsumer streamingConsumer() {
        return factory.createStreamingConsumer(StreamType.valueOf(env.getRequiredProperty("streaming.application.type")));
    }
}

And use no annotation for InventoryStreamingConsumer, meanwhile use

        StreamingConsumer streamingConsumer = context.getBean(StreamingConsumer.class);

in your StreamingConsumerApplication.main() method instead to retrieve streamingConsumer

Upvotes: 2

abstractKarshit
abstractKarshit

Reputation: 1465

First, please annotate the main class with only @SpringBootApplication

@SpringBootApplication
public class StreamingConsumerApplication {
}

@ComponentScan is required if your packages are not within the same structure as main class with main class being outside the sub-package and inside parent package with every other class in the same or some sub-package of parent package.

Second, Please create a Configuration class and annotate it with @Configuration separately and define a @Bean there for StreamingConsumer streamingConsumer and than it can be @Autowired or injected in the InventoryStreamingConsumer class.

Third, where is the @Bean for JavaSparkSessionSingleton defined? Are you sure it can be AutoConfigured for injection

Fourth, InventoryStreamingConsumer can be a @Component, injecting Environment with @Autowiring will work once the above things are sorted.

Also, recommending to change your class to this for the sake depending on how consume() method is used.

@Component 
public class InventoryStreamingConsumer implements StreamingConsumer {

private final Environment env;

private final JavaSparkSessionSingleton sparksession;

private final StreamingContext _CONTEXT;

private final Map<String, String> kafkaParams = new HashMap<String, String>();

@Autowired
public InventoryStreamingConsumer(Environment env, JavaSparkSessionSingleton sparkSession, StreamingContext context) {
    this.env = env;
    this.sparksession = sparkSession;
    this._CONTEXT = context;
}

@Override
public void consume() {
    if(env == null) {
        System.out.println("ENV is NULL");
    }
    System.out.println(env.getRequiredProperty("kafka.brokerlist"));
    kafkaParams.put("metadata.broker.list", env.getRequiredProperty("kafka.brokerlist"));
    Set<String> topics = Collections.singleton(env.getRequiredProperty("kafka.topic"));

// Unrelated code.
}

Upvotes: 1

MyTwoCents
MyTwoCents

Reputation: 7624

Try adding

@PropertySource("classpath:application.properties")

on InventoryStreamingConsumer class

This is how am using it

@Configuration 
@ComponentScan({ "com.spring.config" })
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
public class HibernateConfiguration {


    private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
    private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
    private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
    private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
    private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
    private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";

    @Autowired
    private Environment env;

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
        dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
        dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
        dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
        return dataSource;
    }

Upvotes: 0

Related Questions