Question Pourquoi mon champ Spring @Autowired est-il nul?


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

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

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

Bean de service devant être auto-alimenté MileageFeeCalculator mais ce n'est pas:

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

Quand j'essaie de GET /mileage/3, Je reçois 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)
    ...

436
2017-11-11 00:05


origine


Réponses:


Le champ annoté @Autowired est null parce que le printemps ne sait pas à propos de la copie de MileageFeeCalculator que vous avez créé avec new et ne savait pas l'autowire.

Le conteneur Inversion de contrôle de printemps (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 leur injecte des dépendances en faisant correspondre les dépendances avec les beans dans le contexte, et un solveur de dépendances pouvant examiner une configuration de plusieurs beans et déterminez comment les instancier et les configurer dans l'ordre requis.

Le conteneur IoC n'est pas magique, et il n'a aucun moyen de connaître les objets Java, à moins que vous ne l'ayez en quelque sorte informé. Quand vous appelez new, la machine virtuelle Java instancie une copie du nouvel objet et vous la transmet directement - elle ne passe jamais par le processus de configuration. Il y a trois façons de configurer vos beans.

J'ai posté tout ce code, en utilisant Spring Boot pour lancer, à ce projet GitHub; vous pouvez regarder un projet complet 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 est de laisser le printemps autowire tous vos haricots; cela nécessite le moins de code et est le plus maintenable. Pour faire le travail autowiring comme vous vouliez, autowire également le MileageFeeCalculator comme ça:

@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 de service pour différentes requêtes, vous pouvez toujours utiliser l'injection en utilisant les étendues de haricots de printemps.

Tag qui fonctionne en injectant le @MileageFeeCalculator objet de service: working-inject-bean

Utiliser @Configurable

Si vous avez vraiment besoin d'objets créés avec new pour être automatique, vous pouvez utilise le printemps @Configurable annotation 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 qu'il est créé afin que Spring puisse configurer la nouvelle instance. Cela nécessite un peu de configuration dans votre construction (comme la compilation 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 newinstances de vos entités pour obtenir les informations de persistance nécessaires injectées.

@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 bean: non recommandé

Cette approche ne convient que pour l'interfaçage avec le code existant dans des situations spéciales. Il est presque toujours préférable de créer une classe d'adaptateurs singleton que Spring peut générer automatiquement et que le code hérité peut appeler, mais il est possible de demander directement le contexte d'application Spring pour un bean.

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

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

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


500
2017-11-11 00:05



Si vous ne codez pas une application Web, assurez-vous que votre classe dans laquelle @Autowiring est effectuée est un haricot de printemps. En règle générale, le conteneur de printemps ne sera pas au courant de la classe que nous pourrions penser comme une fève de printemps. Nous devons informer le conteneur de printemps de nos cours de printemps.

Cela peut être réalisé en configurant dans appln-contxt ou la meilleure façon est d'annoter classe comme @Composant et s'il vous plaît ne créez pas la classe annotée en utilisant un nouvel opérateur. Assurez-vous de l'avoir dans 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();

    }

}

45
2018-04-22 06:47



J'ai déjà rencontré le même problème quand je n'étais pas habitué à the life in the IoC world. le @Autowired le champ de l'un de mes beans est nul lors de l'exécution.

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


21
2017-11-12 13:41



En fait, vous devez utiliser des objets gérés par JVM ou des objets gérés 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 qui possède un objet filaire automatique.

MileageFeeCalculator calc = new MileageFeeCalculator();

donc ça ne marchera pas comme ça.

La solution rend ce MileageFeeCalculator comme un objet filaire automatique 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);
    }
}

19
2017-07-26 09:25



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

MileageFeeCalculator calc = new MileageFeeCalculator();

Avec annotation @Service, @Component, @Configuration les haricots sont créés dans le
    contexte d'application de Spring au démarrage du serveur. Mais quand nous créons des objets     en utilisant new operator, l'objet n'est pas enregistré dans le contexte d'application déjà créé. Par exemple, la 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());
        }
    }
}

}

17
2017-10-08 09:17



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

Je fais le printemps injecter applicationContext dans ce haricot:

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 principale de l'application si vous le souhaitez.

D'autres classes peuvent l'utiliser comme ceci:

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

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


7
2018-05-14 12:44



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

Nous avons utilisé @Inject au lieu de @Autowired qui est javaee standard pris en charge par Spring. À tous les endroits, cela fonctionnait bien et les haricots étaient injectés correctement, au lieu d'un seul endroit. L'injection de haricot semble la même

@Inject
Calculator myCalculator

Enfin, nous avons constaté que l'erreur était que nous (en fait, la fonction complète automatique Eclipse) importé com.opensymphony.xwork2.Inject au lieu de javax.inject.Inject !

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


7
2018-06-11 15:15



Je pense que vous avez manqué d'instruire Spring pour analyser les classes avec une annotation.

Vous pouvez utiliser @ComponentScan("packageToScan") sur la classe de configuration de votre application de ressort pour indiquer au ressort de numériser.

@Service, @Component Les annotations etc. ajoutent une description méta.
 

Spring injecte uniquement les instances de ces classes qui sont créées en tant que bean ou marquées avec des annotations.

Les classes marquées avec l'annotation doivent être identifiées au printemps avant l'injection, @ComponentScan demander au printemps de chercher les classes marquées d'une annotation. Quand le printemps trouve @Autowired il recherche le bean associé et injecte l'instance requise.

Ajouter uniquement des annotations, ne pas corriger ou faciliter l'injection de dépendance, Spring doit savoir où chercher.


3
2018-01-10 18:00



Une autre solution serait de lancer un appel: SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
Au constructeur MileageFeeCalculator comme ceci:

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

2
2018-04-21 11:35