Das Hauptproblem ist, dass die ConfigSource sehr früh instanziiert wird, wenn der BeanManager noch nicht verfügbar ist. Selbst die JNDI-Suche funktioniert zu diesem Zeitpunkt nicht. Daher muss ich die Injektion/Suche verzögern.
Was ich jetzt gemacht habe, ist ein statischer boolean zu meiner Config-Quelle hinzuzufügen, die ich manuell einstelle. Wir haben einen InitializerService, der sicherstellt, dass das System richtig eingerichtet ist. Am Ende des Initialisierungsprozesses rufe ich allowInitialization()
an, um der Konfigurationsquelle mitzuteilen, dass die Bean jetzt injizierbar ist. Wenn die ConfigSource das nächste Mal gefragt wird, kann sie die Bean unter Verwendung von BeanProvider.injectFields
injizieren.
public class DatabaseConfigSource implements ConfigSource {
private static boolean allowInit;
@Inject
private ConfigParameterProvider configParameterProvider;
@Override
public int getOrdinal() {
return 500;
}
@Override
public String getPropertyValue(String key) {
initIfNecessary();
if (configParameterProvider == null) {
return null;
}
return configParameterProvider.getProperty(key);
}
public static void allowInitialization() {
allowInit = true;
}
private void initIfNecessary() {
if (allowInit) {
BeanProvider.injectFields(this);
}
}
}
Ich habe eine Anfrage-Bean, die alle meine Config-Variablen für Typ-Safe-Zugriff enthält.
@RequestScoped
public class Configuration {
@Inject
@ConfigProperty(name = "myProperty")
private String myProperty;
@Inject
@ConfigProperty(name = "myProperty2")
private String myProperty2;
....
}
Beim Injizieren der Konfigurationsklasse in eine andere Bean wird jede ConfigProperty aufgelöst. Da meine benutzerdefinierte DatabaseConfigSource die höchste Ordnungszahl (500) hat, wird sie zuerst für die Eigenschaftsauflösung verwendet. Wenn die Eigenschaft nicht gefunden wird, wird die Auflösung an die nächste ConfigSource delegiert.
Für jede ConfigProperty wird die getPropertyValue
-Funktion von DatabaseConfigSource aufgerufen. Da ich die Parameter für jede Konfigurationseigenschaft nicht aus der Datenbank abrufen möchte, habe ich die Auflösung der Konfigurationseigenschaft in eine Anforderungsbereichs-Bean verschoben.
@RequestScoped
public class ConfigParameterProvider {
@Inject
private ConfigParameterDao configParameterDao;
private Map<String, String> configParameters = new HashMap<>();
@PostConstruct
public void init() {
List<ConfigParameter> configParams = configParameterDao.findAll();
configParameters = configParams.stream()
.collect(toMap(ConfigParameter::getId, ConfigParameter::getValue));
}
public String getProperty(String key) {
return configParameters.get(key);
}
}
Ich könnte sicher die Request-Bereich ConfigParameterProvider zu ApplicationScoped ändern. Wir haben jedoch ein Multi-Tenant-Setup und die Parameter müssen pro Anfrage aufgelöst werden.
Wie Sie sehen können, ist dies ein bisschen hacky, weil wir explizit die ConfigSource mitteilen müssen, wenn es richtig instanziiert werden darf (inject the bean).
Ich würde eine standardisierte Lösung von DeltaSpike für die Verwendung von CDI in einer ConfigSource bevorzugen. Wenn Sie eine Idee haben, wie Sie dies richtig umsetzen können, lassen Sie es mich wissen.
Sie können 'initIfNecessary()' noch verbessern - im Moment funktioniert es eher wie 'initIfPossible()', dh die Felder werden nicht zu früh injiziert, aber sie werden bei jedem Aufruf von 'getPropertyValue()' injiziert . –
Die Version 1.8 wird dies beheben. –