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,例如在包含生产者的类中?
我需要的是基本上每个不同value
的ApplicationScoped
实例。
您尝试实现的并不是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 extends Annotation> 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, 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
文本文件来激活此扩展。
还有其他方法可以使用扩展来创建您的function,但我尝试使用CDI 1.0提供代码( @Vetoed
注释除外)。
您可以在Github上的CDI Sandbox中找到此扩展的源代码。
代码非常简单,但如果您有疑问,请不要犹豫。