Frage

Wir haben eine Situation, wo wir eine externe Konfiguration in Form einer Karte zu unseren laufenden Programmen zur Verfügung stellen. Ich habe festgestellt, dass JSR-330 Dependency Injection einen viel sauberen Weg gibt, dass die Konfiguration der Karte im Code zu verwenden, anstatt die Karte von herumgereicht oder JNDI zu verwenden, um es zu erhalten.

@Inject @Named("server.username") String username;

läßt die JSR-330 Implementierung fill in diesem Bereich automatisch.

Mit Guice ich den Wert mit

bindConstant().annotatedWith(Names.named(key)).to(value);

Ich möchte in der Lage sein, das gleiche in Weld zu tun (bind „server.username“ zB in „foobar“), und ich verstehe, dass der Mechanismus wahrscheinlich beans.xml ist, aber ich würde es vorziehen, eine einfache „füttern diese Karte zu Weld, bitte“Code Alternative. Was wäre ein guter Weg, dies zu tun?


EDIT 2013.10.16: Nach dem in Dolch suchen, die bei der Kompilierung funktioniert und nicht Laufzeit, fand ich, dass bei uns in die Regel 10 bis 20 pro Programm mit uns mit einer @Provider Methode für jedes Konfigurationsstring leben könnten, die dann aussieht up in der Konfigurationskarte. Dies ermöglicht eine Methode ein bestimmtes Verhalten (einschließlich Standardwerte), die Fähigkeit, javadoc zu schaffen, und die Fähigkeit, alle diese Methoden in der gleichen Klasse zu setzen. Auch funktioniert es gut mit Weld aus dem Kasten heraus. Ich erwäge eine ausführlichere Erklärung in einem Blog-Eintrag zu schreiben.

War es hilfreich?

Lösung

würde ich, wie die Prämie jetzt bitte. Bezifferung dieses heraus lehrte mich eine ganze Menge über die Innereien von WELD, und hier ist die interessanteste Lektion. @Named ein Qualifikationsspiel ist, und müssen als solche behandelt werden, wenn Sie in der Lage sein werden, gegen sie entsprechen

Ich habe eine Warnung haben für Sie: Wenn Sie irgendwelche Werte in Ihrer App fehlen, wird es bei deploy oder Ladezeit ausfallen. Dies kann für Sie wünschenswert sein, aber es bedeutet insbesondere, dass „default“ Werte sind nicht möglich.

Die Injektionsstelle spezifiziert genau, wie Sie oben haben, und hier ist die Erweiterung Code benötigt, damit es funktioniert:

@ApplicationScoped
public class PerformSetup implements Extension {

    Map<String, String> configMap;

    public PerformSetup() {
        configMap = new HashMap<String, String>();
        // This is a dummy initialization, do something constructive here
        configMap.put("string.value", "This is a test value");
    }

    // Add the ConfigMap values to the global bean scope
    void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager bm) {
        // Loop through each entry registering the strings.
        for (Entry<String, String> configEntry : configMap.entrySet()) {
            final String configKey = configEntry.getKey();
            final String configValue = configEntry.getValue();

            AnnotatedType<String> at = bm.createAnnotatedType(String.class);
            final InjectionTarget<String> it = bm.createInjectionTarget(at);

            /**
             * All of this is necessary so WELD knows where to find the string,
             * what it's named, and what scope (singleton) it is.
             */ 
            Bean<String> si = new Bean<String>() {

                public Set<Type> getTypes() {
                    Set<Type> types = new HashSet<Type>();
                    types.add(String.class);
                    types.add(Object.class);
                    return types;
                }

                public Set<Annotation> getQualifiers() {
                    Set<Annotation> qualifiers = new HashSet<Annotation>();
                    qualifiers.add(new NamedAnnotationImpl(configKey));
                    return qualifiers;

                }

                public Class<? extends Annotation> getScope() {
                    return Singleton.class;
                }

                public String getName() {
                    return configKey;
                }

                public Set<Class<? extends Annotation>> getStereotypes() {
                    return Collections.EMPTY_SET;
                }

                public Class<?> getBeanClass() {
                    return String.class;
                }

                public boolean isAlternative() {
                    return false;
                }

                public boolean isNullable() {
                    return false;
                }

                public Set<InjectionPoint> getInjectionPoints() {
                    return it.getInjectionPoints();
                }

                @Override
                public String create(CreationalContext<String> ctx) {
                    return configValue;

                }

                @Override
                public void destroy(String instance,
                        CreationalContext<String> ctx) {
                    // Strings can't be destroyed, so don't do anything
                }
            };
            abd.addBean(si);
        }
    }

    /**
     * This is just so we can create a @Named annotation at runtime.
     */
    class NamedAnnotationImpl extends AnnotationLiteral<Named> implements Named {
        final String nameValue;

        NamedAnnotationImpl(String nameValue) {
            this.nameValue = nameValue;
        }

        public String value() {
            return nameValue;
        }

    }
}

Getestet habe ich, dass dies, indem sie eine WELD-SE App gearbeitet:

@ApplicationScoped
public class App {

    @Inject
    @Parameters
    List<String> parameters;

    @Inject
    @Named("string.value")
    String stringValue;

    public void printHello(@Observes ContainerInitialized event) {
        System.out.println("String Value is " + stringValue);
    }

}

Schließlich, vergessen Sie nicht /META-INF/services/javax.enterprise.inject.spi.Extension, ersetzt weldtest mit dem Classpath Sie verwenden:

weldtest.PerformSetup

Das sollte all diese Arbeit machen. Lassen Sie mich wissen, wenn Sie irgendwelche Schwierigkeiten stoßen auf und ich werde Ihnen mein Testprojekt senden.

Andere Tipps

Nicht alles, was in der Prämie interessiert, aber ich werde es nehmen, wenn es auf dem Tisch noch ist. Dies ist sehr ähnlich wie einige Code, den ich bei $ Dayjob verwenden, und so diese nicht Theorie ist, dann ist es, was ich in der Produktion Code verwenden, aber modifiziert, um die Schuldigen zu schützen. Ich habe nicht versucht, den geänderten Code kompilieren, so gewarnt werden, dass ich in wechselnden Namen und so einige Fehler gemacht haben, aber die hier beteiligten Prinzipien alle getestet wurden und zu arbeiten.

Zuerst müssen Sie einen Wert Halter Qualifier. Verwenden @Nonbinding halten WELD von passenden NUR-Qualifikation mit identischen Werten, da wir alle Werte dieser besonderen Qualifier wollen einen einzigen Anspritzpunkt entsprechen. Durch die Beibehaltung der Qualifier und Wert in der gleichen Anmerkung, können Sie nicht einfach „vergessen“, einer von ihnen durch Zufall. (KISS-Prinzip)

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface ConfigValue {
    // Excludes this value from being considered for injection point matching
    @Nonbinding 
    // Avoid specifying a default value, since it can encourage programmer error.
    // We WANT a value every time.
    String value();
}

Als nächstes müssen Sie eine Producer-Methode, die weiß, wie die Karte zu bekommen. Sie sollten wahrscheinlich eine Named Bohne haben, die die Producer-Methode hält, so können Sie entweder den Wert explizit initialisieren von Getter / Setter verwendet, oder haben die Bohne initialize es für Sie.

Wir müssen einen leeren Wert für das Qualifikationsspiel auf der Producer-Methode angeben Kompilierung Fehler zu vermeiden, aber es wird nie in der Praxis verwendet wird.

@Named
public class ConfigProducer {
    //@Inject // Initialize this parameter somehow
    Map<String,String> configurationMap;

    @PostConstructor
    public void doInit() {
         // TODO: Get the configuration map here if it needs explicit initialization
    }

    // In general, I would discourage using this method, since it can be difficult to control exactly the order in which beans initialize at runtime.
    public void setConfigurationMap(Map<String,String> configurationMap) {
        this.configurationMap = configurationMap;
    }

    @Produces
    @ConfigValue("")
    @Dependent
    public String configValueProducer(InjectionPoint ip) {
        // We know this annotation WILL be present as WELD won't call us otherwise, so no null checking is required.
        ConfigValue configValue = ip.getAnnotated().getAnnotation(ConfigValue.class);
        // This could potentially return a null, so the function is annotated @Dependent to avoid a WELD error.
        return configurationMap.get(configValue.value());
    }
}

Die Verwendung ist einfach:

@Inject
@ConfigValue("some.map.key.here")
String someConfigValue;

Es kann möglich sein, dies als @Dependent Producer Verfahren implementieren, dass selbst eine @InjectionPoint injiziert, die Sie auf dem Feld wir Sie in injiziert zu reflektieren erlauben würde - dies würde Sie in eine benutzerdefinierte Anmerkung peek (nicht ein Qualifier) ??Mitglied auf dem Feld, um herauszufinden, die val Sie zurückgeben möchten

@Inject @ConfigMapQualifier @Val("user.name") String user;

...

@Produces @ConfigMapQualifier configProducr(...) { 
...
@Inject InjectionPoint ip;

// use e.g. ip/getJavaMember() then reflection to figure out the @Val value membr.

würde eine benutzerdefinierte Weld InjectionServices keine Option sein, denn hier?

was ist

@Resource(name = "server.username", type = java.lang.String.class)
private String injectTo;

Javadoc: http://download.oracle. com / JavaSE / 6 / docs / api / javax / Anmerkung / Resource.html

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top