2014-10-20 11 views
16

Sto utilizzando Spring Boot per l'applicazione autonoma Java. Ho un fagiolo che fa uso di un servizio. Voglio iniettare diverse implementazioni di quel servizio in fase di esecuzione, in base a una proprietà in un file di proprietà con Spring (4 per quella materia).Come iniettare servizi diversi in fase di esecuzione in base a una proprietà con Spring senza XML


Questo suona come il modello di fabbrica, ma Spring consente anche di utilizzare annotazioni per risolvere il problema, come questo.

@Autowired @Qualifier("selectorProperty") private MyService myService; 

Poi nel file beans.xml Ho un alias, in modo che possa utilizzare la proprietà nel @Qualifier.

<alias name="${selector.property}" alias="selectorProperty" /> 

E nelle mie diverse implementazioni avrei qualificazioni diverse.

@Component("Selector1") 
public class MyServiceImpl1 

@Component("Selector2") 
public class MyServiceImpl2 

application.properties

selector.property = Selector1 

selector.property = Selector2 

Mentre per quanto riguarda il modello di fabbrica, in primavera è possibile utilizzare ServiceLocatorFactoryBean per creare una fabbrica che darebbe la stessa funzionalità.

<bean 
    class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean" 
    id="myServiceFactory"> 
    <property 
    name="serviceLocatorInterface" 
    value="my.company.MyServiceFactory"> 
    </property> 
</bean> 

public interface MyServiceFactory 
{ 
    MyService getMyService(String selector); 
} 

E quindi nel bean è possibile utilizzare qualcosa di simile per ottenere la corretta implementazione in fase di esecuzione in base al valore della proprietà.

@Value("${selector.property}") private String selectorProperty; 

@Autowired private MyServiceFactory myServiceFactory; 

private MyService myService; 

@PostConstruct 
public void postConstruct() 
{ 
    this.myService = myServiceFactory.getMyService(selectorProperty); 
} 

Ma il problema con questa soluzione è che non riuscivo a trovare un modo per evitare l'uso di XML per definire la fabbrica, e vorrei utilizzare solo le annotazioni.


Quindi la domanda sarebbe, c'è un modo per utilizzare il ServiceLocatorFactoryBean (o qualcosa di equivalente) utilizzando solo le annotazioni, o sono costretto a usare il modo @Autowired @Qualifier se io non voglio definire i fagioli in XML? O esiste un altro modo per iniettare servizi diversi in fase di esecuzione in base a una proprietà con Spring 4 che evita XML? Se la tua risposta è semplicemente usare lo @Autowired @Qualifier con l'alias, ti preghiamo di spiegare perché è meglio che usare un modello di fabbrica ben noto.

L'utilizzo dell'XML in più mi costringe a utilizzare @ImportResource("classpath:beans.xml") nella mia classe Launcher, che preferirei non utilizzare.

Grazie.

+0

Hai provato @Qualifier ("$ {selector.property}")? È possibile nominare dinamicamente i bean, quindi forse è possibile impostare qualificatori anche in questo modo ... Non ho provato me stesso però .. – Nadir

+0

L'ho provato, ma non funziona. @Autowired @Qualifier ("$ {selector.property}") si risolve in null. Ha bisogno dell'alias come passo intermedio. Grazie comunque per il suggerimento. –

+0

Non sei sicuro, cosa stai cercando di archiviare, hai guardato la separazione della configurazione di primavera con i profili? – mavarazy

risposta

8

In realtà, è possibile utilizzare ServiceLocatorFactory senza XML dichiarando come un fagiolo nel file di configurazione.

@Bean 
public ServiceLocatorFactoryBean myFactoryServiceLocatorFactoryBean() 
{ 
    ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean(); 
    bean.setServiceLocatorInterface(MyServiceFactory.class); 
    return bean; 
} 

@Bean 
public MyServiceFactory myServiceFactory() 
{ 
    return (MyServiceFactory) myFactoryServiceLocatorFactoryBean().getObject(); 
} 

Quindi è ancora possibile utilizzare la fabbrica come al solito, ma non è coinvolto XML.

@Value("${selector.property}") private String selectorProperty; 

@Autowired @Qualifier("myServiceFactory") private MyServiceFactory myServiceFactory; 

private MyService myService; 

@PostConstruct 
public void postConstruct() 
{ 
    this.myService = myServiceFactory.getMyService(selectorProperty); 
} 
+0

Autowiring 'MyServiceFactory' senza Qualifier funziona anche senza creare bean aggiuntivo' myServiceFactory() '. Dubito che il nome della funzione e il nome del Qualificatore siano correlati in questo caso. Ma sì, nel complesso la tua soluzione è buona. – Lokesh

7

sto usando primavera profilo

Ad esempio, con origini dati Con esso si può definire tutte le origini dati, come ti piace

@Configuration 
@Profile("dev") 
public class StandaloneDataConfig { 

    @Bean 
    public DataSource dataSource() { 
     return new EmbeddedDatabaseBuilder() 
      .setType(EmbeddedDatabaseType.HSQL) 
      .addScript("classpath:com/bank/config/sql/schema.sql") 
      .addScript("classpath:com/bank/config/sql/test-data.sql") 
      .build(); 
    } 

} 

@Configuration 
@Profile("cloud") 
public class CloudDataConfig { 

    @Bean 
    public DataSource dataSource() { 
     return new EmbeddedDatabaseBuilder() 
      .setType(EmbeddedDatabaseType.HSQL) 
      .addScript("classpath:com/bank/config/sql/schema.sql") 
      .addScript("classpath:com/bank/config/sql/test-data.sql") 
      .build(); 
    } 

} 

E in fase di esecuzione, specificando

-Dspring.profiles.active = "myProfile"

si attiva una o un'altra configurazione (tutti devono essere importati nella configurazione principale, vengono semplicemente ignorati in base al profilo attivo).

Ecco un buon articolo: http://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/

+0

Mi piace questa soluzione. Anche se in questo caso particolare non ho profili diversi. Potrei usare qualcosa di simile, ma poi avrei bisogno di un profilo diverso per ogni implementazione della mia interfaccia. Se ho due fabbriche, allora diventa caotico. Grazie mille per la risposta comunque, potrei trovarlo utile per una cosa diversa. –

+0

Siete i benvenuti :) – mavarazy

Problemi correlati