Как легко ввести строковые константы с сварным швом?
-
28-09-2019 - |
Вопрос
У нас есть ситуация, когда мы предоставляем внешнюю конфигурацию в виде карты в наших работающих программах. Я обнаружил, что впрыск зависимости JSR-330 дает гораздо более чистый способ использовать эту карту конфигурации в коде вместо того, чтобы пройти карту вокруг или использовать JNDI, чтобы получить его.
@Inject @Named("server.username") String username;
Давайте автоматически заполним реализацию JSR-330 в этом поле.
С резином, я могу установить значение с
bindConstant().annotatedWith(Names.named(key)).to(value);
Я хотел бы иметь возможность сделать то же самое в сварке (bind "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);
}
}
Наконец, не забывайте /tea-inf/services/javax.enterprise.inject.spi.extension, замена сварного сварных данных с использованием классов:
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();
}
Далее вам нужен метод продюсера, который знает, как получить карту. Вы, вероятно, должны иметь названный фасоль, который содержит метод продюсера, поэтому вы можете либо явно инициализировать значение, используя GetTers / Setters, либо у вас есть фасоль инициализации его для вас.
Мы должны указать пустое значение для квалификатора на методе производителя, чтобы избежать ошибок времени компиляции, но он никогда не используется на практике.
@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;
Может быть возможно реализовать это как метод @DeCentendal Producer, который сам впрыскивает точку @inection, что позволит вам размышлять о поле, которое вы вводите в введение - это позволило бы вам заглянуть в пользовательскую аннотацию (не квалификатор) член на поле, чтобы выяснить, что вы хотите вернуться
@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.orcle.com/javase/6/docs/api/javax/annotation/resource.html.html.