溶接で文字列定数を簡単に注入する方法は?
-
28-09-2019 - |
質問
実行中のプログラムにマップの形で外部構成を提供する状況があります。 JSR-330依存関係注入により、マップを渡す代わりに、またはJNDIを使用してそれを取得する代わりに、コードでその構成マップを使用するよりクリーンな方法が得られることがわかりました。
@Inject @Named("server.username") String username;
JSR-330の実装がこのフィールドに自動的に入力できます。
Guiceを使用すると、値を設定できます
bindConstant().annotatedWith(Names.named(key)).to(value);
溶接で同じことをすることができます(「server.username」を「foobar」にバインドします)。メカニズムはBeans.xmlである可能性が高いことを理解していますが、このマップを溶接にフィードすることを好むでしょう。 、「代替」をコードしてください。これを行う良い方法は何でしょうか?
編集2013-10-16:ランタイムではなくコンパイル時間で機能する短剣を調べた後、私たちは通常、プログラムごとに10-20を持っていることで、 @Provider
構成マップで検索する各構成文字列のメソッド。これにより、メソッド固有の動作(デフォルト値を含む)、Javadocを提供する機能、およびこれらすべてのメソッドを同じクラスに配置する機能が可能になります。また、箱から出して溶接でうまく機能します。ブログエントリでより詳細な説明を書くことを検討しています。
解決
今すぐその賞金をお願いします。これを理解することで、溶接の内臓についてかなり教えてくれました。これが最も興味深いレッスンです。@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.PerformSetup
これにより、このすべてが機能するはずです。困難に遭遇した場合はお知らせください。テストプロジェクトをお送りします。
他のヒント
賞金にそれほど興味があるわけではありませんが、まだテーブルの上にある場合はそれを取ります。これは、$ dayjobで使用しているコードと非常に似ているため、これは理論ではなく、生産コードで使用しているものですが、有罪を保護するために変更されています。変更されたコードをコンパイルしようとしていないので、名前を変更するなど、いくつかのエラーを犯した可能性があることに注意してください。ここに関係する原則はすべてテストされています。
まず、バリューホルダー予選が必要です。 @nonbindingを使用して、この特定の修飾子のすべての値が単一の注入点に一致するようにするため、溶接を同一の値で予選者のみに一致させないようにします。予選と価値を同じ注釈に保つことにより、偶然にそれらの1つを「忘れる」ことはできません。 (キス原則)
@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();
}
次に、マップを取得する方法を知っているプロデューサーの方法が必要です。おそらく、プロデューサーの方法を保持する名前のBeanを持っている必要があります。したがって、Getters/Settersを使用して値を明示的に初期化するか、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;
注入されているフィールドを反映できる@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.
カスタム溶接を実装します 噴射サービス ここでオプションではありませんか?
どうですか
@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