Remarque: Il s'agit d'une réponse canonique à un problème courant.
J'ai une @Service
classe Spring ( MileageFeeCalculator
) qui a un @Autowired
champ ( rateService
), mais le champ est null
lorsque j'essaie de l'utiliser. Les journaux montrent que le MileageFeeCalculator
bean et le MileageRateService
bean sont en cours de création, mais j'obtiens un NullPointerException
chaque fois que j'essaie d'appeler la mileageCharge
méthode sur mon bean de service. Pourquoi Spring ne fait-il pas le câblage automatique sur le terrain?
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 qui devrait être câblé automatiquement MileageFeeCalculator
mais ce n'est pas le cas:
@Service
public class MileageRateService {
public float ratePerMile() {
return 0.565f;
}
}
Quand j'essaye 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)
...
F
est appelé à l'intérieur du constructeur d'un autre beanS
. Dans ce cas, passez le bean requis enF
tant que paramètre à l'autreS
constructeur de beans et annotez le constructeur deS
with@Autowire
. N'oubliez pas d'annoter la classe du premier beanF
avec@Component
.Réponses:
Le champ annoté
@Autowired
estnull
dû au fait que Spring ne connaît pas la copie deMileageFeeCalculator
celle que vous avez créée avecnew
et ne savait pas comment la câbler automatiquement.Le conteneur Spring Inversion of Control (IoC) comprend trois composants logiques principaux: un registre (appelé le
ApplicationContext
) de composants (beans) disponibles pour être utilisés par l'application, un système de configuration qui leur injecte les dépendances des objets en faisant correspondre le dépendances avec des beans dans le contexte, et un 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, sauf si vous en informez-les d'une manière ou d'une autre. Lorsque vous appelez
new
, la JVM instancie une copie du nouvel objet et vous la remet directement - elle ne passe jamais par le processus de configuration. Il existe trois façons de configurer vos beans.J'ai publié tout ce code, en utilisant Spring Boot pour lancer, sur ce projet GitHub ; vous pouvez consulter un projet en cours d'exécution pour chaque approche pour voir tout ce dont vous avez besoin pour le faire fonctionner. Tag avec le
NullPointerException
:nonworking
Injectez vos grains
L'option la plus préférable est de laisser Spring auto-câbler tous vos grains; cela nécessite le moins de code et est le plus facile à gérer. Pour que le câblage automatique fonctionne comme vous le vouliez, procédez également au câblage automatique
MileageFeeCalculator
comme ceci:Si vous devez créer une nouvelle instance de votre objet de service pour différentes demandes, vous pouvez toujours utiliser l'injection en utilisant les étendues de bean Spring .
Balise qui fonctionne en injectant l'
@MileageFeeCalculator
objet de service:working-inject-bean
Utilisez @Configurable
Si vous avez vraiment besoin que les objets créés avec
new
soient câblés automatiquement, vous pouvez utiliser l'@Configurable
annotation Spring avec le tissage au moment de 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 en cours de création afin que Spring puisse configurer la nouvelle instance. Cela nécessite un peu de configuration dans votre build (comme la compilation avecajc
) et l'activation des 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 auxnew
instances de vos entités d'obtenir les informations de persistance nécessaires injectées.Balise 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 du code hérité dans des situations particulières. Il est presque toujours préférable de créer une classe d'adaptateur singleton que Spring peut câbler automatiquement et le code hérité peut appeler, mais il est possible de demander directement au contexte de l'application Spring un bean.
Pour ce faire, vous avez besoin d'une classe à laquelle Spring peut donner une référence à l'
ApplicationContext
objet:Ensuite, votre code hérité peut appeler
getContext()
et récupérer les beans dont il a besoin:Balise qui fonctionne en recherchant manuellement l'objet de service dans le contexte Spring:
working-manual-lookup
la source
@Configuration
bean, où la méthode pour créer une instance d'une classe de bean particulière est annotée@Bean
.@Bean
méthodes lors de l'utilisation de Spring Proxy AOP?@Autowired MileageFeeCalculator calc
. Des pensées?ApplicationContext
. Certains utilisateurs (pour lesquels j'ai fermé comme doublons) ne comprennent pas cela.Si vous ne codez pas une application Web, assurez-vous que votre classe dans laquelle @Autowiring est effectué est un bean à ressort. En règle générale, le conteneur de printemps ne connaîtra pas la classe que nous pourrions considérer comme un haricot de printemps. Nous devons parler au conteneur Spring de nos cours de printemps.
Ceci peut être réalisé en configurant dans appln-contxt ou la meilleure façon est d'annoter la classe en tant que @Component et veuillez ne pas créer la classe annotée en utilisant un nouvel opérateur. Assurez-vous de l'obtenir à partir du contexte Appln comme ci-dessous.
la source
En fait, vous devez utiliser des objets gérés JVM ou des objets gérés par Spring pour appeler des méthodes. à partir de votre code ci-dessus dans votre classe de contrôleur, vous créez un nouvel objet pour appeler votre classe de service qui a un objet auto-câblé.
donc ça ne marchera pas comme ça.
La solution fait de ce MileageFeeCalculator un objet auto-câblé dans le contrôleur lui-même.
Modifiez votre classe de contrôleur comme ci-dessous.
la source
J'ai rencontré une fois le même problème quand je n'étais pas tout à fait habitué
the life in the IoC world
. Le@Autowired
champ de l'un de mes beans est nul au moment de l'exécution.La cause principale est qu'au lieu d'utiliser le bean créé automatiquement maintenu par le conteneur Spring IoC (dont le
@Autowired
champ estindeed
correctement injecté), je suisnewing
ma propre instance de ce type de bean et je l'utilise. Bien sûr, le@Autowired
champ de celui-ci est nul car Spring n'a aucune chance de l'injecter.la source
Votre problème est nouveau (création d'objet en style java)
Avec l' annotation
@Service
,@Component
, les@Configuration
haricots sont créés dans lecadre de l' application du printemps lorsque le serveur est démarré. Mais lorsque nous créons des objets à l'aide d'un nouvel opérateur, l'objet n'est pas enregistré dans le contexte d'application qui est déjà créé. Pour l'exemple de classe Employee.java que j'ai utilisé.
Regarde ça:
la source
Je suis nouveau sur Spring, mais j'ai découvert cette solution de travail. S'il vous plaît, dites-moi si c'est une manière dépréciable.
Je fais Spring injecter
applicationContext
dans ce bean:Vous pouvez également placer ce code dans la classe d'application principale si vous le souhaitez.
D'autres classes peuvent l'utiliser comme ceci:
De cette façon, n'importe quel bean peut être obtenu par n'importe quel objet dans l'application (également initié avec
new
) et de manière statique .la source
Cela semble être un cas rare mais voici ce qui m'est arrivé:
Nous avons utilisé
@Inject
au lieu de@Autowired
cela la norme javaee prise en charge par Spring. À tous les endroits, cela a bien fonctionné et les grains ont été injectés correctement, au lieu d'un seul endroit. L'injection de haricot semble la mêmeEnfin, nous avons constaté que l'erreur était que nous (en fait, la fonction de saisie automatique Eclipse) importions
com.opensymphony.xwork2.Inject
au lieu dejavax.inject.Inject
!Donc , pour résumer, assurez - vous que vos annotations (
@Autowired
,@Inject
,@Service
, ...) ont des paquets corrects!la source
Je pense que vous avez manqué de demander au printemps de scanner les cours avec annotation.
Vous pouvez utiliser
@ComponentScan("packageToScan")
sur la classe de configuration de votre application Spring pour demander au printemps de scanner.@Service, @Component
etc les annotations ajoutent une méta description.Spring injecte uniquement des instances de ces classes qui sont soit créées en tant que bean, soit marquées d'annotations.
Les classes marquées avec annotation doivent être identifiées par ressort avant l'injection,
@ComponentScan
demandez au printemps de rechercher les classes marquées avec annotation. Lorsque Spring le trouve,@Autowired
il recherche le bean associé et injecte l'instance requise.L'ajout d'annotations uniquement, ne corrige pas ou ne facilite pas l'injection de dépendances, Spring doit savoir où chercher.
la source
<context:component-scan base-package="com.mypackage"/>
à monbeans.xml
fichierSi cela se produit dans une classe de test, assurez-vous que vous n'avez pas oublié d'annoter la classe.
Par exemple, dans Spring Boot :
Il s'écoule du temps ...
Spring Boot continue d'évoluer . Il n'est plus nécessaire de l'utiliser
@RunWith
si vous utilisez la bonne version de JUnit .Pour
@SpringBootTest
travailler de manière autonome, vous devez utiliser à@Test
partir de JUnit5 au lieu de JUnit4 .Si vous obtenez ce mal de configuration de vos tests compilerons, mais
@Autowired
et les@Value
champs (par exemple) serontnull
. Étant donné que Spring Boot fonctionne par magie, vous pouvez avoir peu de possibilités de déboguer directement cet échec.la source
@Value
sera nul lorsqu'il est utilisé avec desstatic
champs.Une autre solution serait de mettre l'appel:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
au constructeur MileageFeeCalculator comme ceci:
la source
MISE À JOUR: Les gens vraiment intelligents ont rapidement indiqué cette réponse, ce qui explique l'étrangeté, décrite ci-dessous
RÉPONSE ORIGINALE:
Je ne sais pas si cela aide quelqu'un, mais j'étais coincé avec le même problème même en faisant les choses en apparence. Dans ma méthode Main, j'ai un code comme celui-ci:
et dans un
token.xml
fichier j'ai eu une ligneJ'ai remarqué que le package.path n'existe plus, donc je viens de laisser tomber la ligne pour de bon.
Et après cela, NPE a commencé à entrer. Dans un
pep-config.xml
je n'avais que 2 haricots:et la classe SomeAbac a une propriété déclarée comme
pour une raison inconnue, les paramètres sont nuls dans init (), lorsque l'
<context:component-scan/>
élément n'est pas présent du tout, mais lorsqu'il est présent et a des bs comme basePackage, tout fonctionne bien. Cette ligne ressemble maintenant à ceci:et il fonctionne. Peut-être que quelqu'un peut fournir une explication, mais pour moi, c'est suffisant en ce moment)
la source
<context:component-scan/>
permet implicitement<context:annotation-config/>
nécessaire@Autowired
au fonctionnement.C'est le coupable de donner NullPointerException
MileageFeeCalculator calc = new MileageFeeCalculator();
Nous utilisons Spring - pas besoin de créer un objet manuellement. La création d'objets sera prise en charge par le conteneur IoC.la source
Vous pouvez également résoudre ce problème en utilisant l'annotation @Service sur la classe de service et en transmettant le bean classA requis en tant que paramètre à l'autre constructeur de classe B de beans et en annotant le constructeur de classB avec @Autowired. Exemple d'extrait ici:
la source
Ce qui n'a pas été mentionné ici est décrit dans cet article au 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 câbler automatiquement d'autres composants à l'intérieur, j'ai été frappé par le fait que ces autres composants étaient toujours nuls à l'intérieur du constructeur du composant parent.
L'utilisation de @PostConstruct résout ce qui suit:
et:
la source
Ceci n'est valable qu'en cas de test unitaire.
Ma classe de service avait une annotation de service et c'était
@autowired
une autre classe de composants. Lorsque j'ai testé la classe de composants était devenue nulle. Parce que pour la classe de service, je créais l'objet en utilisantnew
Si vous écrivez un test unitaire, assurez-vous que vous ne créez pas d'objet à l'aide de
new object()
. Utilisez plutôt injectMock.Cela a résolu mon problème. Voici un lien utile
la source
Notez également que si, pour une raison quelconque, vous créez une méthode dans un
@Service
asfinal
, les beans câblés automatiquement auxquels vous accéderez seront toujoursnull
.la source
En termes simples, il y a principalement deux raisons pour qu'un
@Autowired
champ soitnull
VOTRE CLASSE N'EST PAS UN HARICOT DE PRINTEMPS.
LE CHAMP N'EST PAS UN HARICOT.
la source
Pas entièrement lié à la question, mais si l'injection de champ est nulle, l'injection basée sur le constructeur fonctionnera toujours correctement.
la source