2016-03-17 6 views
0

ПроблемаCDI - ApplicationScoped но настроен

Использование CDI Я хочу, чтобы произвести @ApplicationScoped бобы.

Кроме того, я хочу, чтобы обеспечить конфигурации аннотацию к точкам впрыска, например .:

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

Следствия боб не может быть приложение области действия больше, потому что каждая инъекция точка может быть, возможно, различной (точка ввода параметров для производителей не работает для @AplicationScoped аннотированных производителей).

Так что это решение не работает.


Вопрос

мне нужна возможность того, что точки впрыска с тем же значением получить тот же экземпляр компонента.

Есть ли встроенный способ CDI? Или мне нужно как-то «помнить» сами бобы в списке, например. в классе, содержащем производителя?

Что мне нужно, это в основном пример ApplicationScoped для каждого другого value.

ответ

2

Что вы пытаетесь достичь, это не выход из функции коробки в CDI, но благодаря SPI и портативному расширению вы можете достичь того, что вам нужно.

Это расширение будет анализировать все точки впрыска с данным типом, получить @Configuration аннотаций на каждом из них и будет создавать боб в applicationScoped для каждого различного значения члена value() в аннотации.

Как вы зарегистрировать несколько компонентов с тем же типом вы должны сначала превратить вашу аннотацию в классификаторе

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

Ниже класса использовать для создания экземпляров боба:

@Vetoed 
public class ConfiguredService { 

    private String value; 

    protected ConfiguredService() { 
    } 

    public ConfiguredService(String value) { 
     this.value = value; 
    } 

    public String getValue() { 
     return value; 
    } 
} 

Обратите внимание на аннотацию @Vetoed, чтобы убедиться, что CDI не возьмет этот класс, чтобы создать компонент, поскольку мы сделаем это сами. Этот класс должен иметь конструктор по умолчанию без параметра, который будет использоваться как класс пассивирующего компонента (в области приложения)

Затем вам нужно объявить класс вашего пользовательского компонента. Видит это как владелец фабрики и метаданных (область действия, квалификаторы и т. Д.) Вашего компонента.

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().

Наконец, вы создадите расширение, которое зарегистрирует ваши компоненты из коллекции точек впрыска.

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.

Код довольно прост, но не стесняйтесь, если у вас есть вопросы.

Смежные вопросы