Il campo annotato @Autowired
è null
perché Spring non è a conoscenza della copia MileageFeeCalculator
che hai creato new
e non sapeva di autowire.
Il contenitore Spring Inversion of Control (IoC) ha tre componenti logici principali: un registro (chiamato ApplicationContext
) di componenti (bean) disponibili per essere utilizzati dall'applicazione, un sistema configuratore che inietta in essi le dipendenze degli oggetti abbinando il dipendenze con bean nel contesto e un risolutore di dipendenze che può esaminare una configurazione di molti bean diversi e determinare come istanziarli e configurarli nell'ordine necessario.
Il contenitore IoC non è magico e non ha modo di conoscere gli oggetti Java se non lo si informa in qualche modo. Quando chiami new
, JVM crea un'istanza di una copia del nuovo oggetto e te lo passa direttamente, non passa mai attraverso il processo di configurazione. Esistono tre modi per configurare i bean.
Ho pubblicato tutto questo codice, usando Spring Boot per il lancio, in questo progetto GitHub ; puoi guardare un progetto in piena esecuzione per ogni approccio per vedere tutto il necessario per farlo funzionare. Tagga con NullPointerException
:nonworking
Iniettare i fagioli
L'opzione più preferibile è lasciare che Spring autowire tutti i tuoi bean; questo richiede la minima quantità di codice ed è il più gestibile. Per fare in modo che l'autowiring funzioni come desiderato, anche autowire in MileageFeeCalculator
questo modo:
@Controller
public class MileageFeeController {
@Autowired
private MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
Se è necessario creare una nuova istanza dell'oggetto del servizio per richieste diverse, è comunque possibile utilizzare l'iniezione utilizzando gli ambiti Spring bean .
Tag che funziona iniettando l' @MileageFeeCalculator
oggetto del servizio:working-inject-bean
Usa @Configurable
Se hai davvero bisogno degli oggetti creati con new
per essere autowired, puoi usare l' @Configurable
annotazione Spring insieme alla tessitura in fase di compilazione AspectJ per iniettare i tuoi oggetti. Questo approccio inserisce il codice nel costruttore del tuo oggetto che avvisa Spring che viene creato in modo che Spring possa configurare la nuova istanza. Ciò richiede un po 'di configurazione nella build (come la compilazione con ajc
) e l'attivazione dei gestori di configurazione runtime di Spring ( @EnableSpringConfigured
con la sintassi JavaConfig). Questo approccio viene utilizzato dal sistema Roo Active Record per consentire alle new
istanze delle entità di ottenere le informazioni di persistenza necessarie iniettate.
@Service
@Configurable
public class MileageFeeCalculator {
@Autowired
private MileageRateService rateService;
public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile());
}
}
Tag che funziona utilizzando @Configurable
l'oggetto del servizio:working-configurable
Ricerca bean manuale: non consigliata
Questo approccio è adatto solo per l'interfaccia con il codice legacy in situazioni speciali. È quasi sempre preferibile creare una classe di adattatori singleton che Spring può autowire e il codice legacy può chiamare, ma è possibile chiedere direttamente al contesto dell'applicazione Spring un bean.
Per fare ciò, è necessaria una classe a cui Spring può fornire un riferimento ApplicationContext
all'oggetto:
@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;
}
}
Quindi il tuo codice legacy può chiamare getContext()
e recuperare i bean di cui ha bisogno:
@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 che funziona cercando manualmente l'oggetto del servizio nel contesto Spring: working-manual-lookup
F
viene chiamato all'interno del costruttore di un altro beanS
. In questo caso, passare il bean richiestoF
come parametro all'altroS
costruttore di bean e annotare il costruttore diS
with@Autowire
. Ricorda di annotare la classe del primo beanF
con@Component
.