Ich verwende Anmerkungen, um meine Federumgebung wie folgt zu konfigurieren:
@Configuration
...
@PropertySource("classpath:/config/default.properties")
...
public class GeneralApplicationConfiguration implements WebApplicationInitializer
{
@Autowired
Environment env;
}
Dies führt dazu, dass meine Eigenschaften default.properties
nicht Teil der Environment
. Ich möchte den @PropertySource
Mechanismus hier verwenden, da er bereits die Möglichkeit bietet, Eigenschaften über mehrere Fallback-Ebenen und verschiedene dynamische Speicherorte hinweg zu überladen, basierend auf den Umgebungseinstellungen (z. B. config_dir-Speicherort). Ich habe gerade den Fallback entfernt, um das Beispiel zu vereinfachen.
Mein Problem ist jetzt jedoch, dass ich beispielsweise meine Datenquelleneigenschaften in konfigurieren möchte default.properties
. Sie können die Einstellungen an die Datenquelle übergeben, ohne genau zu wissen, welche Einstellungen die Datenquelle erwartet
Properties p = ...
datasource.setProperties(p);
Das Problem ist jedoch, dass das Environment
Objekt weder ein Properties
Objekt noch ein Map
oder etwas Vergleichbares ist. Aus meiner Sicht ist es einfach nicht möglich, auf alle Werte der Umgebung zuzugreifen, da es keine keySet
oder iterator
Methode oder etwas Vergleichbares gibt.
Properties p <=== Environment env?
Vermisse ich etwas Ist es Environment
irgendwie möglich, auf alle Einträge des Objekts zuzugreifen ? Wenn ja, könnte ich die Einträge einem Map
oder einem Properties
Objekt zuordnen, ich könnte sie sogar nach Präfix filtern oder zuordnen - Teilmengen als Standard-Java erstellen Map
... Dies ist, was ich tun möchte. Irgendwelche Vorschläge?
applicationContext.getEnvironment().getProperty(key)
auflösenDies ist eine alte Frage, aber die akzeptierte Antwort weist einen schwerwiegenden Fehler auf. Wenn das Spring-
Environment
Objekt überschreibende Werte enthält (wie unter Externalisierte Konfiguration beschrieben ), kann nicht garantiert werden, dass die von ihm erzeugte Zuordnung der Eigenschaftswerte mit den vom Objekt zurückgegebenen Werten übereinstimmtEnvironment
. Ich fand heraus, dass das einfache Durchlaufen desPropertySource
s desEnvironment
in der Tat keine übergeordneten Werte ergab. Stattdessen wurde der ursprüngliche Wert erzeugt, der überschrieben werden sollte.Hier ist eine bessere Lösung. Dies verwendet das
EnumerablePropertySource
s vonEnvironment
, um die bekannten Eigenschaftsnamen zu durchlaufen, liest dann aber den tatsächlichen Wert aus der realen Spring-Umgebung. Dies garantiert, dass der Wert der tatsächlich von Spring aufgelöste Wert ist, einschließlich aller überschreibenden Werte.Properties props = new Properties(); MutablePropertySources propSrcs = ((AbstractEnvironment) springEnv).getPropertySources(); StreamSupport.stream(propSrcs.spliterator(), false) .filter(ps -> ps instanceof EnumerablePropertySource) .map(ps -> ((EnumerablePropertySource) ps).getPropertyNames()) .flatMap(Arrays::<String>stream) .forEach(propName -> props.setProperty(propName, springEnv.getProperty(propName)));
quelle
collect
Stream integrierten Methode erfassen, anstatt Folgendes auszuführenforEach
:.distinct().collect(Collectors.toMap(Function.identity(), springEnv::getProperty))
. Wenn Sie es in einer Eigenschaft anstelle einer Karte sammeln müssen, können Sie die Version mit vier Argumenten von verwendencollect
.springEnv
? Woher kommt das? Unterscheidet es sich vonenv
der akzeptierten Lösung?springEnv
ist dasenv
Objekt der ursprünglichen Frage & akzeptierte Lösung. Ich hätte den Namen wohl gleich behalten sollen.ConfigurableEnvironment
die Besetzung verwenden und müssen sie nicht machen.Ich musste alle Eigenschaften abrufen, deren Schlüssel mit einem bestimmten Präfix beginnt (z. B. alle Eigenschaften, die mit "log4j.appender" beginnen) und schrieb folgenden Code (unter Verwendung von Streams und Lamdas von Java 8).
public static Map<String,Object> getPropertiesStartingWith( ConfigurableEnvironment aEnv, String aKeyPrefix ) { Map<String,Object> result = new HashMap<>(); Map<String,Object> map = getAllProperties( aEnv ); for (Entry<String, Object> entry : map.entrySet()) { String key = entry.getKey(); if ( key.startsWith( aKeyPrefix ) ) { result.put( key, entry.getValue() ); } } return result; } public static Map<String,Object> getAllProperties( ConfigurableEnvironment aEnv ) { Map<String,Object> result = new HashMap<>(); aEnv.getPropertySources().forEach( ps -> addAll( result, getAllProperties( ps ) ) ); return result; } public static Map<String,Object> getAllProperties( PropertySource<?> aPropSource ) { Map<String,Object> result = new HashMap<>(); if ( aPropSource instanceof CompositePropertySource) { CompositePropertySource cps = (CompositePropertySource) aPropSource; cps.getPropertySources().forEach( ps -> addAll( result, getAllProperties( ps ) ) ); return result; } if ( aPropSource instanceof EnumerablePropertySource<?> ) { EnumerablePropertySource<?> ps = (EnumerablePropertySource<?>) aPropSource; Arrays.asList( ps.getPropertyNames() ).forEach( key -> result.put( key, ps.getProperty( key ) ) ); return result; } // note: Most descendants of PropertySource are EnumerablePropertySource. There are some // few others like JndiPropertySource or StubPropertySource myLog.debug( "Given PropertySource is instanceof " + aPropSource.getClass().getName() + " and cannot be iterated" ); return result; } private static void addAll( Map<String, Object> aBase, Map<String, Object> aToBeAdded ) { for (Entry<String, Object> entry : aToBeAdded.entrySet()) { if ( aBase.containsKey( entry.getKey() ) ) { continue; } aBase.put( entry.getKey(), entry.getValue() ); } }
Beachten Sie, dass der Ausgangspunkt die ConfigurableEnvironment ist, die die eingebetteten PropertySources zurückgeben kann (die ConfigurableEnvironment ist ein direkter Nachkomme von Environment). Sie können es automatisch verdrahten, indem Sie:
@Autowired private ConfigurableEnvironment myEnv;
Wenn Sie keine ganz besonderen Arten von Eigenschaftsquellen verwenden (wie JndiPropertySource, die normalerweise in der Frühjahrsautokonfiguration nicht verwendet wird), können Sie alle in der Umgebung enthaltenen Eigenschaften abrufen.
Die Implementierung basiert auf der Iterationsreihenfolge, die Spring selbst bereitstellt, und übernimmt die erste gefundene Eigenschaft. Alle später gefundenen Eigenschaften mit demselben Namen werden verworfen. Dies sollte das gleiche Verhalten sicherstellen, als ob die Umgebung direkt nach einer Eigenschaft gefragt würde (Rückgabe der zuerst gefundenen).
Beachten Sie auch, dass die zurückgegebenen Eigenschaften noch nicht aufgelöst sind, wenn sie Aliase mit dem Operator $ {...} enthalten. Wenn Sie einen bestimmten Schlüssel lösen möchten, müssen Sie die Umgebung erneut direkt fragen:
myEnv.getProperty( key );
quelle
NullPointerException
in meinen Unit-Tests, wenn es versucht, die@Autowired
Instanz von zu bekommenConfigurationEnvironment
.Die ursprüngliche Frage deutete an, dass es schön wäre, alle Eigenschaften anhand eines Präfixes filtern zu können. Ich habe gerade bestätigt, dass dies ab Spring Boot 2.1.1.RELEASE für
Properties
oder funktioniertMap<String,String>
. Ich bin sicher, es hat schon eine Weile funktioniert. Interessanterweise funktioniert es nicht ohne dieprefix =
Qualifikation, dh ich weiß nicht , wie ich die gesamte Umgebung in eine Karte laden kann. Wie gesagt, dies könnte tatsächlich das sein, womit OP beginnen wollte. Das Präfix und das folgende '.' wird abgestreift, was sein könnte oder nicht, was man will:@ConfigurationProperties(prefix = "abc") @Bean public Properties getAsProperties() { return new Properties(); } @Bean public MyService createService() { Properties properties = getAsProperties(); return new MyService(properties); }
Nachtrag: Es ist in der Tat möglich und schändlich einfach, die gesamte Umgebung zu erhalten. Ich weiß nicht, wie mir das entgangen ist:
@ConfigurationProperties @Bean public Properties getProperties() { return new Properties(); }
quelle
getAsProperties()
immer eine leereProperties
Instanz zurück, und wenn Sie es ohne ein angegebenes Präfix versuchen, kann es nicht einmal kompiliert werden. Dies ist mit Spring Boot 2.1.6.RELEASEAls Jira-Ticket für diesen Frühling ist es ein absichtliches Design. Aber der folgende Code funktioniert für mich.
public static Map<String, Object> getAllKnownProperties(Environment env) { Map<String, Object> rtn = new HashMap<>(); if (env instanceof ConfigurableEnvironment) { for (PropertySource<?> propertySource : ((ConfigurableEnvironment) env).getPropertySources()) { if (propertySource instanceof EnumerablePropertySource) { for (String key : ((EnumerablePropertySource) propertySource).getPropertyNames()) { rtn.put(key, propertySource.getProperty(key)); } } } } return rtn; }
quelle
Der Frühling erlaubt keine Entkopplung
java.util.Properties
von der Frühlingsumgebung.Funktioniert aber
Properties.load()
immer noch in einer Spring-Boot-Anwendung:Properties p = new Properties(); try (InputStream is = getClass().getResourceAsStream("/my.properties")) { p.load(is); }
quelle
Die anderen Antworten haben die Lösung für die meisten Fälle aufgezeigt
PropertySources
, aber keiner hat erwähnt, dass bestimmte Eigenschaftsquellen nicht in nützliche Typen umgewandelt werden können.Ein solches Beispiel ist die Eigenschaftsquelle für Befehlszeilenargumente. Die verwendete Klasse ist
SimpleCommandLinePropertySource
. Diese private Klasse wird von einer öffentlichen Methode zurückgegeben, was den Zugriff auf die Daten im Objekt äußerst schwierig macht. Ich musste Reflektion verwenden, um die Daten zu lesen und schließlich die Eigenschaftsquelle zu ersetzen.Wenn jemand da draußen eine bessere Lösung hat, würde ich sie wirklich gerne sehen. Dies ist jedoch der einzige Hack, den ich zur Arbeit gebracht habe.
quelle
Bei der Arbeit mit Spring Boot 2 musste ich etwas Ähnliches tun. Die meisten der oben genannten Antworten funktionieren einwandfrei. Beachten Sie jedoch, dass die Ergebnisse in verschiedenen Phasen des App-Lebenszyklus unterschiedlich sind.
Zum Beispiel, nachdem
ApplicationEnvironmentPreparedEvent
irgendwelche Eigenschaften im Innerenapplication.properties
nicht vorhanden sind. Nach einemApplicationPreparedEvent
Ereignis sind sie jedoch.quelle
Bei Spring Boot überschreibt die akzeptierte Antwort doppelte Eigenschaften mit niedrigeren Eigenschaften . Diese Lösung sammelt die Eigenschaften in a
SortedMap
und verwendet nur doppelte Eigenschaften mit der höchsten Priorität.final SortedMap<String, String> sortedMap = new TreeMap<>(); for (final PropertySource<?> propertySource : env.getPropertySources()) { if (!(propertySource instanceof EnumerablePropertySource)) continue; for (final String name : ((EnumerablePropertySource<?>) propertySource).getPropertyNames()) sortedMap.computeIfAbsent(name, propertySource::getProperty); }
quelle
Ich dachte, ich würde noch einen Weg hinzufügen. In meinem Fall gebe ich dies an, für
com.hazelcast.config.XmlConfigBuilder
das nurjava.util.Properties
einige Eigenschaften in der Hazelcast-XML-Konfigurationsdatei aufgelöst werden müssen, dh nur diegetProperty(String)
Methode aufgerufen wird. So konnte ich tun, was ich brauchte:@RequiredArgsConstructor public class SpringReadOnlyProperties extends Properties { private final org.springframework.core.env.Environment delegate; @Override public String getProperty(String key) { return delegate.getProperty(key); } @Override public String getProperty(String key, String defaultValue) { return delegate.getProperty(key, defaultValue); } @Override public synchronized String toString() { return getClass().getName() + "{" + delegate + "}"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; SpringReadOnlyProperties that = (SpringReadOnlyProperties) o; return delegate.equals(that.delegate); } @Override public int hashCode() { return Objects.hash(super.hashCode(), delegate); } private void throwException() { throw new RuntimeException("This method is not supported"); } //all methods below throw the exception * override all methods * }
PS Ich habe dies letztendlich nicht speziell für Hazelcast verwendet, da es nur Eigenschaften für XML-Dateien auflöst, jedoch nicht zur Laufzeit. Da ich auch Spring benutze, habe ich mich für einen Brauch entschieden
org.springframework.cache.interceptor.AbstractCacheResolver#getCacheNames
. Dadurch werden Eigenschaften für beide Situationen aufgelöst, zumindest wenn Sie Eigenschaften in Cache-Namen verwenden.quelle