2015-11-03 6 views
7

Ich möchte eine ordnungsgemäße Eigenschaften-Management-Strategie in einer Java-Webanwendung erstellen, die auf Google Guice als DI-Framework weiterleitet.Google Guice Properties Management

Ich möchte einen Mechanismus haben, die folgenden 3-Anforderungen zu beantworten:

  • Ich mag würde in der Lage sein Eigenschaften einzuspritzen Verwendung guice (@Named)
  • ich möchte in der Lage sein, Objekte zugreifen statisch
  • Der Mechanismus sollte die Priorisierung von Eigenschaften unterstützen, was bedeutet, dass eine Eigenschaft in den implementierten Krieg mit einem bestimmten Wert eingeschlossen werden kann, aber auch auf der Zielsystemebene oder dem lokalen Dateisystem (der Zielmaschine) redundant sein kann Ich setze auf), in einem solchen Fall wird der Wert im Krieg durch den Wert th außer Kraft gesetzt at existiert in der Zielmaschine.

Ich glaube, das ist eine Standardanforderung. Jetzt kann ich mit dem Standardbinder von Guice die erste Anforderung leicht erreichen, aber nicht die anderen beiden. Um die beiden anderen zu bekommen habe ich meine eigene Klasse, macht folgendes:

  • Wraps und macht die Bindungsmethoden von Guice (jene, die Eigenschaften bindet) Zum Beispiel:

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

Wo Die getProperty-Methode weiß, wie man mit meinen Eigenschaften umgeht (man erhält den Wert von der Kriegs- oder Systemebene) und stellt die Eigenschaften auch statisch dar.

Also im Grunde, solange ich dieses Dienstprogramm verwende, das ich für Eigenschaften Bindings erstellt habe, bin ich gut, es deckt alle meine Anforderungen ab, aber sobald ich die Standard-Guice Bindings verwende, verliere ich die zweite und dritte Anforderung.

Gibt es eine Möglichkeit, die Guice-Bindungen zu überschreiben und all diese 3 Anforderungen zu erhalten?

Einmal hatte ich die gleiche Herausforderung in einer Feder basierten App und war ziemlich einfach. Ich implementierte ApplicationContextInitializer mit folgenden Methode:

@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); }

so gab diesen mir einen Weg, um lokale Eigenschaften mit höchster Priorität zu meiner Umwelt hinzuzufügen. Bei Überschneidungen mit Kriegseigenschaften hatten die lokalen Vorrang. Außerdem habe ich meine Umgebung statisch dargestellt, so dass ich statischen Zugriff auf meine Eigenschaften habe (für Dienste, die nicht vom Container verwaltet werden, meist Legacy).

Wie kann ich dies mit guice erreichen?

+0

Was meinen Sie, dass Sie die Eigenschaften in einer statischen Art und Weise zugreifen möchten? Bedeutet das, dass Sie Anwendungsfälle haben, in denen Sie auf eine bestimmte Eigenschaft zugreifen möchten, aber Sie wissen nicht bis zur Laufzeit, auf welche Eigenschaft Sie zugreifen möchten? –

+0

Ich möchte auf eine Eigenschaft zugreifen können, die etwas wie verwendet: SomeClass.getProperty ("my.property"); Dies sollte für Klassen nützlich sein, die nicht von guice verwaltet werden (normalerweise Legacy) und trotzdem Zugriff auf meine Eigenschaften benötigen. – forhas

Antwort

1

Leider glaube ich nicht, dass Sie etwas finden werden, das Ihnen eine wirklich saubere und befriedigende Implementierung gibt. Insbesondere glaube ich nicht, dass Sie irgendetwas finden werden, das Ihnen genau das gibt, was Sie wollen, ohne zumindest Teile davon selbst zu implementieren.

Wenn ich diese Bedürfnisse hätte, würde ich sicherstellen, dass mein Injektor in einer zentralen InjectorFactory erstellt wird. Wenn Sie eine große Anzahl von Parametern von außen benötigen, um Ihren Injektor zu erstellen, würde ich ihn einfach zu Beginn meiner Anwendung erstellen und den Injektor dann in ein statisches Endfeld zwischenspeichern. Dies würde es für eine statische Methode verfügbar machen. Ich würde meine "Fallback" -Eigenschaft an einen expliziten Provider binden. Auf diese Weise, anstatt den Standardnamen.BindProperties (...) Methode würde ich es direkt an einen Provider binden. Dieser Anbieter implementiert dann die Logik, die erforderlich ist, um den Fallback durchzuführen oder mehrere Eigenschaftendateien zusammenzuführen. Wenn der Injektor in ein statisches Feld zwischengespeichert wird, bedeutet dies, dass ich eine statische Methode aufrufen kann, um auf Eigenschaften aus einem globalen Kontext außerhalb meiner injizierten Klassen zuzugreifen.

Die Verwendung Ihres eigenen Anbieters scheint zunächst unangenehm, kann aber einige zusätzliche Vorteile bieten. Für den Anfang können Sie Ihre Fallback-Strategie genau so implementieren, wie Sie möchten. Darüber hinaus können Sie zusätzliche Verhaltensweisen hinzufügen, z. B. das automatische Neuladen Ihrer Eigenschaftendateien usw. (nicht in meinem Codebeispiel gezeigt).

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))); 
    } 

} 

In Anbetracht der oben genannten Code und file1.properties:

property1=Property1Value 
property2=Property2Value 

Und file.properties:

property2=IncorrectProperty2Value 
property3=Property3Value 

mit dem Anbieter

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); 
    } 
} 

Die folgende Nutzung:

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")); 
    } 
} 

Ausgänge:

Property1Value 
Property2Value 
Property3Value 
+0

Nett, aber immer noch benachteiligt - Niemand erzwingt die Verwendung dieser Factory. Wenn sich beispielsweise jemand dazu entschließt, ein neues Modell zu erstellen, aber diese Fabrik nicht verwendet, kann es zu einer Lücke zwischen den verwalteten Eigenschaften und den Fabrikeigenschaften kommen. Habe ich recht? Ich stimme zu, dass, solange jeder die Fabrik für die Erstellung von Modellen verwendet, dies die Lösung bietet, aber das ist nicht genug für mich (Ich habe eine ähnliche Lösung, nur ein bisschen einfacher :)) – forhas

+0

Ja natürlich. Im Allgemeinen kann jeder neue Objekte erstellen, wenn er "neu" verwenden möchte, oder er könnte seinen eigenen Injektor erstellen, wenn er eine eigene Liste von Modulen zusammenstellen möchte. Wenn Sie nicht möchten, dass Ihre JVM-Richtlinien verwaltet werden und welche Klassen auf welche anderen Klassen zugreifen dürfen, gibt es keine Möglichkeit, dies zu verhindern. Das Ziel ist also, es den Menschen so leicht wie möglich zu machen, Dinge "richtig" zu machen. Und wie ich schon sagte, es ist unwahrscheinlich, dass Sie eine Lösung finden, die sich sehr zufriedenstellend anfühlt. –