Tho*_*sen 15 java jsr299 jsr330 cdi jboss-weld
我们有一种情况,我们以Map的形式为我们正在运行的程序提供外部配置.我发现JSR-330依赖注入提供了一种更清晰的方式来在代码中使用该配置映射,而不是传递映射或使用JNDI来获取它.
@Inject @Named("server.username") String username;
Run Code Online (Sandbox Code Playgroud)
让JSR-330实现自动填写此字段.
使用Guice,我可以设置值
bindConstant().annotatedWith(Names.named(key)).to(value);
Run Code Online (Sandbox Code Playgroud)
我希望能够在Weld中做同样的事情(将"server.username"绑定到例如"foobar")并且我理解该机制最有可能是beans.xml,但我更喜欢简单的"将此地图提供给Weld" ,请"代码替代.这样做有什么好办法?
编辑2013-10-16:在调查Dagger,它在编译时工作而不是运行时,我发现我们通常每个程序有10-20个,我们可以使用@Provider
每个配置字符串的方法,然后在配置中查找地图.这允许方法特定的行为(包括默认值),提供javadoc的能力,以及将所有这些方法放在同一个类中的能力.它也可以与Weld开箱即用.我正在考虑在博客文章中写一个更全面的解释.
Spl*_*ity 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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我测试了这是通过制作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);
}
}
Run Code Online (Sandbox Code Playgroud)
最后,不要忘记/META-INF/services/javax.enterprise.inject.spi.Extension,将weldtest替换为您使用的类路径:
weldtest.PerformSetup
Run Code Online (Sandbox Code Playgroud)
这应该使所有这些工作.如果您遇到任何困难,请告诉我,我会将测试项目发给您.
Spl*_*ity 11
不是所有对赏金感兴趣的人,但如果它还在桌上,我会接受它.这与我在$ DAYJOB中使用的一些代码非常相似,所以这不是理论,它是我在生产代码中使用的,但是经过修改以保护内疚.我没有尝试编译修改后的代码,因此请注意我在更改名称等方面可能会犯一些错误,但这里涉及的原则都已经过测试和工作.
首先,您需要一个价值持有者限定符.使用@Nonbinding可以使WELD仅与具有相同值的限定符匹配,因为我们希望此特定限定符的所有值都匹配单个注入点.通过将限定符和值保持在相同的注释中,您不能偶然"忘记"其中一个注释.(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();
}
Run Code Online (Sandbox Code Playgroud)
接下来,您需要一个知道如何获取Map的producer方法.您可能应该有一个包含生产者方法的命名bean,因此您可以使用getter/setter显式初始化该值,或者让bean为您初始化它.
我们必须在producer方法上为限定符指定一个空值以避免编译时错误,但它从未在实践中使用过.
@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());
}
}
Run Code Online (Sandbox Code Playgroud)
用法很简单:
@Inject
@ConfigValue("some.map.key.here")
String someConfigValue;
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
6867 次 |
最近记录: |