Sök…


Autowiring alla bönor av en viss typ

Om du har flera implementeringar av samma gränssnitt, kan Spring autowire dem alla till ett samlingsobjekt. Jag kommer att använda ett exempel med ett valideringsmönster 1

Foo Class:

public class Foo {
     private String name;
     private String emailAddress;
     private String errorMessage;
     /** Getters & Setters omitted **/
}

Gränssnitt:

public interface FooValidator {
    public Foo validate(Foo foo);
}

Namn Validator Class:

@Component(value="FooNameValidator")
public class FooNameValidator implements FooValidator {
    @Override
    public Foo validate(Foo foo) {
        //Validation logic goes here.
    }
}

E-postvalideringsklass:

@Component(value="FooEmailValidator")
public class FooEmailValidator implements FooValidator {
    @Override
    public Foo validate(Foo foo) {
        //Different validation logic goes here.
    }
}

Du kan nu autowire dessa validerare individuellt eller tillsammans i en klass.

Gränssnitt:

public interface FooService {
    public void handleFoo(Foo foo);
}

Klass:

@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);               
       }
    }   
}

Det är värt att notera att om du har mer än en implementering av ett gränssnitt i Spring IoC-behållaren och inte anger vilken du vill använda med @Qualifier kommentaren, kommer Spring att göra ett undantag när du försöker starta, eftersom det vann ' t vet vilken instans jag ska använda.

1: Detta är inte det rätta sättet att göra så enkla valideringar. Detta är ett enkelt exempel på autowiring. Om du vill ha en idé om en mycket enklare valideringsmetod, leta upp hur våren validerar med kommentarer.

Förklara Bean

För att förklara en böna, antecknar du bara en metod med @Bean anteckningen eller kommenterar en klass med @Component annotationen (kommentarer @Service , @Repository , @Controller kan också användas).

När JavaConfig möter en sådan metod kommer den att utföra den metoden och registrera returvärdet som en böna i en BeanFactory. Som standard är bönornamnet namnet på metodnamnet.

Vi kan skapa bönor på ett av tre sätt:

  1. Använda Java-baserad konfiguration : I konfigurationsfilen måste vi deklarera bönor med @bean-kommentering

    @Configuration
    public class AppConfig {
        @Bean
        public TransferService transferService() {
            return new TransferServiceImpl();
        }
    }
    
  2. Använda XML-baserad konfiguration : För XML-baserad konfiguration måste vi skapa deklarera bönor i applikationskonfiguration XML, dvs.

     <beans>
         <bean name="transferService" class="com.acme.TransferServiceImpl"/>
     </beans>
    
  3. Annotation-Driven Component : För kommenteringsdrivna komponenter måste vi lägga till @Component-kommentaren till den klass vi vill förklara som bönor.

     @Component("transferService")
     public class TransferServiceImpl implements TransferService {
         ...
     }
    

Nu finns alla tre bönor med name transferService tillgängliga i BeanFactory eller ApplicationContext .

Grundläggande annotering autowiring

Gränssnitt:

public interface FooService {
    public int doSomething();
}

Klass:

@Service
public class FooServiceImpl implements FooService {
    @Override
    public int doSomething() {
        //Do some stuff here
        return 0;
    }
}

Det bör noteras att en klass måste implementera ett gränssnitt för våren för att kunna autowire denna klass. Det finns en metod för att låta Spring att autowire fristående klasser med vävning av lasttid, men det är utanför ramen för detta exempel.

Du kan få tillgång till den här bönan i vilken klass som helst som anordnades av Spring IoC-behållaren med @Autowired kommentaren.

Användande:

@Autowired([required=true])

@Autowired anteckningen kommer först att försöka autowire efter typ och sedan falla tillbaka på bönnamn i händelse av tvetydighet.

Denna kommentar kan tillämpas på flera olika sätt.

Konstruktörsinjektion:

public class BarClass() {
    private FooService fooService         

    @Autowired
    public BarClass(FooService fooService) {
        this.fooService = fooService;
    }
}

Fältinjektion:

public class BarClass() {
    @Autowired
    private FooService fooService;
}

Seterinjektion:

public class BarClass() {
    private FooService fooService;

    @Autowired
    public void setFooService(FooService fooService) {
        this.fooService = fooService;
    }
}

Använda FactoryBean för dynamisk böneangivning

För att dynamiskt bestämma vilka bönor som ska injiceras kan vi använda FactoryBean s. Dessa är klasser som implementerar fabriksmetodsmönstret och ger instanser av bönor för behållaren. De känns igen av våren och kan användas transparent, utan att behöva veta att bönan kommer från en fabrik. Till exempel:

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";
    }
}

Konfiguration:

@Configuration
public class ExampleConfig {
    @Bean
    public FactoryBean<String> fromFactory() {
        return new ExampleFactoryBean();
    }
}

Skaffa bönan:

AbstractApplicationContext context = new AnnotationConfigApplicationContext(ExampleConfig.class);
String exampleString = (String) context.getBean("fromFactory");

För att få den faktiska FactoryBean, använd ampersand-prefixet före bönans namn:

FactoryBean<String> bean = (FactoryBean<String>) context.getBean("&fromFactory");

Observera att du bara kan använda prototype eller singleton omfång - för att ändra räckvidden till prototype isSingleton metoden:

public class ExampleFactoryBean extends AbstractFactoryBean<String> {
    @Override
    public boolean isSingleton() {
        return false;
    }

    // other methods omitted for readability reasons
}

Observera att scoping avser faktiska instanser som skapas, inte själva fabriksbönan.

Injicera prototyp-scoped bönor i singletons

Behållaren skapar en singletonböna och injicerar samarbetspartners bara en gång. Detta är inte det önskade beteendet när en singleton böna har en prototyp-scoped samarbetspartner, eftersom den prototyp-scoped bönan bör injiceras varje gång den åtkomst via accessor.

Det finns flera lösningar på detta problem:

  1. Använd uppslagsmetodinjektion
  2. Hämta en prototyp-scoped böna via javax.inject.Provider
  3. Hämta en prototyp-scoped böna via org.springframework.beans.factory.ObjectFactory (motsvarande nr 2, men med klassen som är specifik för våren)
  4. Gör en singleton bönebehållare medveten via implementeringsgränssnittet ApplicationContextAware

Tillvägagångssätt nr 3 och # 4 är generellt avskräckta, eftersom de starkt binder en app till vårens ramverk. Således omfattas de inte av detta exempel.

Injektionsmetodinjektion via XML-konfiguration och en abstrakt metod

Java-klasser

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>

Injektion av uppslagsmetod via Java-konfiguration och @Component

Java-klasser

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-konfiguration

@Configuration
@ComponentScan("somepackage") // package where WindowGenerator is located
public class MyConfiguration {

    @Bean
    @Lazy
    @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Window window() {
        return new Window();
    }
}

Manuell injektionsmetodinjektion via Java-konfiguration

Java-klasser

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-konfiguration

@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();
            }
        };
    }
}

Injektion av en prototyp-scoped bönor i singleton via javax.inject.Provider

Java-klasser

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>

Samma tillvägagångssätt kan också användas för andra räckvidd (t.ex. för injektion av en förfrågningsskopa i singleton).

Autowiring specifika böna instanser med @Qualifier

Om du har flera implementationer av samma gränssnitt, måste Spring veta vilken den ska autowire till en klass. Jag kommer att använda ett Validator-mönster i det här exemplet. 1

Foo Class:

public class Foo {
     private String name;
     private String emailAddress;
     private String errorMessage;
     /** Getters & Setters omitted **/
}

Gränssnitt:

public interface FooValidator {
    public Foo validate(Foo foo);
}

Namn Validator Class:

@Component(value="FooNameValidator")
public class FooNameValidator implements FooValidator {
    @Override
    public Foo validate(Foo foo) {
        //Validation logic goes here.
    }
}

E-postvalideringsklass:

@Component(value="FooEmailValidator")
public class FooEmailValidator implements FooValidator {
    @Override
    public Foo validate(Foo foo) {
        //Different validation logic goes here.
    }
}

Du kan nu autowire dessa validerare individuellt i en klass.

Gränssnitt:

public interface FooService {
    public void handleFoo(Foo foo);
}

Klass:

@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);
    }   
}

Det är värt att notera att om du har mer än en implementering av ett gränssnitt i Spring IoC-behållaren och inte anger vilken du vill använda med @Qualifier kommentaren, kommer Spring att göra ett undantag när du försöker starta, eftersom det vann ' t vet vilken instans jag ska använda.

1: Detta är inte det rätta sättet att göra så enkla valideringar. Detta är ett enkelt exempel på autowiring. Om du vill ha en idé om en mycket enklare valideringsmetod, leta upp hur våren validerar med kommentarer.

Autowiring specifika instanser av klasser med hjälp av generiska typparametrar

Om du har ett gränssnitt med en generisk typparameter, kan Spring använda det för att bara använda autowire-implementationer som implementerar en typparameter som du anger.

Gränssnitt:

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
    }
}

Du kan nu autowire dessa validatorer med hjälp av typparametrar för att bestämma vilken instans som ska autowire.

Gränssnitt:

public interface FooService {
    public void handleFoo(Foo foo);
}

Klass:

@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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow