16

実行中のプログラムにマップの形式で外部構成を提供する状況があります。JSR-330 Dependency Injection は、マップを渡したり、JNDI を使用して取得したりする代わりに、コード内でその構成マップを使用するためのよりクリーンな方法を提供することを発見しました。

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

JSR-330 実装がこのフィールドに自動的に入力できるようにします。

Guiceを使用すると、値を設定できます

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

Weld でも同じことができるようにしたい (「server.username」を「foobar」などにバインドする) メカニズムはbeans.xml である可能性が最も高いことを理解していますが、単純な「このマップを Weld にフィードする」ことをお勧めします。 、してください」コードの代替。これを行うにはどうすればよいでしょうか。


@ProviderEDIT 2013-10-16: 実行時ではなくコンパイル時に動作する Dagger を調べたところ、通常、プログラムごとに 10 ~ 20 個の Dagger があるため、各構成文字列のメソッドを使用して、構成を検索することができることがわかりました。地図。これにより、メソッド固有の動作 (デフォルト値を含む)、javadoc を提供する機能、およびこれらすべてのメソッドを同じクラスに配置する機能が可能になります。また、すぐに使用できる Weld でもうまく機能します。ブログエントリでより完全な説明を書くことを検討しています。

4

5 に答える 5

12

その報奨金を今すぐお願いします。これを理解することで、WELD の内部についてかなりのことを学びました。これが最も興味深い教訓です。@Named は修飾子であり、それに対して照合できるようにする場合は、そのように扱わなければなりません。

警告があります。アプリで値が欠落している場合、デプロイまたはロード時に失敗します。これは望ましいことかもしれませんが、「デフォルト」値が使用できないことを明確に意味します。

注入ポイントは上記とまったく同じように指定されています。これを機能させるために必要な拡張コードは次のとおりです。

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

    }
}

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

}

最後に、/META-INF/services/javax.enterprise.inject.spi.Extension を忘れずに、weldtest を使用するクラスパスに置き換えます。

weldtest.PerformSetup

これですべてが機能するはずです。問題が発生した場合はお知らせください。テスト プロジェクトをお送りします。

于 2011-01-25T04:40:08.037 に答える
11

報奨金にはあまり興味がありませんが、まだテーブルにある場合は受け取ります。これは、私が $DAYJOB で使用しているコードと非常によく似ています。これは理論ではなく、実稼働コードで使用するものですが、有罪を保護するために変更されています。変更したコードをコンパイルしようとはしていないので、名前の変更などでいくつかのエラーを犯した可能性があることに注意してください。ただし、ここに含まれる原則はすべてテストされ、機能しています。

まず、Value Holder Qualifier が必要です。@Nonbinding を使用して、同じ値を持つ修飾子のみに WELD が一致しないようにします。これは、この特定の修飾子のすべての値を単一の注入ポイントに一致させる必要があるためです。修飾子と値を同じアノテーションに保持することで、それらの 1 つを誤って「忘れる」ことはありません。(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();
}

次に、Map を取得する方法を知っているプロデューサー メソッドが必要です。おそらく、プロデューサー メソッドを保持する Named Bean が必要なので、getter/setter を使用して値を明示的に初期化するか、Bean に初期化させることができます。

コンパイル時のエラーを回避するために、プロデューサー メソッドの修飾子に空白の値を指定する必要がありますが、実際には使用されません。

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

使い方は簡単です:

@Inject
@ConfigValue("some.map.key.here")
String someConfigValue;
于 2011-01-20T09:37:29.930 に答える
0

カスタムWeldInjectionServicesを実装することはここではオプションではありませんか?

于 2010-11-22T11:03:27.983 に答える
0

これを @Dependent Producer メソッドとして実装し、それ自体が @InjectionPoint を注入して、注入されているフィールドを反映できるようにすることが可能になる場合があります。これにより、カスタム注釈 (修飾子ではなく) を覗くことができます。あなたが返したい値を把握するためにフィールドのメンバー

@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.
于 2011-01-04T19:35:42.203 に答える
0

どうですか

@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

于 2010-11-03T14:25:22.683 に答える