Metody wstrzykiwania zależności w Springu
Metody wstrzykiwania zależności w Springu
Według dokumentacji Springa – http://docs.spring.io/spring/docs/5.0.3.RELEASE/spring-framework-reference/core.html#beans-factory-collaborators są trzy metody wstrzykiwania zależności:
- przez konstruktor,
- przez pola,
- przez metody typu setter.
Dla celów edukacyjnych utworzymy serwis:
public interface Printer { String print(String text); }
oraz implementacje:
@Service public class PrinterImpl implements Printer { @Override public String print(String message) { return "[log]" + message; } }
RestController – wstrzykiwanie przez konstruktor – pola inicjowane w momencie kiedy tworzony jest obiekt klasy:
@RestController public class AppInjectionManagerByConstructorRestController { private final Printer printer; @Autowired public AppInjectionManagerByConstructorRestController(Printer printer) { this.printer = printer; } @GetMapping(path="/printerInjectedByConstructor") @ResponseBody public String getPrinterInjectedByConstructor() { return printer.print("printer injected by constructor"); } }
Wynik:
[log]printer injected by constructor
w zasadzie można pominąć adnotacje @Autowired, właściwość Printer printer powinna być finalna co wynika z dobrych praktyk programowania!
RestController – wstrzykiwanie przez pola – nie jest to rekomendowana praktyka, wstrzykiwanie odbywa się poprzez refleksje co utrudnia testowanie aplikacji uzależniając klasę od użytego kontenera IoC:
@RestController public class AppInjectionManagerByFieldRestController { @Autowired private Printer printer; @GetMapping(path="/printerInjectedByField") @ResponseBody public String getPrinterInjectedByField() { return printer.print("printer injected by field"); } }
Wynik:
[log]printer injected by field
RestController – wstrzykiwanie przez metodę typu setter – wstrzykiwanie przez kontener IoC odbywa się po utworzeniu obiektu z użyciem konstruktora bezparametrowego:
@RestController public class AppInjectionManagerBySetterRestController { private Printer printer; @Autowired public void setPrinter(Printer printer) { this.printer = printer; } @GetMapping(path="/printerInjectedBySetter") @ResponseBody public String getPrinterInjectedBySetter() { return printer.print("printer injected by setter"); } }
[log]printer injected by setter
Adnotacja @Autowired w tym przypadku jest wymagana.
Pisząc o wstrzykiwaniu zależności należy również wspomnieć o adnotacji @Lookup. W Springu domyślny zasięg Beanów to Singleton. A co jeśli zależy nam na tym, aby w beanach o zasięgu Singleton posiadać Beana o zasięgu prototype (inna instancja zwracana przez kontener IoC)? Do tego celu należy użyć wstrzykiwania z użyciem adnotacji @Lookup!
@Component("printerPrototypeImpl") @Scope("prototype") public class PrinterPrototypeImpl implements Printer { @Override public String print(String message) { return "[log] " + message; } }
@RestController public class AppInjectionManagerByLookupRestController { @Lookup("printerPrototypeImpl") public Printer getPrinter() { return null; } @GetMapping(path = "/printerInjectedByLookup") @ResponseBody public String getPrinterInjectedByField() { return getPrinter().print("printer injected by field") + " " + (getPrinter() == getPrinter()); } }
Wynik:
[log] printer injected by lookup false
Należy zwrócić uwagę, że dodana została nowa implementacja interfejsu Printer. Konieczna jest zatem modyfikacja poprzednich przykładów – trzeba dodać adnotację @Qualifier aby uniknąć niejednoznaczności podczas wstrzykiwania zależności. Po modyfikacji przykłady przedstawiają się następująco:
@Service("printerImpl") public class PrinterImpl implements Printer { @Override public String print(String message) { return "[log] " + message; } }
@RestController public class AppInjectionManagerByConstructorRestController { private final Printer printer; public AppInjectionManagerByConstructorRestController( @Qualifier("printerImpl") Printer printer) { this.printer = printer; } @GetMapping(path="/printerInjectedByConstructor") @ResponseBody public String getPrinterInjectedByConstructor() { return printer.print("printer injected by constructor"); } }
@RestController public class AppInjectionManagerByFieldRestController { @Autowired @Qualifier("printerImpl") private Printer printer; @GetMapping(path="/printerInjectedByField") @ResponseBody public String getPrinterInjectedByField() { return printer.print("printer injected by field"); } }
@RestController public class AppInjectionManagerBySetterRestController { private Printer printer; @Qualifier("printerImpl") @Autowired public void setPrinter(Printer printer) { this.printer = printer; } @GetMapping(path="/printerInjectedBySetter") @ResponseBody public String getPrinterInjectedBySetter() { return printer.print("printer injected by setter"); } }
Leave a comment