Question

Nous avons une situation où nous fournissons une configuration externe sous la forme d'une carte à nos programmes en cours d'exécution. J'ai trouvé que JSR-330 injection de dépendance donne une manière beaucoup plus propre à utiliser cette carte de configuration dans le code au lieu de passer la carte autour ou à utiliser JNDI pour l'obtenir.

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

permet le remplissage de la mise en œuvre JSR-330 dans ce domaine automatiquement.

Avec Guice je peux définir la valeur avec

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

Je voudrais pouvoir faire la même chose à souder (bind « server.username » à par exemple « foobar ») et je comprends que le mécanisme le plus probable est beans.xml, mais je préférerais un simple « nourrir cette carte à souder, s'il vous plaît » alternative code. Ce qui serait une bonne façon de le faire?


EDIT 16/10/2013: Après avoir examiné Dagger qui travaille à la compilation et non l'exécution, je trouve que nous habituellement avoir 10-20 par programme, nous pourrions vivre d'avoir une méthode de @Provider pour chaque chaîne de configuration qui puis des regards dans la carte de configuration. Cela permet un comportement spécifique de la méthode (y compris les valeurs par défaut), la capacité de fournir javadoc, et la capacité de mettre toutes ces méthodes dans la même classe. En outre, il fonctionne bien avec Weld hors de la boîte. Je songe à écrire une explication plus complète dans une entrée de blog.

Était-ce utile?

La solution

Je voudrais que bounty maintenant s'il vous plaît. Ceci m'a Figuring appris un peu sur les entrailles de Weld, et voici la leçon la plus intéressante. @Named est un qualificatif, et doit être traitée comme telle si vous allez être en mesure de faire correspondre contre

J'ai un avertissement pour vous: Si vous manque des valeurs dans votre application, il échouera au moment du déploiement ou de la charge. Cela peut être souhaitable pour vous, mais cela ne signifie en particulier que les valeurs « par défaut » ne sont pas possibles.

Le point d'injection est spécifié exactement comme vous avez ci-dessus, et voici le code d'extension nécessaire pour le faire fonctionner:

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

    }
}

Je l'ai testé que cela a fonctionné en faisant une application 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);
    }

}

Enfin, ne pas oublier /META-INF/services/javax.enterprise.inject.spi.Extension, en remplacement weldtest avec le classpath que vous utilisez:

weldtest.PerformSetup

Cela devrait faire tout ce travail. Faites-moi savoir si vous rencontrez des difficultés et je vais vous envoyer mon projet de test.

Autres conseils

Pas du tout intéressé à la générosité, mais je vais le prendre si elle est toujours sur la table. Ceci est très similaire à un code que je utilise à $ Dayjob, et donc ce n'est pas la théorie, il est ce que je l'utilise dans le code de production, mais modifié pour protéger les coupables. Je ne l'ai pas essayé de compiler le code modifié, alors soyez averti que je l'ai fait quelques erreurs dans la modification des noms et autres, mais les principes en cause ont tous été testés et fonctionnent.

Tout d'abord, vous avez besoin d'un porteur de valeur Qualifier. Utilisez @Nonbinding pour garder WELD de correspondre uniquement à des qualificatifs avec des valeurs identiques, puisque nous voulons que toutes les valeurs de ce qualificatif particulier pour correspondre à un seul point d'injection. En gardant la qualification et de la valeur dans la même annotation, vous ne pouvez pas « oublier » l'un d'entre eux par accident. (Principe 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();
}

Ensuite, vous avez besoin d'une méthode de production qui sait comment obtenir la carte. Vous devriez probablement avoir un haricot nommé qui détient la méthode de production, de sorte que vous pouvez initialiser explicitement la valeur en utilisant les getters / setters, ou bien ont le grain initialize pour vous.

Il faut spécifier une valeur vide pour le match de qualification sur la méthode de production afin d'éviter les erreurs de compilation, mais il n'a jamais utilisé dans la pratique.

@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'utilisation est simple:

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

Il est possible de mettre en œuvre cette méthode comme @Dependent du producteur qui s'injecte un @InjectionPoint qui vous permettra de réfléchir sur le champ que vous êtes d'être injecté dans - cela vous laisser jeter un regard dans une annotation personnalisée (non un qualificatif) membre sur le terrain pour déterminer la val vous voulez retourner

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

Would la mise en œuvre d'une mesure de soudure InjectionServices ne pas être une option ici?

Qu'en est-

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

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top