web-dev-qa-db-fra.com

Spring Boot - Environment @Autowired lève NullPointerException

J'ai une configuration de projet utilisant Spring Boot 0.5.0.M5.

Dans l'un des fichiers de configuration, j'essaie de @Autowire Environment mais cela échoue avec un NullPointerException.

Voici ce que j'ai jusqu'à présent:

Application.Java

@EnableAutoConfiguration
@Configuration
@ComponentScan
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

JpaConfig.Java où j'essaie de @Autowire Environment

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.ui.persistence.repository")
public class JpaConfig {
    private static final String DATABASE_DRIVER = "db.driver";
    private static final String DATABASE_PASSWORD = "db.password";
    private static final String DATABASE_URL = "db.url";
    private static final String DATABASE_USERNAME = "db.username";
    private static final String HIBERNATE_DIALECT = "hibernate.dialect";
    private static final String HIBERNATE_SHOW_SQL = "hibernate.show_sql";
    private static final String ENTITYMANAGER_PACKAGES_TO_SCAN 
        = "entitymanager.packages.to.scan";

    @Autowired
    private Environment env;

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty(DATABASE_DRIVER));
        dataSource.setUrl(env.getProperty(DATABASE_URL));
        dataSource.setUsername(env.getProperty(DATABASE_USERNAME));
        dataSource.setPassword(env.getProperty(DATABASE_PASSWORD));
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean 
                = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setPersistenceProviderClass(
                HibernatePersistence.class);
        entityManagerFactoryBean.setPackagesToScan(
                env.getProperty(ENTITYMANAGER_PACKAGES_TO_SCAN));
        entityManagerFactoryBean.setJpaProperties(hibernateProperties());
        return entityManagerFactoryBean;
    }
}

J'essaie de charger les propriétés de la base de données configurées dans un fichier de propriétés. Cependant, le Environment n'est pas injecté et le code échoue avec NullPointerException. Je n'ai aucune configuration dans les fichiers XML.

Pour le fichier de propriétés, j'ai configuré PropertySourcesPlaceholderConfigurer de cette façon:

@Configuration
@PropertySource("classpath:database.properties")
public class PropertyConfig {
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}

J'ai essayé d'échanger @Autowired, @Resource et @Inject mais rien n'a fonctionné jusqu'à présent. J'apprécierais toute aide. Merci.

15
imme

Je crois qu'il y avait des problèmes de cycle de vie avec Spring et le EntityManagerFactory, et vous pourriez en être tombé (corrigé dans 4.0.0.RC1) - si votre @Configuration la classe est instanciée très tôt, elle pourrait ne pas être éligible pour le câblage automatique. Vous pouvez probablement dire à partir de la sortie du journal si c'est le cas.

Juste par intérêt, saviez-vous que les fonctionnalités fournies par vos JpaConfig et PropertyConfig sont déjà préréglées prédéfinies si vous utilisez @EnableAutoConfiguration (tant que tu @ComponentScan ce package où vos référentiels sont définis)? Voir exemple JPA dans Spring Boot pour un exemple.

6
Dave Syer

Bien que votre problème spécifique soit résolu, voici comment obtenir Environment au cas où le câblage automatique de Spring arriverait trop tard.

L'astuce consiste à implémenter org.springframework.context.EnvironmentAware; Spring transmet ensuite l'environnement à la méthode setEnvironment(). Cela fonctionne depuis le printemps 3.1.

Un exemple:

@Configuration
@PropertySource("classpath:myProperties.properties")
public class MyConfiguration implements EnvironmentAware {

    private Environment environment;

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

    public void myMethod() {
        final String myPropertyValue = environment.getProperty("myProperty");
        // ...
    }

}

Ce n'est pas aussi élégant que @Autowired Ou @Value, Mais cela fonctionne comme solution de contournement dans certaines situations.

23
Alex Shesterov

J'avais le même problème pour lire les propriétés de mon fichier application.properties dans l'application Spring Boot. J'ai eu beaucoup de mal à comprendre le problème et à le faire fonctionner. Enfin je l'ai fait. Voici ma classe Constants qui lira les valeurs des propriétés du fichier de propriétés. J'espère que cela aidera quelqu'un.

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("classpath:application.properties")
public class Constants implements EnvironmentAware {

static Environment environment;

@Override
public void setEnvironment(Environment environment) {
    Constants.environment = environment;
}

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

public static String getActiveMQHost() {
    System.out.println(environment.getProperty("spring.activemq.broker-Host"));
    return environment.getProperty("spring.activemq.broker-Host");
}

public static String getActiveMQPort() {
    System.out.println(environment.getProperty("spring.activemq.broker-port"));
    return environment.getProperty("spring.activemq.broker-port");
}

public static String getActiveMQUser() {
    System.out.println(environment.getProperty("spring.activemq.user"));
    return environment.getProperty("spring.activemq.user");
}

public static String getActiveMQPassword() {
    System.out.println(environment.getProperty("spring.activemq.password"));
    return environment.getProperty("spring.activemq.password");
}

}

Ce sont les clés de propriété déclarées dans mon application.properties,

spring.activemq.broker-Host
spring.activemq.broker-port
spring.activemq.user
spring.activemq.password
2
Nallamachu

J'ai eu le même problème sur Spring Batch. Les écrivains ne peuvent pas câbler automatiquement la classe d'environnement car la classe de configuration a été instanciée plus tôt. J'ai donc créé une sorte de Singleton (à l'ancienne) pour instancier l'Environnement et je pouvais y accéder à chaque fois.

J'ai fait cette implémentation:

@Configuration
@PropertySource(value = { "classpath:kid-batch.properties" }, ignoreResourceNotFound = false)
public class BatchConfiguration implements EnvironmentAware {

private static Environment env;

public static String getProperty(String key) {
    return env.getProperty(key);
}

@Override
public void setEnvironment(Environment env) {
    BatchConfiguration.env = env;
}

}

Et il fonctionne

2
Greg Florance