CDI – ApplicationScoped但已配置

问题

使用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方式? 或者我是否需要在列表中以某种方式“记住”bean,例如在包含生产者的类中?

我需要的是基本上每个不同valueApplicationScoped实例。

您尝试实现的并不是CDI中的盒子function,但由于其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, PassivationCapable { static Set types; private final Configuration configuration; private final Set qualifiers = new HashSet<>(); public ConfiguredServiceBean(Configuration configuration) { this.configuration = configuration; qualifiers.add(configuration); qualifiers.add(new AnnotationLiteral() { }); } @Override public Class getBeanClass() { return ConfiguredService.class; } @Override public Set getInjectionPoints() { return Collections.EMPTY_SET; } @Override public boolean isNullable() { return false; } @Override public Set getTypes() { return types; } @Override public Set getQualifiers() { return qualifiers; } @Override public Class getScope() { return ApplicationScoped.class; } @Override public String getName() { return null; } @Override public Set> getStereotypes() { return Collections.EMPTY_SET; } @Override public boolean isAlternative() { return false; } @Override public ConfiguredService create(CreationalContext creationalContext) { return new ConfiguredService(configuration.value()); } @Override public void destroy(ConfiguredService instance, CreationalContext creationalContext) { } @Override public String getId() { return getClass().toString() + configuration.value(); } } 

请注意,限定符是唯一的参数,允许我们将限定符的内容链接到create()方法中的实例。

最后,您将创建一个扩展,它将从一组注入点注册您的bean。

 public class ConfigurationExtension implements Extension { private Set configurations = new HashSet<>(); public void retrieveTypes(@Observes ProcessInjectionPoint 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文本文件来激活此扩展。

还有其他方法可以使用扩展来创建您的function,但我尝试使用CDI 1.0提供代码( @Vetoed注释除外)。

您可以在Github上的CDI Sandbox中找到此扩展的源代码。

代码非常简单,但如果您有疑问,请不要犹豫。