2015-11-03 43 views
7

Vorrei creare una corretta strategia di gestione delle proprietà in una webapp java che si basa su google guice come framework DI.Gestione proprietà di Google Guice

desidero avere un meccanismo di rispondere alle 3 requisiti seguenti:

  • desidero poter iniettare proprietà usando guice (@Named)
  • desidero poter accedere alle proprietà in modo statico
  • Il meccanismo deve supportare la definizione delle priorità delle proprietà, ovvero una proprietà può essere incapsulata nella guerra distribuita con un determinato valore ma può anche essere ridondante nel livello di sistema di destinazione o nel file system locale (della macchina di destinazione Mi schiererò su), in tal caso il valore in guerra sarà sostituito dal valore th esiste nella macchina target.

Credo che questo sia un requisito standard. Ora, usando il raccoglitore standard Guice, posso facilmente ottenere il primo requisito, ma non gli altri due. Per ottenere gli altri due ho creato la mia classe che fa la seguente:

  • avvolge e espone i metodi di rilegatura di Guice (quelle che lega le proprietà) Ad esempio:

public static void bindString(AnnotatedBindingBuilder<String> binder, String property, String defaultValue) { binder.annotatedWith(Names.named(property)).toInstance(getProperty(property, defaultValue)); }

Dove il metodo getProperty sa come gestire le mie proprietà (ricava il valore dal livello war o di sistema) e espone staticamente anche le proprietà.

Fondamentalmente, finché utilizzo questa utility che ho creato per il binding delle proprietà, sto bene, copre tutte le mie esigenze ma una volta che utilizzo i collegamenti standard guiz, sto perdendo il secondo e il terzo requisito.

C'è un modo per ignorare le associazioni di Guzzi e ottenere tutti questi 3 requisiti?

Una volta ho avuto la stessa sfida in un'app basata sulla primavera ed è stato abbastanza facile. Ho implementato ApplicationContextInitializer con il seguente metodo:

@Override public void initialize(ConfigurableWebApplicationContext ctx) { PropertySource<Map<String, Object>> localProps = null; try { localProps = new ResourcePropertySource(new ClassPathResource(LOCAL_PROPERTIES_FILE_NAME)); } catch (IOException e) { LOG.fatal("Could not load local properties from classpath " + LOCAL_PROPERTIES_FILE_NAME); return; } LOG.info("Loaded configuration from classpath local file " + LOCAL_PROPERTIES_FILE_NAME); ctx.getEnvironment().getPropertySources().addFirst(localProps); }

quindi questo mi ha dato un modo per aggiungere proprietà locali con la massima priorità al mio ambiente. In caso di sovrapposizione con le proprietà di guerra, quelle locali avevano una priorità più alta. Inoltre ho esposto staticamente il mio ambiente in modo da avere accesso statico alle mie proprietà (per servizi che non sono gestiti dal container, legacy in gran parte).

Come posso ottenere questo risultato con guice?

+0

Che cosa si intende, a cui si desidera accedere alle proprietà in modo statico? Vuol dire che hai casi d'uso in cui vuoi accedere a una particolare proprietà, ma non sai fino a runtime a quale proprietà vuoi accedere? –

+0

Vorrei poter accedere a una proprietà utilizzando qualcosa come: SomeClass.getProperty ("my.property"); Questo dovrebbe essere utile per le classi che non sono gestite da guizzi (legacy di solito) e hanno ancora bisogno di accedere alle mie proprietà – forhas

risposta

1

Purtroppo, non penso che troverai qualcosa che ti dia un'implementazione veramente pulita e soddisfacente. Soprattutto, non penso che troverai nulla che ti dia esattamente quello che vuoi senza implementare almeno una parte di te stesso.

Se avessi quelle necessità, farei in modo che il mio iniettore venga creato in una InjectorFactory centrale. Se hai bisogno di un gran numero di parametri dall'esterno per creare il tuo iniettore, lo creo semplicemente una volta all'inizio della mia applicazione e poi metto in cache l'iniettore in un campo finale statico. Ciò renderebbe disponibile un metodo statico. Vorrei legare la mia proprietà "fall-back" a un provider esplicito. In questo modo, invece di utilizzare lo standard Names.bindProperties (...), lo legherei direttamente a un provider. Questo provider implementa quindi la logica necessaria per eseguire il fallback o unire più file di proprietà. Avere l'iniettore memorizzato nella cache in un campo statico significa che posso chiamare un metodo statico per accedere alle proprietà da un contesto globale al di fuori delle mie classi iniettate.

L'utilizzo del proprio provider sembra inizialmente spiacevole, ma può fornire alcuni ulteriori vantaggi. Per cominciare, puoi implementare la tua strategia di fallback esattamente come desideri. Inoltre, è possibile aggiungere ulteriori comportamenti come il ricaricamento automatico dei propri file di proprietà, ecc. (Non mostrato nel mio esempio di codice).

public class InjectorFactory { 
    private static Injector injector = null; 
    public static synchronized Injector getOrCreateInjector() { 
     if(injector == null) { 
      injector = Guice.createInjector(new AbstractModule() { 
       @Override 
       protected void configure() { 
        Properties properties1 = createProperties("file1.properties"); 
        Properties properties2 = createProperties("file2.properties"); 
        Set<Object> propertyNames = new HashSet<Object>(); 
        propertyNames.addAll(properties1.keySet()); 
        propertyNames.addAll(properties2.keySet()); 

        for (Object object : propertyNames) { 
         String propertyName = (String) object; 
         bind(String.class).annotatedWith(Names.named(propertyName)).toProvider(new StringProvider(properties1, properties2, propertyName)); 
        } 
       } 

       private Properties createProperties(String propertyFileName) { 
        try { 
         InputStream stream = InjectorFactory.class.getResourceAsStream(propertyFileName); 
         try { 
          Properties properties = new Properties(); 
          properties.load(stream); 
          return properties; 
         } finally { 
          stream.close(); 
         } 

        } catch (IOException exception) { 
         throw new RuntimeException("Could not load properties file"); 
        } 
       } 
      }); 
     } 
     return injector; 
    } 

    public static String getProperty(String propertyName) { 
     return getOrCreateInjector().getInstance(Key.get(String.class, Names.named(propertyName))); 
    } 

} 

dato il codice di cui sopra e file1.properties:

property1=Property1Value 
property2=Property2Value 

E file.properties:

property2=IncorrectProperty2Value 
property3=Property3Value 

con il provider

public class StringProvider implements Provider<String> { 
    private Properties properties1; 
    private Properties properties2; 
    private String propertyName; 
    public StringProvider(Properties properties1, Properties properties2, 
      String propertyName) { 
     this.properties1 = properties1; 
     this.properties2 = properties2; 
     this.propertyName = propertyName; 
    } 
    public String get() { 
     if(properties1.containsKey(propertyName)) { 
      return properties1.getProperty(propertyName); 
     } 
     return properties2.getProperty(propertyName); 
    } 
} 

Il seguente utilizzo:

public class InjectorFactoryTest { 
    public static void main(String ... parameters) { 
     System.out.println(InjectorFactory.getProperty("property1")); 
     System.out.println(InjectorFactory.getProperty("property2")); 
     System.out.println(InjectorFactory.getProperty("property3")); 
    } 
} 

Uscite:

Property1Value 
Property2Value 
Property3Value 
+0

Nice, ma hanno ancora uno svantaggio - Nessuno impone l'uso di questa fabbrica. Ad esempio, se qualcuno decide di creare un nuovo modello ma non di usare questa fabbrica, potrei finire con un divario tra le proprietà gestite da guiz per le proprietà di fabbrica, giusto? Sono d'accordo che finchè tutti usano la fabbrica per la creazione di modelli questo fornirà la soluzione, ma questo non è abbastanza per me (ho una soluzione simile al momento, solo un po 'più semplice :)) – forhas

+0

Sì, certo. In generale, chiunque può creare nuovi oggetti se volevano usare "nuovo", altrimenti potevano creare il proprio iniettore se dovessero assemblare il proprio elenco di moduli. A meno che tu non voglia iniziare a gestire le tue politiche JVM e quali classi siano autorizzate ad accedere a quali altre classi, non c'è davvero alcun modo per proibirle. L'obiettivo, quindi, è di rendere il più semplice possibile per le persone fare le cose nel modo giusto. E come ho detto prima, penso che sia improbabile che trovi una soluzione che ti dà una sensazione molto soddisfacente. –