Le champ annoté @Autowired
est null
dû au fait que Spring ne connaît pas la copie de MileageFeeCalculator
celle que vous avez créée avec new
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:
@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 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 avec ajc
) 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 aux new
instances de vos entités d'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());
}
}
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:
@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);
}
}
Balise qui fonctionne en recherchant manuellement l'objet de service dans le contexte Spring: working-manual-lookup
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
.