CDI - ApplicationScoped 但已配置
CDI - ApplicationScoped but configured
问题
我想使用 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方式吗?或者我需要以某种方式 "remember" 列表中的豆子,例如在包含制作人的 class 中?
我需要的基本上是每个不同 value
的 ApplicationScoped
实例。
您尝试实现的不是 CDI 中的开箱即用功能,但由于它的 SPI 和便携式扩展,您可以实现您需要的。
此扩展将分析具有给定类型的所有注入点,获取每个注入点的 @Configuration
注释,并将在 applicationScoped 中为成员 value()
中的每个不同值创建一个 bean注释。
当您注册多个具有相同类型的 bean 时,您必须首先将注释转换为限定符
@Qualifier
@Target({TYPE, METHOD, PARAMETER, FIELD})
@Retention(RUNTIME)
@Documented
public @interface Configuration {
String value();
}
下面的 class 用于创建您的 bean 实例:
@Vetoed
public class ConfiguredService {
private String value;
protected ConfiguredService() {
}
public ConfiguredService(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
注意 @Vetoed
注释以确保 CDI 不会选择此 class 来创建 bean,因为我们将自己创建。这个 class 必须有一个没有参数的默认构造函数才能用作钝化 bean 的 class(在应用程序范围内)
然后您需要声明自定义 bean 的 class。将其视为您的 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()
方法中link将限定符的内容传给实例
最后,您将创建将从一组注入点注册您的 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));
}
}
}
通过将其完全限定的 class 名称添加到 META-INF/services/javax.enterprise.inject.spi.Extension
文本文件来激活此扩展。
还有其他方法可以使用扩展来创建您的功能,但我试图为您提供从 CDI 1.0 开始工作的代码(@Vetoed
注释除外)。
你可以在我的 CDI Sandbox on Github.
中找到这个扩展的源代码
代码非常简单,但如果您有任何疑问,请不要犹豫。
问题
我想使用 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方式吗?或者我需要以某种方式 "remember" 列表中的豆子,例如在包含制作人的 class 中?
我需要的基本上是每个不同 value
的 ApplicationScoped
实例。
您尝试实现的不是 CDI 中的开箱即用功能,但由于它的 SPI 和便携式扩展,您可以实现您需要的。
此扩展将分析具有给定类型的所有注入点,获取每个注入点的 @Configuration
注释,并将在 applicationScoped 中为成员 value()
中的每个不同值创建一个 bean注释。
当您注册多个具有相同类型的 bean 时,您必须首先将注释转换为限定符
@Qualifier
@Target({TYPE, METHOD, PARAMETER, FIELD})
@Retention(RUNTIME)
@Documented
public @interface Configuration {
String value();
}
下面的 class 用于创建您的 bean 实例:
@Vetoed
public class ConfiguredService {
private String value;
protected ConfiguredService() {
}
public ConfiguredService(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
注意 @Vetoed
注释以确保 CDI 不会选择此 class 来创建 bean,因为我们将自己创建。这个 class 必须有一个没有参数的默认构造函数才能用作钝化 bean 的 class(在应用程序范围内)
然后您需要声明自定义 bean 的 class。将其视为您的 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()
方法中link将限定符的内容传给实例
最后,您将创建将从一组注入点注册您的 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));
}
}
}
通过将其完全限定的 class 名称添加到 META-INF/services/javax.enterprise.inject.spi.Extension
文本文件来激活此扩展。
还有其他方法可以使用扩展来创建您的功能,但我试图为您提供从 CDI 1.0 开始工作的代码(@Vetoed
注释除外)。
你可以在我的 CDI Sandbox on Github.
中找到这个扩展的源代码代码非常简单,但如果您有任何疑问,请不要犹豫。