web-dev-qa-db-fra.com

Pourquoi mon champ Spring @Autowired est-il nul?

Remarque: Ceci est destiné à être une réponse canonique à un problème commun.

J'ai une classe @Service de printemps (MileageFeeCalculator) qui a un champ @Autowired (rateService), mais le champ est null lorsque j'essaie de l'utiliser. Les journaux indiquent que le bean MileageFeeCalculator et le bean MileageRateService sont en cours de création, mais je reçois un NullPointerException chaque fois que j'essaie d'appeler la méthode mileageCharge sur mon bean service. Pourquoi Spring n’autorise-t-il pas le câblage automatique?

Classe de contrôleur:

@Controller
public class MileageFeeController {    
    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        MileageFeeCalculator calc = new MileageFeeCalculator();
        return calc.mileageCharge(miles);
    }
}

Classe de service:

@Service
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService; // <--- should be autowired, is null

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile()); // <--- throws NPE
    }
}

Le haricot de service qui devrait être câblé automatiquement dans MileageFeeCalculator mais ce n'est pas:

@Service
public class MileageRateService {
    public float ratePerMile() {
        return 0.565f;
    }
}

Lorsque j'essaie de GET /mileage/3, j'obtiens cette exception:

Java.lang.NullPointerException: null
    at com.chrylis.example.spring_autowired_npe.MileageFeeCalculator.mileageCharge(MileageFeeCalculator.Java:13)
    at com.chrylis.example.spring_autowired_npe.MileageFeeController.mileageFee(MileageFeeController.Java:14)
    ...
497
chrylis

Le champ annoté @Autowired est null car Spring ne connaît pas la copie de MileageFeeCalculator que vous avez créée avec new et ne savait pas la transférer automatiquement.

Le conteneur Spring Inversion of Control (IoC) comporte trois composants logiques principaux: un registre (appelé ApplicationContext) des composants (beans) disponibles pour être utilisés par l'application, un système de configuration qui injecte les dépendances des objets dans en comparant les dépendances avec les beans dans le contexte et un résolveur de dépendance qui peut examiner une configuration de nombreux beans différents et déterminer comment les instancier et les configurer dans l'ordre nécessaire.

Le conteneur IoC n'est pas magique et il n'a aucun moyen de connaître les objets Java à moins que vous ne l'informiez d'une manière ou d'une autre. Lorsque vous appelez new, la machine virtuelle Java instancie une copie du nouvel objet et vous la remet directement. Elle ne passe jamais par le processus de configuration. Vous pouvez configurer vos beans de trois manières différentes.

J'ai posté tout ce code, en utilisant Spring Boot pour lancer, à ce projet GitHub ; vous pouvez consulter un projet en cours d'exécution pour chaque approche afin de voir tout ce dont vous avez besoin pour le faire fonctionner. Tag avec le NullPointerException: nonworking

Injectez vos haricots

L'option la plus préférable consiste à laisser Spring relier automatiquement tous vos haricots; Cela nécessite le moins de code possible et est le plus facile à gérer. Pour que le câblage automatique fonctionne comme vous le souhaitez, connectez également le MileageFeeCalculator comme ceci:

@Controller
public class MileageFeeController {

    @Autowired
    private MileageFeeCalculator calc;

    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        return calc.mileageCharge(miles);
    }
}

Si vous devez créer une nouvelle instance de votre objet service pour différentes demandes, vous pouvez toujours utiliser l'injection à l'aide de les portées du bean Spring .

Tag qui fonctionne en injectant l'objet service @MileageFeeCalculator: working-inject-bean

Utilisez @Configurable

Si vous avez vraiment besoin que les objets créés avec new soient automatiquement connectés, vous pouvez utiliser l'annotation Spring @Configurable avec le tissage à la compilation AspectJ pour injecter vos objets. Cette approche insère du code dans le constructeur de votre objet qui avertit Spring que celui-ci est créé afin que Spring puisse configurer la nouvelle instance. Cela nécessite un peu de configuration dans votre construction (compiler avec ajc) et activer les gestionnaires de configuration d'exécution de Spring (@EnableSpringConfigured avec la syntaxe JavaConfig). Cette approche est utilisée par le système Roo Active Record pour permettre aux instances new de vos entités de recevoir les informations de persistance nécessaires.

@Service
@Configurable
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService;

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile());
    }
}

Tag qui fonctionne en utilisant @Configurable sur l'objet de service: working-configurable

Recherche manuelle de haricot: non recommandé

Cette approche ne convient que pour l'interface avec le code existant dans des situations spéciales. Il est presque toujours préférable de créer une classe d'adaptateur singleton que Spring puisse autoriser et que le code hérité puisse appeler, mais il est possible de demander directement le contexte de l'application Spring pour un bean.

Pour ce faire, vous avez besoin d’une classe à laquelle Spring peut donner une référence à l’objet ApplicationContext:

@Component
public class ApplicationContextHolder implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;   
    }

    public static ApplicationContext getContext() {
        return context;
    }
}

Ensuite, votre code hérité peut appeler getContext() et récupérer les beans dont il a besoin:

@Controller
public class MileageFeeController {    
    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        MileageFeeCalculator calc = ApplicationContextHolder.getContext().getBean(MileageFeeCalculator.class);
        return calc.mileageCharge(miles);
    }
}

Tag qui fonctionne en recherchant manuellement l'objet de service dans le contexte Spring: working-manual-lookup

543
chrylis

Si vous ne codez pas une application Web, assurez-vous que votre classe dans laquelle @Autowiring est effectuée est un haricot printanier. En règle générale, les contenants de printemps ne connaissent pas la classe que nous pourrions considérer comme un haricot de printemps. Nous devons informer le conteneur Spring de nos cours de printemps.

Ceci peut être réalisé en configurant dans appln-contxt ou le meilleur moyen est d’annoter la classe en tant que @Component et de ne pas créer la classe annotée avec un nouvel opérateur ..__ Appln-context comme ci-dessous.

@Component
public class MyDemo {


    @Autowired
    private MyService  myService; 

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
            System.out.println("test");
            ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
            System.out.println("ctx>>"+ctx);

            Customer c1=null;
            MyDemo myDemo=ctx.getBean(MyDemo.class);
            System.out.println(myDemo);
            myDemo.callService(ctx);


    }

    public void callService(ApplicationContext ctx) {
        // TODO Auto-generated method stub
        System.out.println("---callService---");
        System.out.println(myService);
        myService.callMydao();

    }

}
49
Shirish Coolkarni

En fait, vous devez utiliser des objets gérés par JVM ou un objet géré par Spring pour appeler des méthodes . À partir du code ci-dessus dans votre classe de contrôleur, vous créez un nouvel objet pour appeler votre classe de service comportant un objet auto-câblé.

MileageFeeCalculator calc = new MileageFeeCalculator();

donc ça ne marchera pas comme ça. 

La solution permet à MileageFeeCalculator de devenir un objet auto-câblé dans le contrôleur lui-même.

Changez votre classe de contrôleur comme ci-dessous.

@Controller
public class MileageFeeController {

    @Autowired
    MileageFeeCalculator calc;  

    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        return calc.mileageCharge(miles);
    }
}
29
Ravi Durairaj

Une fois, j'ai rencontré le même problème alors que je n'étais pas tout à fait habitué à the life in the IoC world. Le champ @Autowired de l'un de mes beans est null au moment de l'exécution.

La cause principale est qu'au lieu d'utiliser le bean créé automatiquement et maintenu par le conteneur Spring IoC (dont le champ @Autowired est indeed correctement injecté), je suis newing ma propre instance de ce type de bean et je l'utilise. Bien sûr, le champ @Autowired de celui-ci est nul car Spring n'a aucune chance de l'injecter.

21
smwikipedia

Votre problème est nouveau (création d'objet en style Java)

MileageFeeCalculator calc = new MileageFeeCalculator();

Avec les annotations @Service, @Component, @Configuration, des beans sont créés dans le
contexte d'application de Spring lorsque le serveur est démarré. Mais quand on crée des objets En utilisant l'opérateur new, l'objet n'est pas enregistré dans le contexte d'application déjà créé. Par exemple, classe Employee.Java que j’ai utilisée.

Regarde ça:

public class ConfiguredTenantScopedBeanProcessor implements BeanFactoryPostProcessor {

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    String name = "tenant";
    System.out.println("Bean factory post processor is initialized"); 
    beanFactory.registerScope("employee", new Employee());

    Assert.state(beanFactory instanceof BeanDefinitionRegistry,
            "BeanFactory was not a BeanDefinitionRegistry, so CustomScope cannot be used.");
    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

    for (String beanName : beanFactory.getBeanDefinitionNames()) {
        BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
        if (name.equals(definition.getScope())) {
            BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(definition, beanName), registry, true);
            registry.registerBeanDefinition(beanName, proxyHolder.getBeanDefinition());
        }
    }
}

}
18
Deepak

Il semble que ce soit un cas rare mais voici ce qui m'est arrivé:

Nous avons utilisé @Inject au lieu de @Autowired qui est la norme Java prise en charge par Spring. Tous les endroits ont bien fonctionné et les haricots ont été injectés correctement, au lieu d’un seul endroit. L'injection de haricot semble la même

@Inject
Calculator myCalculator

Enfin, nous avons découvert que l’erreur était que nous (en fait, la fonction de complétion automatique d’Eclipse) importait com.opensymphony.xwork2.Inject au lieu de javax.inject.Inject!

Donc, pour résumer, assurez-vous que vos annotations (@Autowired, @Inject, @Service, ...) ont les packages corrects!

9
Alireza Fattahi

Je suis nouveau à Spring, mais j'ai découvert cette solution de travail. S'il vous plaît dites-moi si c'est une façon déconseillée.

Je fais printemps injecter applicationContext dans cette fève:

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

@Component
public class SpringUtils {

    public static ApplicationContext ctx;

    /**
     * Make Spring inject the application context
     * and save it on a static variable,
     * so that it can be accessed from any point in the application. 
     */
    @Autowired
    private void setApplicationContext(ApplicationContext applicationContext) {
        ctx = applicationContext;       
    }
}

Vous pouvez également mettre ce code dans la classe d'application principale si vous le souhaitez.

D'autres classes peuvent l'utiliser comme ceci:

MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);

De cette manière n'importe quel haricot peut être obtenu par n'importe quel objet de l'application (également avec new) et de manière statique .

7
bluish

Une autre solution serait de passer l’appel: SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
Pour le constructeur MileageFeeCalculator, procédez comme suit:

@Service
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService; // <--- will be autowired when constructor is called

    public MileageFeeCalculator() {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
    }

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile()); 
    }
}
4
Ondrej Bozek

Je pense que vous avez manqué d'instruire Spring à scanner les classes avec des annotations. 

Vous pouvez utiliser @ComponentScan("packageToScan") sur la classe de configuration de votre application Spring pour demander à Spring d'analyser.

@Service, @Component etc annotations add meta description .

Spring n'injecte que les occurrences des classes créées en tant que bean ou marquées avec une annotation.

Les classes marquées avec une annotation doivent être identifiées par un ressort avant l'injection. @ComponentScan instruire un printemps recherche les classes marquées avec une annotation. Lorsque Spring trouve @Autowired, il recherche le bean associé et injecte l'instance requise.

L'ajout d'une annotation uniquement ne corrige ni ne facilite l'injection de dépendance. Spring doit savoir où chercher.

4
msucil

Si cela se produit dans une classe de test, assurez-vous de ne pas avoir oublié d'annoter la classe.

Par exemple, dans Spring Boot :

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
    ....
3
nobar

Vous pouvez également résoudre ce problème en utilisant l'annotation @Service sur la classe de service et en transmettant la classe A du bean requise en tant que paramètre au constructeur des classes B des autres beans et annotez le constructeur de la classe B avec @Autowired. Extrait extrait ici:

@Service
public class ClassB {

    private ClassA classA;

    @Autowired
    public ClassB(ClassA classA) {
        this.classA = classA;
    }

    public void useClassAObjectHere(){
        classA.callMethodOnObjectA();
    }
}
2
apandey846

UPDATE: Les personnes très intelligentes ont rapidement indiqué la this réponse, ce qui explique l'étrangeté décrite ci-dessous.

RÉPONSE ORIGINALE:

Je ne sais pas si cela peut aider qui que ce soit, mais je me suis retrouvé confronté au même problème, même si je faisais les choses apparemment bien. Dans ma méthode principale, j'ai un code comme celui-ci:

ApplicationContext context =
    new ClassPathXmlApplicationContext(new String[] {
        "common.xml",
        "token.xml",
        "pep-config.xml" });
    TokenInitializer ti = context.getBean(TokenInitializer.class);

et dans un fichier token.xml j'ai eu une ligne

<context:component-scan base-package="package.path"/>

J'ai remarqué que le package.path n'existe plus, alors je viens de laisser tomber la ligne pour de bon. 

Et après cela, NPE a commencé à arriver. Dans un pep-config.xml, je n'avais que 2 haricots: 

<bean id="someAbac" class="com.pep.SomeAbac" init-method="init"/>
<bean id="settings" class="com.pep.Settings"/>

et la classe SomeAbac a une propriété déclarée comme

@Autowired private Settings settings;

pour une raison inconnue, les paramètres sont null dans init (), lorsque l'élément <context:component-scan/> n'est pas présent du tout, mais lorsqu'il est présent et qu'il contient des bs comme basePackage, tout fonctionne correctement. Cette ligne ressemble maintenant à ceci: 

<context:component-scan base-package="some.shit"/>

et il fonctionne. Peut-être que quelqu'un peut fournir une explication, mais pour moi c'est suffisant maintenant)

2
62mkv

Notez également que si, pour une raison quelconque, vous créez une méthode dans un @Service sous la forme final, les beans autochargés auxquels vous aurez accès seront toujours null.

0
yglodt

Ce qui n'a pas été mentionné ici est décrit dans l'article this du paragraphe "Ordre d'exécution".

Après avoir "appris" que je devais annoter une classe avec @Component ou les dérivés @Service ou @Repository (je suppose qu'il y en a plus), pour autoriser le câblage d'autres composants à l'intérieur d'eux, il m'a semblé que ces autres composants étaient toujours nuls au constructeur. du composant parent.

Utiliser @PostConstruct résout les problèmes suivants:

@SpringBootApplication
public class Application {
    @Autowired MyComponent comp;
}

et:

@Component
public class MyComponent {
    @Autowired ComponentDAO dao;

    public MyComponent() {
        // dao is null here
    }

    @PostConstruct
    public void init() {
        // dao is initialized here
    }
}
0
JackLeEmmerdeur

C'est le coupable d'avoir donné NullPointerException MileageFeeCalculator calc = new MileageFeeCalculator(); Nous utilisons Spring - il n'est pas nécessaire de créer l'objet manuellement. La création d'objet sera effectuée par conteneur IoC.

0
Atul Jain