example container bean java configuration scope cdi code-injection

java - container - CDI: ApplicationScoped pero configurado



cdi container (1)

Problema

Uso de CDI Deseo producir beans @ApplicationScoped .

Además, quiero proporcionar una anotación de configuración a los puntos de inyección, por ejemplo:

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

No quiero escribir un productor por separado para cada posibilidad diferente de value .

Enfoque

La forma habitual sería hacer un productor y manejar las anotaciones del punto de inyección:

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

Por consecuencia, el bean ya no puede tener más alcance en la aplicación, porque cada punto de inyección podría ser posiblemente diferente (el punto de inyección del parámetro para los productores no funciona para los productores anotados @AplicationScoped ).

Entonces esta solución no funciona.

Pregunta

Necesitaría una posibilidad de que los puntos de inyección con el mismo valor obtengan la misma instancia de frijol.

¿Hay una forma de CDI incorporada? ¿O tengo que "recordar" los frijoles yo mismo en una lista, por ejemplo, en la clase que contiene el productor?

Lo que necesito es básicamente una instancia ApplicationScoped para cada value diferente.


Lo que intenta lograr no es una función de caja en CDI, pero gracias a su SPI y una extensión portátil puede lograr lo que necesita.

Esta extensión analizará todos los puntos de inyección con un tipo dado, obtendrá las anotaciones de @Configuration en cada uno de ellos y creará un bean en applicationScoped para cada valor diferente del valor de miembro value() en la anotación.

Como va a registrar varios beans con el mismo tipo, primero tendrá que transformar su anotación en un calificador

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

Debajo de la clase que se usará para crear instancias de beans:

@Vetoed public class ConfiguredService { private String value; protected ConfiguredService() { } public ConfiguredService(String value) { this.value = value; } public String getValue() { return value; } }

Tenga en cuenta la anotación @Vetoed para asegurarse de que CDI no recogerá esta clase para crear un bean ya que lo haremos nosotros mismos. Esta clase debe tener un constructor predeterminado sin ningún parámetro para usar como clase de un bean pasivante (en el ámbito de la aplicación)

Luego necesita declarar la clase de su bean personalizado. Lo ve como un titular de fábrica y metadatos (alcance, calificadores, etc.) de su grano.

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(); } }

Tenga en cuenta que el calificador es el único parámetro que nos permite vincular el contenido del calificador con la instancia en el método create() .

Finalmente, creará la extensión que registrará sus granos de una colección de puntos de inyección.

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)); } } }

Esta extensión se activa al agregar su nombre de clase completamente calificado al archivo de texto META-INF/services/javax.enterprise.inject.spi.Extension .

Hay otra manera de crear su función con una extensión, pero traté de darle un código que funciona desde CDI 1.0 (excepto la anotación @Vetoed ).

Puede encontrar el código fuente de esta extensión en mi CDI Sandbox en Github .

El código es bastante directo, pero no dude si tiene alguna pregunta.