Domanda

Abbiamo una situazione in cui mettiamo a disposizione una configurazione esterna sotto forma di una mappa per i nostri programmi in esecuzione. Ho scoperto che JSR-330 Dependency Injection fornisce un modo più pulito tanto per usare quella mappa di configurazione nel codice invece di passare la mappa intorno o utilizzare JNDI per farlo.

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

consente automaticamente l'implementazione di riempimento JSR-330 in questo campo.

Con Guice posso impostare il valore con

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

Mi piacerebbe essere in grado di fare lo stesso in Weld (bind "server.username" per esempio "foobar") e ho capito che il meccanismo più probabile è beans.xml, ma io preferirei un semplice "alimentano questo mapping a Weld, per favore" codice alternativo. Quale sarebbe un buon modo per fare questo?


EDIT 2013/10/16: Dopo aver guardato in Dagger che opera in fase di compilazione e non runtime, ho scoperto che con noi di solito con 10-20 per programma potremmo vivere con avere un metodo @Provider per ogni stringa di configurazione che poi sguardi nella mappa di configurazione. Questo permette di comportamento metodo specifico (compresi i valori predefiniti), capacità di fornire javadoc, e la capacità di mettere tutti questi metodi nella stessa classe. Inoltre funziona bene con saldatura fuori dalla scatola. Sto considerando scrivere una spiegazione più completa in un blog.

È stato utile?

Soluzione

Mi piacerebbe che di taglie ora per favore. Capire questo fuori mi ha insegnato un po 'sulle interiora di WELD, ed ecco la lezione più interessante:. @Named è un qualificatore, e deve essere trattata come tale se si sta andando ad essere in grado di corrispondere contro di esso

Io ho un avvertimento per voi: se vi manca qualsiasi valore nella vostra applicazione, non riuscirà a implementare o caricare tempo. Questo può essere utile per voi, ma non specificamente significa che i valori di "default" non sono possibili.

Il punto di iniezione è specificato esattamente come avete sopra, ed ecco il codice di estensione necessario per farlo funzionare:

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

    }
}

Ho provato che questo ha lavorato facendo un'app WELD-SE:

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

}

Infine, non dimenticate /META-INF/services/javax.enterprise.inject.spi.Extension, sostituendo weldtest con il percorso di classe si utilizza:

weldtest.PerformSetup

che dovrebbe fare tutto questo lavoro. Fatemi sapere se si esegue in difficoltà e ti manderò il mio progetto di test.

Altri suggerimenti

Non tutto ciò che interessati alla bontà, ma la prendo se è ancora sul tavolo. Questo è molto simile a un codice che sto utilizzando a $ dayjob, e quindi questo non è teoria, è quello che uso nel codice di produzione, ma modificato per proteggere il colpevole. Non ho provato la compilazione del codice modificato, in modo da essere avvertito che forse ho fatto alcuni errori nella modifica dei nomi e simili, ma i principi coinvolti qui sono stati tutti testati e di lavoro.

In primo luogo, è necessario un supporto del valore Qualifier. Utilizzare @Nonbinding per mantenere WELD dalla corrispondenza SOLO alle qualificazioni con valori identici, dato che vogliamo tutti i valori di questo particolare qualificazione per indicare un singolo punto di iniezione. Mantenendo la qualificazione e il valore nella stessa annotazione, non si può semplicemente "dimenticare" uno di loro per caso. (Principio KISS)

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

Successivamente, è necessario un metodo produttore, che sa come ottenere la mappa. Probabilmente si dovrebbe avere un fagiolo nome che contiene il metodo produttore, in modo è possibile inizializzare in modo esplicito il valore utilizzando getter / setter, oppure avere il fagiolo di inizializzazione per voi.

Si deve specificare un valore vuoto per la qualificazione del metodo produttore per evitare errori in fase di compilazione, ma non è mai utilizzato in pratica.

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

L'uso è semplice:

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

E 'possibile implementare questo come un metodo produttore @Dependent che si inietta un @InjectionPoint che consentirebbe di riflettere sul campo sei stato iniettato - questo sarebbe consentono di sbirciare in un'annotazione personalizzato (non un qualificatore) membro sul campo per capire la val si desidera tornare

@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.

Sarebbe attuazione di una saldatura personalizzato InjectionServices non essere un'opzione qui?

che dire

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

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top