【问题标题】:CDI - ApplicationScoped but configuredCDI - ApplicationScoped 但已配置
【发布时间】:2016-03-17 15:33:01
【问题描述】:

问题

使用 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 不能再被应用程序限定,因为每个注入点可能不同(生产者的参数注入点不适用于@AplicationScopedannotated 生产者)。

所以这个解决方案不起作用。


问题

我需要具有相同值的注入点获得相同 bean 实例的可能性。

有内置的 CDI 方式吗?或者我是否需要以某种方式自己“记住”列表中的豆类,例如在包含生产者的类中?

我需要的基本上是每个不同的valueApplicationScopedinstance。

【问题讨论】:

    标签: java configuration scope cdi code-injection


    【解决方案1】:

    您尝试实现的不是 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 注释除外)。

    你可以在我的CDI Sandbox on Github找到这个扩展的源代码。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-08
      • 2012-12-02
      • 1970-01-01
      • 1970-01-01
      • 2014-12-25
      • 1970-01-01
      • 2013-12-07
      • 1970-01-01
      相关资源
      最近更新 更多