问题
使用CDI我想生成@ApplicationScoped
个bean。
此外,我想为注入点提供配置注释,例如:
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Configuration {
String value();
}
我不想为value
的每种不同可能性编写单独的生产者。
方法
通常的方法是制作一个制作人并处理注入点注释:
@Produces
public Object create(InjectionPoint injectionPoint) {
Configuration annotation = injectionPoint.getAnnotated().getAnnotation(Configuration .class);
...
}
结果是bean不再是应用程序作用域,因为每个注入点可能可能不同(生成器的参数注入点不适用于@AplicationScoped
注释生成器)。
所以这个解决方案不起作用。
问题
我需要一个具有相同值的注入点获得相同bean实例的可能性。
是否有内置的CDI方式?或者我需要以某种方式"记住"我自己在列表中的豆子,例如在包含生产者的类中?
对于每个不同的ApplicationScoped
,我需要的基本上是value
个实例。
答案 0 :(得分:3)
您尝试实现的不仅仅是CDI的盒子功能,但由于其SPI和便携式扩展,您可以实现所需。
此扩展将分析具有给定类型的所有注入点,在每个注入点上获取@Configuration
注释,并将在applicationScoped中为注释中成员value()
的每个不同值创建一个bean。
当您注册多个具有相同类型的bean时,您首先要将注释转换为限定符
@Qualifier
@Target({TYPE, METHOD, PARAMETER, FIELD})
@Retention(RUNTIME)
@Documented
public @interface Configuration {
String value();
}
在用于创建bean实例的类下面:
@Vetoed
public class ConfiguredService {
private String value;
protected ConfiguredService() {
}
public ConfiguredService(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
请注意@Vetoed
注释,以确保CDI不会选择此类来创建bean,因为我们将自己执行此操作。这个类必须有一个默认的构造函数,没有参数可以用作钝化bean的类(在应用程序作用域中)
然后,您需要声明自定义bean的类。将其视为bean的工厂和元数据持有者(范围,限定符等)。
public class ConfiguredServiceBean implements Bean<ConfiguredService>, PassivationCapable {
static Set<Type> types;
private final Configuration configuration;
private final Set<Annotation> qualifiers = new HashSet<>();
public ConfiguredServiceBean(Configuration configuration) {
this.configuration = configuration;
qualifiers.add(configuration);
qualifiers.add(new AnnotationLiteral<Any>() {
});
}
@Override
public Class<?> getBeanClass() {
return ConfiguredService.class;
}
@Override
public Set<InjectionPoint> getInjectionPoints() {
return Collections.EMPTY_SET;
}
@Override
public boolean isNullable() {
return false;
}
@Override
public Set<Type> getTypes() {
return types;
}
@Override
public Set<Annotation> getQualifiers() {
return qualifiers;
}
@Override
public Class<? extends Annotation> getScope() {
return ApplicationScoped.class;
}
@Override
public String getName() {
return null;
}
@Override
public Set<Class<? extends Annotation>> getStereotypes() {
return Collections.EMPTY_SET;
}
@Override
public boolean isAlternative() {
return false;
}
@Override
public ConfiguredService create(CreationalContext<ConfiguredService> creationalContext) {
return new ConfiguredService(configuration.value());
}
@Override
public void destroy(ConfiguredService instance, CreationalContext<ConfiguredService> creationalContext) {
}
@Override
public String getId() {
return getClass().toString() + configuration.value();
}
}
请注意,限定符是唯一的参数,允许我们将限定符的内容链接到create()
方法中的实例。
最后,您将创建一个扩展,它将从一组注入点注册您的bean。
public class ConfigurationExtension implements Extension {
private Set<Configuration> configurations = new HashSet<>();
public void retrieveTypes(@Observes ProcessInjectionPoint<?, ConfiguredService> pip, BeanManager bm) {
InjectionPoint ip = pip.getInjectionPoint();
if (ip.getAnnotated().isAnnotationPresent(Configuration.class))
configurations.add(ip.getAnnotated().getAnnotation(Configuration.class));
else
pip.addDefinitionError(new IllegalStateException("Service should be configured"));
}
public void createBeans(@Observes AfterBeanDiscovery abd, BeanManager bm) {
ConfiguredServiceBean.types = bm.createAnnotatedType(ConfiguredService.class).getTypeClosure();
for (Configuration configuration : configurations) {
abd.addBean(new ConfiguredServiceBean(configuration));
}
}
}
通过将其完全限定的类名添加到META-INF/services/javax.enterprise.inject.spi.Extension
文本文件中来激活此扩展。
还有其他方法可以使用扩展程序创建您的功能,但我尝试使用CDI 1.0提供代码(@Vetoed
注释除外)。
代码很直接,但如果您有疑问,请不要犹豫。