spring
Bohnen erstellen und verwenden
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:
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(); } }
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>
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:
- Verwenden Sie eine Suchmethode-Injektion
- Rufen Sie über
javax.inject.Provider
ein Bean mit einem prototypischenjavax.inject.Provider
- Rufen Sie eine Bean mit einem Prototypbereich über
org.springframework.beans.factory.ObjectFactory
(Entsprechung zu # 2, jedoch mit der für Spring spezifischen Klasse). - 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);
}
}