2009-07-06 25 views
8

Sto utilizzando Spring per gestire le chiamate RMI su un server remoto. È semplice costruire un contesto applicativo ed ottenere il fagiolo di invocazioni remoti nel client:Passare le proprietà a un contesto Spring

ApplicationContext context = new ApplicationContext("classpath:context.xml"); 

MyService myService = (MyService) context.getBean("myService "); 

Tuttavia non vedono un modo semplice per passare proprietà nella configurazione. Ad esempio se voglio determinare il nome host per il server remoto in fase di esecuzione all'interno del client.

avrei idealmente avere una voce nel contesto primavera come questo:

<bean id="myService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> 
    <property name="serviceUrl" value="rmi://${webServer.host}:80/MyService"/> 
    <property name="serviceInterface" value="com.foo.MyService"/> 
</bean> 

e passare le proprietà del contesto da parte del cliente come parametro.

Posso usare un PropertyPlaceholderConfigurer nel contesto per sostituire queste proprietà, ma per quanto posso dire questo funziona solo per le proprietà lette da un file.

Ho un'implementazione che risolve questo problema (aggiunta come risposta) ma sto cercando un'implementazione standard di Spring per evitare di far girare il mio. C'è un altro configuratore Spring (o qualsiasi altra cosa) per aiutare a inizializzare la configurazione o sto meglio guardando java config per raggiungere questo?

+0

Dove altro si potrebbe memorizzare le proprietà? –

+0

il client può connettersi a qualsiasi server, ad es. l'utente può digitare il nome host in un campo di testo. Quindi non è determinato al momento della compilazione. –

risposta

1

Aggiornamento:

Sulla base della domanda di aggiornamento, il mio suggerimento è:

  1. Creare un fagiolo ServiceResolver che gestisce tutto ciò che è necessario gestire in base all'input del cliente;
  2. Dichiarare questo bean come dipendenza dai servizi rilevanti;
  3. In fase di runtime, è possibile aggiornare/utilizzare questo bean come meglio credi.

Il ServiceResolver può quindi, sia sul init-method o ad ogni invocazione determinare i valori da restituire al client, ad esempio sulla base di Ricerche JNDI o variabili ambientali.

Ma prima di farlo, potresti dare un'occhiata allo configuration options disponibile. È possibile:

  • aggiungere file di proprietà che non devono essere presenti in fase di compilazione;
  • cercare valori da JNDI;
  • ottiene i valori da System.properties.

Se avete bisogno di ricercare oggetti di un percorso personalizzato, un'occhiata a org.springframework.beans.factory.config.BeanFactoryPostProcessor e come il org.springframework.beans.factory.config.PropertyPlaceholderConfigurer è implementato.

L'idea di base è che si ottengano i bean con le proprietà "raw", ad es. ${jdbcDriverClassName} e quindi si arriva a risolverli e sostituirli con i valori desiderati.

+0

Grazie è così che funziona la mia implementazione esistente, aggiornerò la mia domanda per riflettere questo. Speravo che ci fosse una implementazione standard di Spring per questo, così posso evitare di farmi da solo. –

1

PropertyPlaceholderConfigurer può recuperare le proprietà da un file, è vero, ma se non riesce a trovarle, torna a utilizzare le proprietà di sistema.Sembra una valida opzione per l'applicazione client, basta passare la proprietà di sistema usando -D quando si avvia il client.

Dal javadoc

Un configuratore verificherà anche contro proprietà di sistema (ad esempio "user.dir") se non può risolvere un segnaposto con qualsiasi proprietà specificate. Questo può essere personalizzato tramite "systemPropertiesMode".

2

La mia soluzione esistente prevede la definizione di un nuovo MapAwareApplicationContext che accetta una mappa come ulteriore argomento del costruttore.

public MapAwareApplicationContext(final URL[] configURLs, 
    final String[] newConfigLocations, 
    final Map<String, String> additionalProperties) { 
    super(null); 

    //standard constructor content here 

    this.map = new HashMap<String, String>(additionalProperties); 

    refresh(); 
} 

Esso sovrascrive postProcessBeanFactory() per aggiungere in un MapAwareProcessor:

protected void postProcessBeanFactory(
    final ConfigurableListableBeanFactory beanFactory) { 
    beanFactory.addBeanPostProcessor(new MapAwareProcessor(this.map)); 
    beanFactory.ignoreDependencyInterface(MapAware.class); 
} 

Il MapAwareProcessor implementa postProcessBeforeInitialization() per iniettare la mappa in qualsiasi tipo che implementa l'interfaccia MapAware:

public Object postProcessBeforeInitialization(final Object bean, 
     final String beanName) { 
    if (this.map != null && bean instanceof MapAware) { 
     ((MapAware) bean).setMap(this.map); 
    } 

    return bean; 
} 

Quindi aggiungo un nuovo bean alla mia configurazione per dichiarare un MapAwarePropertyPlaceholderConfigurer:

<bean id="propertyConfigurer" 
    class="com.hsbc.r2ds.spring.MapAwarePropertyPlaceholderConfigurer"/> 

Il configuratore implementa MapAware, quindi verrà iniettato con la Mappa come sopra. E poi implementa resolvePlaceholder() per risolvere le proprietà dalla mappa, o delegare al configuratore genitore:

protected String resolvePlaceholder(final String placeholder, 
     final Properties props, final int systemPropertiesMode) { 
    String propVal = null; 
    if (this.map != null) { 
     propVal = this.map.get(placeholder); 
    } 
    if (propVal == null) { 
     propVal = super.resolvePlaceholder(placeholder, props); 
    } 
    return propVal; 
} 
+1

Buon Dio, è una soluzione complicata a un semplice problema ... – skaffman

+0

Questo è il mio punto, sembra qualcosa che dovrebbe essere realizzabile senza troppo sforzo, certamente meno sforzo di questo –

+0

Oppure si potrebbe usare BeanFactoryPostProcessor: http://springindepth.com/book/in-depth-ioc-bean- postprocessori-e-BeanFactory post-processors.html – Talijanac

0

creare un'istanza RmiProxyFactoryBean e configurare la proprietà serviceUrl direttamente nel codice:

String serverHost = "www.example.com"; 

RmiProxyFactoryBean factory = new RmiProxyFactoryBean(); 
factory.setServiceUrl("rmi://" + serverHost + ":80/MyService"); 
factory.setServiceInterface(MyService.class); 
try { 
    factory.afterPropertiesSet(); 
} catch (Exception e) { 
    throw new RuntimeException(
      "Problem initializing myService factory", e); 
} 
MyService myService = (MyService) factory.getObject(); 
13

Vedi http://forum.springsource.org/showthread.php?t=71815

TestClass.java

package com.spring.ioc; 

public class TestClass { 

    private String first; 
    private String second; 

    public String getFirst() { 
     return first; 
    } 

    public void setFirst(String first) { 
     this.first = first; 
    } 

    public String getSecond() { 
     return second; 
    } 

    public void setSecond(String second) { 
     this.second = second; 
    } 
} 

SpringStart.java

package com.spring; 

import java.util.Properties; 

import com.spring.ioc.TestClass; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; 

public class SpringStart { 
    public static void main(String[] args) throws Exception { 
    PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer(); 
    Properties properties = new Properties(); 
    properties.setProperty("first.prop", "first value"); 
    properties.setProperty("second.prop", "second value"); 
    configurer.setProperties(properties); 

    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(); 
    context.addBeanFactoryPostProcessor(configurer); 

    context.setConfigLocation("spring-config.xml"); 
    context.refresh(); 

    TestClass testClass = (TestClass)context.getBean("testBean"); 
    System.out.println(testClass.getFirst()); 
    System.out.println(testClass.getSecond()); 
    } 
} 

spring-config.xml

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation=" 
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> 

    <bean id="testBean" class="com.spring.ioc.TestClass"> 
     <property name="first" value="${first.prop}"/> 
     <property name="second" value="${second.prop}"/> 
    </bean> 

</beans> 

uscita:

first value 
second value 
Problemi correlati