CDI - ApplicationScoped但已配置

时间:2016-03-17 15:33:02

标签: java configuration scope cdi code-injection

问题

使用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个实例。

1 个答案:

答案 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注释除外)。

您可以在CDI Sandbox on Github

中找到此扩展程序的源代码

代码很直接,但如果您有疑问,请不要犹豫。