Metody wstrzykiwania zależności w Springu


Metody wstrzykiwania zależności w Springu

Według dokumentacji Springa – https://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

Your email address will not be published.


*