Suche…


Automatische Einstellung aller Beans eines bestimmten Typs

Wenn Sie mehrere Implementierungen derselben Schnittstelle haben, kann Spring diese automatisch in ein Collection-Objekt übernehmen. Ich werde ein Beispiel mit einem Validator-Muster 1 verwenden

Foo-Klasse:

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

Schnittstelle:

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

Namensprüfungsklasse:

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

E-Mail-Validierungsklasse:

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

Sie können diese Validatoren jetzt einzeln oder zusammen in eine Klasse automatisch einverleiben.

Schnittstelle:

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

Wenn Sie mehr als eine Implementierung einer Schnittstelle im Spring-IoC-Container haben und nicht angeben, welche @Qualifier Sie mit der @Qualifier Annotation verwenden @Qualifier , gibt Spring beim Start eine Ausnahme aus, da sie nicht erfolgreich war Ich weiß nicht, welche Instanz verwendet werden soll.

1: Dies ist nicht der richtige Weg, um solche einfachen Überprüfungen durchzuführen. Dies ist ein einfaches Beispiel für das Autowiring. Wenn Sie eine Vorstellung von einer viel einfacheren Validierungsmethode erhalten möchten, schauen Sie nach, wie Spring die Validierung mit Anmerkungen durchführt.

Bean deklarieren

Um ein Bean zu deklarieren, kommentieren Sie einfach eine Methode mit der @Bean Annotation oder eine Klasse mit der @Component Annotation (Annotations @Service , @Repository , @Controller können ebenfalls verwendet werden).

Wenn JavaConfig auf eine solche Methode stößt, führt sie diese Methode aus und registriert den Rückgabewert als Bean in einer BeanFactory. Standardmäßig ist der Bean-Name der Name der Methode.

Wir können Bean auf drei Arten erstellen:

  1. Java-basierte Konfiguration verwenden : In der Konfigurationsdatei müssen Sie die Bean mit der @bean-Annotation deklarieren

    @Configuration
    public class AppConfig {
        @Bean
        public TransferService transferService() {
            return new TransferServiceImpl();
        }
    }
    
  2. Verwendung einer XML-basierten Konfiguration : Für eine XML-basierte Konfiguration müssen Sie deklarieren Bean in der Anwendungskonfigurations-XML erstellen, dh

     <beans>
         <bean name="transferService" class="com.acme.TransferServiceImpl"/>
     </beans>
    
  3. Annotationsgesteuerte Komponente : Für Annotations-gesteuerte Komponenten müssen wir die @Component-Annotation der Klasse hinzufügen, die wir als Bean deklarieren möchten.

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

Jetzt sind alle drei Beans mit dem Namen transferService in BeanFactory oder ApplicationContext verfügbar.

Grundlegende Annotation für automatisches Verweilen

Schnittstelle:

public interface FooService {
    public int doSomething();
}

Klasse:

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

Es ist zu beachten, dass eine Klasse eine Schnittstelle für Spring implementieren muss, um diese Klasse automatisch verwenden zu können. Es gibt eine Methode, mit der Spring autonome Standalone-Klassen mithilfe von Ladezeitwebung automatisieren kann. Dies ist jedoch für diesen Fall nicht möglich.

Sie können auf diese Bean in jeder Klasse zugreifen, die mithilfe der Annotation @Autowired vom Spring-IoC-Container instanziiert wird.

Verwendungszweck:

@Autowired([required=true])

Die Annotation @Autowired versucht zuerst, sich automatisch nach Typ zu richten, und greift dann im Fall von Unklarheiten auf den Bean-Namen zurück.

Diese Anmerkung kann auf verschiedene Arten angewendet werden.

Konstruktorinjektion:

public class BarClass() {
    private FooService fooService         

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

Feldinjektion:

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

Setterinjektion:

public class BarClass() {
    private FooService fooService;

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

Verwendung von FactoryBean zur dynamischen Bean-Instantiierung

Um dynamisch zu entscheiden, welche Beans injiziert werden sollen, können wir FactoryBean s verwenden. Dies sind Klassen, die das Factory-Method-Pattern implementieren und Beans-Instanzen für den Container bereitstellen. Sie werden von Spring erkannt und können transparent verwendet werden, ohne dass die Bohne aus einer Fabrik stammt. Zum Beispiel:

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

Aufbau:

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

Die Bohne bekommen:

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

Verwenden Sie das kaufmännische Und-Zeichen vor dem Namen der Bean, um die tatsächliche FactoryBean zu erhalten:

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

Bitte beachten Sie, dass Sie nur verwenden können , prototype oder singleton Bereiche - den Umfang zu ändern , um prototype Überschreibung isSingleton Methode:

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

    // other methods omitted for readability reasons
}

Beachten Sie, dass sich der Umfang auf die tatsächlich erstellten Instanzen bezieht, nicht auf die Factory-Bean selbst.

Injizieren Sie Prototyp-Beans in Singletons

Der Container erstellt eine Singleton-Bean und fügt nur ein einziges Mal Mitarbeiter hinzu. Dies ist nicht das gewünschte Verhalten, wenn eine Singleton-Bean einen Kollaborator für Prototyp-Bereiche hat, da die Bean für Prototyp-Bereich bei jedem Zugriff über einen Accessor eingefügt werden sollte.

Es gibt mehrere Lösungen für dieses Problem:

  1. Verwenden Sie eine Suchmethode-Injektion
  2. Rufen Sie über javax.inject.Provider ein Bean mit einem prototypischen javax.inject.Provider
  3. Rufen Sie eine Bean mit einem Prototypbereich über org.springframework.beans.factory.ObjectFactory (Entsprechung zu # 2, jedoch mit der für Spring spezifischen Klasse).
  4. Machen Sie einen Singleton-Bean-Container durch Implementieren der ApplicationContextAware Schnittstelle aufmerksam

Die Ansätze Nr. 3 und Nr. 4 werden im Allgemeinen nicht empfohlen, da sie eine App stark an das Spring-Framework binden. Daher werden sie in diesem Beispiel nicht behandelt.

Suche nach Method-Injection über XML-Konfiguration und eine abstrakte 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>

Suche nach Method-Injection über Java-Konfiguration und @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-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();
    }
}

Manuelle Suchmethoden-Injektion über Java-Konfiguration

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-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 einer Bean mit Protoype-Bereich in Singleton über javax.inject.Provider

Java-Klassen

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>

Die gleichen Ansätze können auch für andere Bereiche verwendet werden (z. B. für die Injektion eines Anforderungsbereichs-Bean in Singleton).

Bestimmte Bean-Instanzen mit @Qualifier automatisch einschalten

Wenn Sie mehrere Implementierungen derselben Schnittstelle haben, muss Spring wissen, welche Implementierung automatisch in eine Klasse integriert werden sollte. Ich werde in diesem Beispiel ein Validator-Muster verwenden. 1

Foo-Klasse:

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

Schnittstelle:

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

Namensprüfungsklasse:

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

E-Mail-Validierungsklasse:

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

Sie können diese Validatoren jetzt automatisch in eine Klasse einbetten.

Schnittstelle:

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

Wenn Sie mehr als eine Implementierung einer Schnittstelle im Spring-IoC-Container haben und nicht angeben, welche @Qualifier Sie mit der @Qualifier Annotation verwenden @Qualifier , gibt Spring beim Start eine Ausnahme aus, da sie nicht erfolgreich war Ich weiß nicht, welche Instanz verwendet werden soll.

1: Dies ist nicht der richtige Weg, um solche einfachen Überprüfungen durchzuführen. Dies ist ein einfaches Beispiel für das Autowiring. Wenn Sie eine Vorstellung von einer viel einfacheren Validierungsmethode erhalten möchten, schauen Sie nach, wie Spring die Validierung mit Anmerkungen durchführt.

Automatisches Aktivieren bestimmter Klasseninstanzen mit generischen Typparametern

Wenn Sie über eine Schnittstelle mit einem generischen Typparameter verfügen, kann Spring dies nur für automatische Implementierungen verwenden, die einen von Ihnen angegebenen Typparameter implementieren.

Schnittstelle:

public interface GenericValidator<T> {
    public T validate(T object);
}

Foo-Validator-Klasse:

@Component
public class FooValidator implements GenericValidator<Foo> {
    @Override
    public Foo validate(Foo foo) {
        //Logic here to validate foo objects.
    }
}

Bar-Validator-Klasse:

@Component
public class BarValidator implements GenericValidator<Bar> {
    @Override
    public Bar validate(Bar bar) {
        //Bar validation logic here
    }
}

Sie können diese Validatoren jetzt mithilfe von Typparametern automatisch aktivieren, um zu entscheiden, welche Instanz automatisch aktiviert werden soll.

Schnittstelle:

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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow