Pregunta

Tenemos una situación en la que se proporciona una configuración externa en forma de un mapa de nuestros programas en ejecución. He encontrado que JSR-330 Inyección de dependencia da una forma más limpia mucho que usar ese mapa de configuración en el código en lugar de pasar el mapa alrededor o utilizar JNDI para conseguirlo.

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

permite el relleno aplicación JSR-330 en este campo automáticamente.

Con Guice puedo configurar el valor con

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

Me gustaría ser capaz de hacer lo mismo en Weld (bind "server.username" para por ejemplo "foobar") y entiendo que el mecanismo más probable es beans.xml, pero yo preferiría un simple "alimentar este mapa de Weld, por favor" alternativa de código. Lo que sería una buena manera de hacer esto?


EDITAR 10/16/2013: Después de mirar en la daga que funciona en tiempo de compilación en tiempo de ejecución y no, he encontrado que con nosotros por lo general con un 10-20 por programa que podría vivir con tener un método @Provider para cada cadena de configuración que luego mira en el mapa de configuración. Esto permite que para el comportamiento método específico (incluyendo los valores por defecto), capacidad de proporcionar javadoc, y la capacidad para poner todos estos métodos en la misma clase. También funciona bien con autógena de la caja. Estoy pensando en escribir una explicación más completa en una entrada de blog.

¿Fue útil?

Solución

Me gustaría que recompensa ahora, por favor. Calcular esto me ha enseñado mucho acerca de las entrañas de la soldadura, y aquí está la lección más interesante:. @Named es un partido de clasificación, y debe ser tratado como tal si va a ser capaz de igualar en contra de ella

tengo una advertencia para usted: Si se echa en falta algún valor en su aplicación, se producirá un error en el despliegue o el tiempo de carga. Esto puede ser conveniente para usted, pero sí significa específicamente que los valores "por defecto" no son posibles.

El punto de inyección se especifica exactamente como usted tiene más arriba, y aquí está el código de extensión necesaria para hacer que funcione:

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

    }
}

He probado que este trabajó haciendo una aplicación 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);
    }

}

Por último, no se olvide /META-INF/services/javax.enterprise.inject.spi.Extension, en sustitución de weldtest con la ruta de clase que utilice:

weldtest.PerformSetup

Esto debería hacer todo este trabajo. Déjame saber si llegas a tener ninguna dificultad y te envío mi proyecto de prueba.

Otros consejos

No todo lo que interesado en la recompensa, pero me lo llevo si todavía está sobre la mesa. Esto es muy similar a un código que estoy usando en $ Dayjob, y por lo tanto esto no es teoría, es lo que yo uso en el código de producción, pero modificado para proteger a los culpables. No he tratado de compilar el código modificado, lo que se advirtió que puedo haber cometido algunos errores en el cambio de nombres y tal, pero los principios involucrados aquí se han probado y trabajo.

En primer lugar, se necesita un titular calificador. Uso @NonBinding para mantener soldadura contra la búsqueda sólo a los calificadores con valor, ya que queremos que todos los valores de esta clasificación particular, para que coincida con un único punto de inyección. Al mantener el calificador y el valor de la misma anotación, puede no sólo "se olvide" uno de ellos por accidente. (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();
}

A continuación, se necesita un método de productor, que sabe cómo obtener el mapa. Probablemente debería tener un bean llamado que contiene el método del productor, así que o bien puede inicializar explícitamente el valor mediante el uso de getters / setters, o bien tener el grano de inicialización por usted.

debe especificar un valor en blanco para el partido en el método del productor para evitar errores de tiempo de compilación, pero nunca ha utilizado en la práctica.

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

El uso es simple:

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

Puede ser posible llevar a cabo esto como un método Productor @Dependent que sí inyecta un @InjectionPoint lo que permitiría a reflexionar sobre el campo que estás siendo inyectado en - esto permitiría que usted se asoma en una anotación personalizada (no un partido de clasificación) miembro en el campo de averiguar el val desea volver

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

manera la ejecución de una costumbre Weld InjectionServices no ser una opción aquí?

¿qué pasa

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

Javadoc: http://download.oracle. com / JavaSE / 6 / docs / api / javax / anotación / Resource.html

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top