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:

  1. 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();
        }
    }
    
  2. 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>
    
  3. 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:

  1. Gebruik opzoekmethode injectie
  2. Haal een bonen met prototype- javax.inject.Provider via javax.inject.Provider
  3. Haal een bonen met prototype- org.springframework.beans.factory.ObjectFactory via org.springframework.beans.factory.ObjectFactory (een equivalent van # 2, maar met de klasse die specifiek is voor Spring)
  4. 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);
    }   
}


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow