spring
Bonen maken en gebruiken
Zoeken…
Autowiring alle bonen van een specifiek type
Als u meerdere implementaties van dezelfde interface hebt, kan Spring ze allemaal automatisch in een verzamelobject omzetten. Ik ga een voorbeeld gebruiken met een Validator-patroon 1
Foaklasse:
public class Foo {
private String name;
private String emailAddress;
private String errorMessage;
/** Getters & Setters omitted **/
}
Koppel:
public interface FooValidator {
public Foo validate(Foo foo);
}
Naam Validator Class:
@Component(value="FooNameValidator")
public class FooNameValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Validation logic goes here.
}
}
E-mail Validator Class:
@Component(value="FooEmailValidator")
public class FooEmailValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Different validation logic goes here.
}
}
U kunt deze validators nu afzonderlijk of samen in een klasse automatisch doorsturen.
Koppel:
public interface FooService {
public void handleFoo(Foo foo);
}
Klasse:
@Service
public class FooServiceImpl implements FooService {
/** Autowire all classes implementing FooValidator interface**/
@Autowired
private List<FooValidator> allValidators;
@Override
public void handleFoo(Foo foo) {
/**You can use all instances from the list**/
for(FooValidator validator : allValidators) {
foo = validator.validate(foo);
}
}
}
Het is vermeldenswaard dat als je meer dan één implementatie van een interface in de Spring IoC-container hebt en niet opgeeft welke je wilt gebruiken met de annotatie @Qualifier
, Spring een uitzondering zal @Qualifier
wanneer hij probeert te starten, omdat hij heeft gewonnen ' t weet welke instantie te gebruiken.
1: Dit is niet de juiste manier om dergelijke eenvoudige validaties uit te voeren. Dit is een eenvoudig voorbeeld over autowiring. Als u een idee van een veel eenvoudiger validatiemethode wilt, kijk dan hoe Spring validatie uitvoert met annotaties.
Boon verklaren
Om een bean te declareren, annoteert u eenvoudig een methode met de @Bean
annotatie of annoteert u een klasse met de @Component
annotatie (annotaties @Service
, @Repository
, @Controller
kunnen ook worden gebruikt).
Wanneer JavaConfig een dergelijke methode tegenkomt, zal het die methode uitvoeren en de retourwaarde als een bean in een BeanFactory registreren. De boonnaam is standaard die van de methode.
We kunnen bonen maken op een van de volgende drie manieren:
Gebruik van op Java gebaseerde configuratie : in het configuratiebestand moeten we bean declareren met @bean-annotatie
@Configuration public class AppConfig { @Bean public TransferService transferService() { return new TransferServiceImpl(); } }
Met behulp van op XML gebaseerde configuratie : voor op XML gebaseerde configuratie moeten we declarate bean maken in applicatieconfiguratie XML dwz
<beans> <bean name="transferService" class="com.acme.TransferServiceImpl"/> </beans>
Annotatie-aangedreven component : Voor annotatie-aangedreven componenten moeten we de @Component-annotatie toevoegen aan de klasse die we als bean willen declareren.
@Component("transferService") public class TransferServiceImpl implements TransferService { ... }
Nu zijn alle drie de bonen met name transferService
beschikbaar in BeanFactory
of ApplicationContext
.
Standaard annotatie automatisch inschakelen
Koppel:
public interface FooService {
public int doSomething();
}
Klasse:
@Service
public class FooServiceImpl implements FooService {
@Override
public int doSomething() {
//Do some stuff here
return 0;
}
}
Opgemerkt moet worden dat een klasse een interface voor Spring moet implementeren om deze klasse automatisch te kunnen bedraden. Er is een methode om Spring in staat te stellen stand-alone klassen automatisch te laten werken met behulp van load time weaving, maar dat valt buiten het bereik van dit voorbeeld.
U kunt toegang krijgen tot deze boon in elke klasse die wordt gestart door de Spring IoC-container met behulp van de annotatie @Autowired
.
Gebruik:
@Autowired([required=true])
De @Autowired
annotatie probeert eerst automatisch te typen op type en valt dan terug op de naam van de boon in geval van onduidelijkheid.
Deze annotatie kan op verschillende manieren worden toegepast.
Constructor injectie:
public class BarClass() {
private FooService fooService
@Autowired
public BarClass(FooService fooService) {
this.fooService = fooService;
}
}
Veld injectie:
public class BarClass() {
@Autowired
private FooService fooService;
}
Zetter injectie:
public class BarClass() {
private FooService fooService;
@Autowired
public void setFooService(FooService fooService) {
this.fooService = fooService;
}
}
FactoryBean gebruiken voor dynamische boneninstantie
Om dynamisch te bepalen welke bonen moeten worden geïnjecteerd, kunnen we FactoryBean
s gebruiken. Dit zijn klassen die het patroon van de fabrieksmethode implementeren en bonen voor de container bieden. Ze worden herkend door Spring en kunnen transparant worden gebruikt, zonder dat u hoeft te weten dat de boon uit een fabriek komt. Bijvoorbeeld:
public class ExampleFactoryBean extends AbstractFactoryBean<String> {
// This method determines the type of the bean for autowiring purposes
@Override
public Class<?> getObjectType() {
return String.class;
}
// this factory method produces the actual bean
@Override
protected String createInstance() throws Exception {
// The thing you return can be defined dynamically,
// that is read from a file, database, network or just
// simply randomly generated if you wish.
return "Something from factory";
}
}
Configuratie:
@Configuration
public class ExampleConfig {
@Bean
public FactoryBean<String> fromFactory() {
return new ExampleFactoryBean();
}
}
De boon halen:
AbstractApplicationContext context = new AnnotationConfigApplicationContext(ExampleConfig.class);
String exampleString = (String) context.getBean("fromFactory");
Om de echte FactoryBean te krijgen, gebruikt u het ampersand-voorvoegsel vóór de naam van de boon:
FactoryBean<String> bean = (FactoryBean<String>) context.getBean("&fromFactory");
Houd er rekening mee dat u alleen prototype
of singleton
scopes kunt gebruiken - om de scope te wijzigen in prototype
overschrijving met de isSingleton
methode:
public class ExampleFactoryBean extends AbstractFactoryBean<String> {
@Override
public boolean isSingleton() {
return false;
}
// other methods omitted for readability reasons
}
Merk op dat scoping verwijst naar de werkelijke instanties die worden gemaakt, niet naar de fabrieksboon zelf.
Injecteer bonen met prototype-scoped
De container maakt een singleton bean en injecteert medewerkers er slechts één keer in. Dit is niet het gewenste gedrag wanneer een singletonboon een prototype-scopedbijdrager heeft, omdat de prototype-scopedboon moet worden geïnjecteerd telkens wanneer deze wordt benaderd via accessor.
Er zijn verschillende oplossingen voor dit probleem:
- Gebruik opzoekmethode injectie
- Haal een bonen met prototype-
javax.inject.Provider
viajavax.inject.Provider
- Haal een bonen met prototype-
org.springframework.beans.factory.ObjectFactory
viaorg.springframework.beans.factory.ObjectFactory
(een equivalent van # 2, maar met de klasse die specifiek is voor Spring) - Maak een singleton bean-container bewust via de implementatie van de
ApplicationContextAware
interface
Benaderingen # 3 en # 4 worden over het algemeen afgeraden, omdat ze een app sterk koppelen aan Spring-framework. Ze worden dus niet behandeld in dit voorbeeld.
Opzoekmethode injectie via XML-configuratie en een abstracte methode
Java-klassen
public class Window {
}
public abstract class WindowGenerator {
public Window generateWindow() {
Window window = createNewWindow(); // new instance for each call
...
}
protected abstract Window createNewWindow(); // lookup method
}
XML
<bean id="window" class="somepackage.Window" scope="prototype" lazy-init="true"/>
<bean id="windowGenerator" class="somepackage.WindowGenerator">
<lookup-method name="createNewWindow" bean="window"/>
</bean>
Opzoekmethode injectie via Java-configuratie en @Component
Java-klassen
public class Window {
}
@Component
public class WindowGenerator {
public Window generateWindow() {
Window window = createNewWindow(); // new instance for each call
...
}
@Lookup
protected Window createNewWindow() {
throw new UnsupportedOperationException();
}
}
Java-configuratie
@Configuration
@ComponentScan("somepackage") // package where WindowGenerator is located
public class MyConfiguration {
@Bean
@Lazy
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Window window() {
return new Window();
}
}
Handmatige opzoekmethode injectie via Java-configuratie
Java-klassen
public class Window {
}
public abstract class WindowGenerator {
public Window generateWindow() {
Window window = createNewWindow(); // new instance for each call
...
}
protected abstract Window createNewWindow(); // lookup method
}
Java-configuratie
@Configuration
public class MyConfiguration {
@Bean
@Lazy
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Window window() {
return new Window();
}
@Bean
public WindowGenerator windowGenerator(){
return new WindowGenerator() {
@Override
protected Window createNewWindow(){
return window();
}
};
}
}
Injectie van een protoype-scoped bean in singleton via javax.inject.Provider
Java lessen
public class Window {
}
public class WindowGenerator {
private final Provider<Window> windowProvider;
public WindowGenerator(final Provider<Window> windowProvider) {
this.windowProvider = windowProvider;
}
public Window generateWindow() {
Window window = windowProvider.get(); // new instance for each call
...
}
}
XML
<bean id="window" class="somepackage.Window" scope="prototype" lazy-init="true"/>
<bean id="windowGenerator" class="somepackage.WindowGenerator">
<constructor-arg>
<bean class="org.springframework.beans.factory.config.ProviderCreatingFactoryBean">
<property name="targetBeanName" value="window"/>
</bean>
</constructor-arg>
</bean>
Dezelfde benaderingen kunnen ook voor andere scopes worden gebruikt (bijv. Voor injectie van een scoopboon op aanvraag in singleton).
Specifieke bean-instanties automatisch aansturen met @Qualifier
Als u meerdere implementaties van dezelfde interface hebt, moet Spring weten welke het automatisch in een klasse moet aansluiten. Ik ga in dit voorbeeld een Validator-patroon gebruiken. 1
Foaklasse:
public class Foo {
private String name;
private String emailAddress;
private String errorMessage;
/** Getters & Setters omitted **/
}
Koppel:
public interface FooValidator {
public Foo validate(Foo foo);
}
Naam Validator Class:
@Component(value="FooNameValidator")
public class FooNameValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Validation logic goes here.
}
}
E-mail Validator Class:
@Component(value="FooEmailValidator")
public class FooEmailValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Different validation logic goes here.
}
}
U kunt deze validators nu afzonderlijk in een klasse omleiden.
Koppel:
public interface FooService {
public void handleFoo(Foo foo);
}
Klasse:
@Service
public class FooServiceImpl implements FooService {
/** Autowire validators individually **/
@Autowired
/*
* Notice how the String value here matches the value
* on the @Component annotation? That's how Spring knows which
* instance to autowire.
*/
@Qualifier("FooNameValidator")
private FooValidator nameValidator;
@Autowired
@Qualifier("FooEmailValidator")
private FooValidator emailValidator;
@Override
public void handleFoo(Foo foo) {
/**You can use just one instance if you need**/
foo = nameValidator.validate(foo);
}
}
Het is vermeldenswaard dat als je meer dan één implementatie van een interface in de Spring IoC-container hebt en niet opgeeft welke je wilt gebruiken met de annotatie @Qualifier
, Spring een uitzondering zal @Qualifier
wanneer hij probeert te starten, omdat hij heeft gewonnen ' t weet welke instantie te gebruiken.
1: Dit is niet de juiste manier om dergelijke eenvoudige validaties uit te voeren. Dit is een eenvoudig voorbeeld over autowiring. Als u een idee van een veel eenvoudiger validatiemethode wilt, kijk dan hoe Spring validatie uitvoert met annotaties.
Autowiring specifieke instanties van klassen met behulp van generieke typeparameters
Als u een interface hebt met een generieke parameter type, kan Spring die gebruiken om alleen implementaties automatisch door te voeren die een door u opgegeven parameter type implementeren.
Koppel:
public interface GenericValidator<T> {
public T validate(T object);
}
Foo Validator Class:
@Component
public class FooValidator implements GenericValidator<Foo> {
@Override
public Foo validate(Foo foo) {
//Logic here to validate foo objects.
}
}
Bar Validator Class:
@Component
public class BarValidator implements GenericValidator<Bar> {
@Override
public Bar validate(Bar bar) {
//Bar validation logic here
}
}
U kunt deze validators nu automatisch gebruiken met behulp van typeparameters om te beslissen welk exemplaar automatisch moet worden geleid.
Koppel:
public interface FooService {
public void handleFoo(Foo foo);
}
Klasse:
@Service
public class FooServiceImpl implements FooService {
/** Autowire Foo Validator **/
@Autowired
private GenericValidator<Foo> fooValidator;
@Override
public void handleFoo(Foo foo) {
foo = fooValidator.validate(foo);
}
}