当前位置: 首页 > 知识库问答 >
问题:

CDI-已处理但已配置的应用程序

宣高朗
2023-03-14

问题

使用CDI,我希望生成@ApplicationScopedbean。

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

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

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

问题

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

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

我需要的基本上是为每个不同的提供一个应用程序范围实例。

共有1个答案

寇升
2023-03-14

您尝试实现的不是CDI中的开箱即用功能,而是由于其SPI和便携式扩展,您可以实现所需的功能。

此扩展将分析给定类型的所有注入点,在每个注入点上获取@Configuration注释,并将在应用程序中为注释中成员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的类。将其视为工厂和元数据持有者(范围、限定符等)你的豆子。

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,可以激活此扩展。企业注射spi。扩展名文本文件。

有其他方法可以通过扩展创建功能,但我尝试给您一个从CDI 1.0开始工作的代码(除了@Vetoed注释)。

您可以在Github上的MyCDI沙箱中找到此扩展的源代码。

代码非常直截了当,但如果您有疑问,请不要犹豫。

 类似资料:
  • Spring Security的Java配置没有公开每个配置对象的每一个属性,这简化了广大用户的配置。毕竟如果要配置每一个属性,用户可以使用标准的Bean配置。 虽然有一些很好的理由不直接暴露所有属性,用户可能任然需要更多高级配置,为了解决这个Spring Security引入了 ObjectPostProcessor 概念,用来修改或替换Java配置的对象实例。例如:如果你想在FilterSec

  • 使用焊接1.1.13。最终在测试与Arquillian...... 假设我向一个字段注入了一些不稳定的东西。比如一个受更改影响的属性,我希望拥有注入点的bean接收更改事件。考虑创建一个CDI扩展。 捕获ProcessAnnotatedType事件并查找在字段注入点上具有自定义注释的所有字段: 之后,他甚至抓取了字段的所有注入点,并用对应于“包装器”类型的新字段替换了底层的WeldField。否则

  • 我运行Kafkajava客户端使用0.10.1.0。根据这里建议的配置https://cwiki.apache.org/confluence/display/KAFKA/Compression 我把 在生产者财产中。但是,当我运行时,生成器会执行配置已提供,但不是已知配置。将显示此警告。 根据API文件,http://home.apache.org/~jgus/kafka-0.10.1.0-rc0

  • 我正在尝试将一个.ear应用程序部署到WildFly10.1Final。ear有2个嵌套的.war文件。war文件中没有“jboss-web.xml”文件。 信息[org.jboss.as.server.deployment.scanner](DeploymentScanner-Threads-1)WFLYDS0004:在部署目录中找到MyApp.ear。要触发部署,请创建一个名为myapp.ea

  • 我部署了我的Rails应用程序,但当我打开它时,我在Web浏览器中收到此错误: 应用程序错误应用程序中发生错误,无法查看您的页面。如果您是应用程序所有者,请查看日志以了解详细信息。 以下是我的日志错误: 2018-03-19T04:48:12.360662 00:00 heroku[路由器]:at=错误代码=H10 desc=“应用程序崩溃”方法=获取路径=“/”主机=marmelade1。her

  • 试图在我的应用程序中实现GCM。编译成功。但当我运行我的应用程序时,它崩溃了。堆栈跟踪: 而且 java.lang.IncompatibleClassChangeError:方法“java.io.file android.support.v4.content.contextCompat.GetNoBackupFilesDir(android.content.context)”应为virtual类型