spring
Creare e usare i fagioli
Ricerca…
Autowiring tutti i bean di un tipo specifico
Se hai diverse implementazioni della stessa interfaccia, Spring può autorizzarle tutte in un oggetto di raccolta. Userò un esempio usando un pattern Validator 1
Classe Foo:
public class Foo {
private String name;
private String emailAddress;
private String errorMessage;
/** Getters & Setters omitted **/
}
Interfaccia:
public interface FooValidator {
public Foo validate(Foo foo);
}
Nome Validator Class:
@Component(value="FooNameValidator")
public class FooNameValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Validation logic goes here.
}
}
Email Validator Class:
@Component(value="FooEmailValidator")
public class FooEmailValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Different validation logic goes here.
}
}
Ora puoi autorizzare questi validatori singolarmente o insieme in una classe.
Interfaccia:
public interface FooService {
public void handleFoo(Foo foo);
}
Classe:
@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);
}
}
}
Vale la pena notare che se si dispone di più di un'implementazione di un'interfaccia nel contenitore IoC di primavera e non si specifica quale si desidera utilizzare con l'annotazione @Qualifier
, Spring genera un'eccezione quando tenta di avviarsi, perché ha vinto " so quale istanza usare.
1: Questo non è il modo giusto per fare convalide così semplici. Questo è un semplice esempio di autowiring. Se vuoi un'idea di un metodo di validazione molto più semplice, cerca in che modo Spring convalida con le annotazioni.
Dichiarazione di Bean
Per dichiarare un bean, annotare semplicemente un metodo con l'annotazione @Bean
o annotare una classe con l'annotazione @Component
(possono essere utilizzate anche annotazioni @Service
, @Repository
, @Controller
).
Quando JavaConfig incontra un tale metodo, eseguirà quel metodo e registrerà il valore restituito come bean all'interno di BeanFactory. Per impostazione predefinita, il nome del bean sarà quello del nome del metodo.
Possiamo creare bean usando uno dei tre modi:
Utilizzo della configurazione basata su Java : nel file di configurazione è necessario dichiarare il bean usando l'annotazione @bean
@Configuration public class AppConfig { @Bean public TransferService transferService() { return new TransferServiceImpl(); } }
Utilizzo della configurazione basata su XML : per la configurazione basata su XML, è necessario creare un bean di dichiarazione nell'XI di configurazione dell'applicazione
<beans> <bean name="transferService" class="com.acme.TransferServiceImpl"/> </beans>
Componente basato sull'annotazione: per i componenti basati su annotazioni, è necessario aggiungere l'annotazione @Component alla classe che si desidera dichiarare come bean.
@Component("transferService") public class TransferServiceImpl implements TransferService { ... }
Ora tutti e tre i bean con nome transferService
sono disponibili in BeanFactory
o ApplicationContext
.
Autowiring di annotazione di base
Interfaccia:
public interface FooService {
public int doSomething();
}
Classe:
@Service
public class FooServiceImpl implements FooService {
@Override
public int doSomething() {
//Do some stuff here
return 0;
}
}
Va notato che una classe deve implementare un'interfaccia per Spring per poter autorizzare questa classe. Esiste un metodo per consentire a Spring di eseguire l'autowire delle classi autonome utilizzando la tessitura del tempo di caricamento, ma questo è fuori portata per questo esempio.
È possibile accedere a questo bean in qualsiasi classe istanziata dal contenitore IoC Spring utilizzando l'annotazione @Autowired
.
Uso:
@Autowired([required=true])
L'annotazione @Autowired
tenterà prima di eseguire l'autowire per tipo e quindi ricadrà sul nome del bean in caso di ambiguità.
Questa annotazione può essere applicata in diversi modi.
Iniezione del costruttore:
public class BarClass() {
private FooService fooService
@Autowired
public BarClass(FooService fooService) {
this.fooService = fooService;
}
}
Iniezione sul campo:
public class BarClass() {
@Autowired
private FooService fooService;
}
Iniezione setter:
public class BarClass() {
private FooService fooService;
@Autowired
public void setFooService(FooService fooService) {
this.fooService = fooService;
}
}
Uso di FactoryBean per l'istanziazione di bean dinamica
Per decidere in modo dinamico quali bean iniettare, possiamo utilizzare FactoryBean
s. Queste sono classi che implementano lo schema del metodo factory, fornendo istanze di bean per il contenitore. Sono riconosciuti da Spring e possono essere utilizzati in modo trasparente, senza necessità di sapere che il bean proviene da una fabbrica. Per esempio:
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";
}
}
Configurazione:
@Configuration
public class ExampleConfig {
@Bean
public FactoryBean<String> fromFactory() {
return new ExampleFactoryBean();
}
}
Ottenere il fagiolo:
AbstractApplicationContext context = new AnnotationConfigApplicationContext(ExampleConfig.class);
String exampleString = (String) context.getBean("fromFactory");
Per ottenere il FactoryBean effettivo, utilizzare il prefisso dello e commerciale prima del nome del bean:
FactoryBean<String> bean = (FactoryBean<String>) context.getBean("&fromFactory");
Si noti che è possibile utilizzare solo gli scopi prototype
o singleton
, per modificare l'ambito del metodo isSingleton
dell'override del prototype
:
public class ExampleFactoryBean extends AbstractFactoryBean<String> {
@Override
public boolean isSingleton() {
return false;
}
// other methods omitted for readability reasons
}
Si noti che l'ambito si riferisce alle istanze effettive create, non al bean di fabbrica stesso.
Iniettare i fagioli con scope prototipo in singleton
Il contenitore crea un bean singleton e inietta i collaboratori solo una volta. Questo non è il comportamento desiderato quando un bean singleton ha un collaboratore con scope prototipo, poiché il bean con scope prototipo deve essere iniettato ogni volta che viene acceduto tramite accessor.
Esistono diverse soluzioni a questo problema:
- Utilizzare l'iniezione del metodo di ricerca
- Recupera un bean con ambito prototipo tramite
javax.inject.Provider
- Recupera un bean con ambito prototipo tramite
org.springframework.beans.factory.ObjectFactory
(un equivalente di # 2, ma con la classe specifica per Spring) - Rendi consapevole un contenitore di bean singleton tramite l'implementazione dell'interfaccia
ApplicationContextAware
Gli approcci n. 3 e n. 4 sono generalmente scoraggiati, poiché legano fortemente un'app alla struttura di Spring. Quindi, non sono coperti in questo esempio.
Iniezione del metodo di ricerca tramite la configurazione XML e un metodo astratto
Classi Java
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>
Iniezione del metodo di ricerca tramite la configurazione Java e @Component
Classi Java
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();
}
}
Configurazione Java
@Configuration
@ComponentScan("somepackage") // package where WindowGenerator is located
public class MyConfiguration {
@Bean
@Lazy
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Window window() {
return new Window();
}
}
Iniezione del metodo di ricerca manuale tramite configurazione Java
Classi Java
public class Window {
}
public abstract class WindowGenerator {
public Window generateWindow() {
Window window = createNewWindow(); // new instance for each call
...
}
protected abstract Window createNewWindow(); // lookup method
}
Configurazione Java
@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();
}
};
}
}
Iniezione di un bean con javax.inject.Provider
in singleton tramite javax.inject.Provider
Classi Java
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>
Gli stessi approcci possono essere utilizzati anche per altri scopi (ad esempio per l'iniezione di un bean con scope richiesta in singleton).
Autowiring istanze di bean specifiche con @Qualifier
Se hai più implementazioni della stessa interfaccia, Spring ha bisogno di sapere quale deve essere autowire in una classe. In questo esempio utilizzerò un pattern Validator. 1
Classe Foo:
public class Foo {
private String name;
private String emailAddress;
private String errorMessage;
/** Getters & Setters omitted **/
}
Interfaccia:
public interface FooValidator {
public Foo validate(Foo foo);
}
Nome Validator Class:
@Component(value="FooNameValidator")
public class FooNameValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Validation logic goes here.
}
}
Email Validator Class:
@Component(value="FooEmailValidator")
public class FooEmailValidator implements FooValidator {
@Override
public Foo validate(Foo foo) {
//Different validation logic goes here.
}
}
Ora puoi autorizzare questi validatori singolarmente in una classe.
Interfaccia:
public interface FooService {
public void handleFoo(Foo foo);
}
Classe:
@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);
}
}
Vale la pena notare che se si dispone di più di un'implementazione di un'interfaccia nel contenitore IoC di primavera e non si specifica quale si desidera utilizzare con l'annotazione @Qualifier
, Spring genera un'eccezione quando tenta di avviarsi, perché ha vinto " so quale istanza usare.
1: Questo non è il modo giusto per fare convalide così semplici. Questo è un semplice esempio di autowiring. Se vuoi un'idea di un metodo di validazione molto più semplice, cerca in che modo Spring convalida con le annotazioni.
Autowiring di istanze specifiche di classi che utilizzano parametri di tipo generico
Se hai un'interfaccia con un parametro di tipo generico, Spring può utilizzarla solo per implementazioni autowire che implementano un parametro di tipo specificato.
Interfaccia:
public interface GenericValidator<T> {
public T validate(T object);
}
Classe Foo Validator:
@Component
public class FooValidator implements GenericValidator<Foo> {
@Override
public Foo validate(Foo foo) {
//Logic here to validate foo objects.
}
}
Classe di convalida della barra:
@Component
public class BarValidator implements GenericValidator<Bar> {
@Override
public Bar validate(Bar bar) {
//Bar validation logic here
}
}
Ora puoi autorizzare questi validatori usando i parametri del tipo per decidere quale istanza autorizzare.
Interfaccia:
public interface FooService {
public void handleFoo(Foo foo);
}
Classe:
@Service
public class FooServiceImpl implements FooService {
/** Autowire Foo Validator **/
@Autowired
private GenericValidator<Foo> fooValidator;
@Override
public void handleFoo(Foo foo) {
foo = fooValidator.validate(foo);
}
}