如何轻松地向焊接注入字符串常数?
-
28-09-2019 - |
题
我们的情况是,我们为运行程序提供了以地图形式的外部配置。我发现JSR-330依赖项注入提供了一种更干净的方法,可以在代码中使用该配置映射,而不是通过围绕地图或使用JNDI将其传递给它。
@Inject @Named("server.username") String username;
让JSR-330实现自动填写此字段。
我可以用Guice设置价值
bindConstant().annotatedWith(Names.named(key)).to(value);
我希望能够在焊缝(bind'server.username“ to eg 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;
}
}
}
我测试了这是通过制作焊接应用程序来起作用的:
@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.spi.extension,用您使用的类Pather替换WELDTEST:
weldtest.PerformSetup
这应该使所有这一切工作。让我知道您是否遇到任何困难,我将向您发送我的测试项目。
其他提示
并不是所有对赏金感兴趣的人,但是如果它仍然在桌子上,我会接受。这与我在$ Dayjob使用的某些代码非常相似,因此这不是理论,而是我在生产代码中使用的,而是修改以保护有罪。我没有尝试编译修改后的代码,因此请注意,我可能在更改名称等方面犯了一些错误,但是这里涉及的原则都已进行了测试和工作。
首先,您需要一个价值持有人的预选赛。使用@nonbinding,以防止焊接仅匹配具有相同值的预选赛,因为我们希望该特定限定符的所有值能够匹配单个注入点。通过将预选赛和价值保持在相同的注释中,您不能只是偶然地“忘记”其中一个。 (吻原理)
@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;
可以将其作为 @depentent的生产者方法实现,该方法本身会注入@indoctionpoint,它可以使您反思所注入的字段 - 这将使您可以窥视自定义注释(而不是预选赛)现场成员找出要返回的瓦尔
@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/annotation/resource.html