CDI - ApplicationScoped但已配置 [英] CDI - ApplicationScoped but configured

查看:167
本文介绍了CDI - ApplicationScoped但已配置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

使用CDI我想生成 @ApplicationScoped bean。

Using CDI I want to produce @ApplicationScoped beans.

此外,我想为注入点提供配置注释,例如:

Additionally I want to provide a configuration annotation to the injection points, e.g.:

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Configuration {

  String value();

}

我不想为每个不同的人编写单独的生产者可能

I do not want to write a separate producer for each different possibility of value.

方法

通常的方法是制作一个制作人并处理注入点注释:

The usual way would be to make a producer and handle the injection point annotations:

@Produces
public Object create(InjectionPoint injectionPoint) {
    Configuration annotation = injectionPoint.getAnnotated().getAnnotation(Configuration .class);
    ...
}

因此,bean不再是应用程序作用域,因为每个注入点可能可能不同(生产者的参数注入点不适用于 @AplicationScoped 带注释的生产者)。

By consequence the bean cannot be application scoped anymore, because each injection point could be possibly different (parameter injectionpoint for producers does not work for @AplicationScopedannotated producers).

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

So this solution does not work.

问题

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

I would need a possibility that the injection points with the same value get the same bean instance.

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

Is there an inbuilt CDI way? Or do I need to somehow "remember" the beans myself in a list, e.g. in the class containing the producer?

对于每个不同的值,我需要的基本上是 ApplicationScoped 实例

What I need is basically an ApplicationScopedinstance for each different value.

推荐答案

你试图实现的不是CDI中的盒子功能,而是因为它的SPI和便携式扩展,你可以实现你所需要的。

What you try to achieve is not an out fo the box feature in CDI, but thanks to its SPI and a portable extension you can achieve what you need.

这个扩展将分析给定类型的所有注入点,得到 @Configuration 注释,并将在applicationScoped中为注释中的成员 value()的每个不同值创建一个bean。

This extension will analyse all injection poins with a given type, get the @Configuration annotations on each of them and will create a bean in applicationScoped for each different value of the member value() in the annotation.

当您注册多个具有相同类型的bean时,您首先要将注释转换为限定符

As you'll register multiple beans with the same type you'll have first to transform your annotation into a qualifier

@Qualifier
@Target({TYPE, METHOD, PARAMETER, FIELD})
@Retention(RUNTIME)
@Documented
public @interface Configuration {
    String value();
}

用于创建bean实例的类下面:

Below the class to use to create your bean instances:

@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的类(在应用程序作用域中)

Note the @Vetoed annotation to make sure that CDI won't pick up this class to create a bean as we'll do it ourself. This class has to have a default constructor with no parameter to be used as class of a passivating bean (in application scoped)

然后你需要声明自定义的类豆。将其视为bean的工厂和元数据持有者(范围,限定符等)。

Then you need to declare the class of your custom bean. Sees it as a factory and metadata holder (scope, qualifiers, etc...) of your 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()方法中的实例。

Note that the qualifier is the only parameter, allowing us to link the content of the qualifier to the instance in the create() method.

最后,你将会创建将从注入点集合注册bean的扩展。

Finally, you'll create the extension that will register your beans from a collection of injection points.

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 文本文件的完全限定类名。

This extension is activated by adding its fully qualified classname to the META-INF/services/javax.enterprise.inject.spi.Extension text file.

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

There are other way to create your feature with an extension, but I tried to give you a code working from CDI 1.0 (except for the @Vetoed annotation).

您可以在我的 Github上的CDI Sandbox

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

The code is quite straight forward, but don't hesitate if you have questions.

这篇关于CDI - ApplicationScoped但已配置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆