javadependency-injectioncditargetweld

How to ensure a Java CDI framework implementation will inject an instance at a custom injectionpoint of a custom bean?


Given that:

How do I ensure that CDI will inject an instance of the type required by the InjectionPoint?

In other words, how do I construct a custom bean for a class over which I have no control, in such a way that I can still inject an instance of a class over which I do have control into a particular field?

My current (non-functional) code looks like this:

    void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager beanManager) {
    final AnnotatedType<ClassWithoutControl> annotatedType = beanManager.createAnnotatedType(ClassWithoutControl.class);
    AnnotatedField<ClassWithoutControl> annotatedField = null;
    for (AnnotatedField<? super ClassWithoutControl> field : annotatedType.getFields()) {
        if ("field".equals(field.getJavaMember().getName()) && ClassWithControl.class.equals(field.getJavaMember().getType())) {
            annotatedField = (AnnotatedField<ClassWithoutControl>) field;
            break;
        }
    }
    final InjectionPoint injectionPoint = beanManager.createInjectionPoint(annotatedField);
    final HashSet<InjectionPoint> injectionPoints = new HashSet<>();
    injectionPoints.add(injectionPoint);
    BiFunction<CreationalContext<ClassWithoutControl>, Bean<ClassWithoutControl>, ClassWithoutControl> creator = (creationalContext, bean) -> {
        final InjectionTarget<ClassWithoutControl> injectionTarget = beanManager.createInjectionTarget(annotatedType);
        ClassWithoutControl instance = new ClassWithoutControl(this.paramater1, this.parameter2);
        injectionTarget.inject(instance, creationalContext);
        injectionTarget.postConstruct(instance);
        return instance;
    };
    customBeanBuilder.setInjectionPoints(injectionPoints).setCreator(creator);
    final BeanAttributes<ClassWithoutControl> beanAttributes = beanManager.createBeanAttributes(annotatedType);
    customBeanBuilder.setBeanAttributes(beanAttributes);
    abd.addBean(customBeanBuilder.build());
}

CustomBeanBuilder is a class which creates an instance of CustomBean which extends Bean. The creator BiFunction is called in the create(CreationalContext ctx) function of a CustomBean. The creator's parameters are the CreationalContext as passed to create() and a CustomBean (this).

I know why the above does not work. A NonProducibleInjectionTarget is returned by Weld, since the AnnotatedType that is used by weld to create the InjectionTarget has no no-args constructor. I am however looking for a way that I can do this, without having to depend on Weld's internal implementations. I can't find a way to trick the CDI into accepting my instance of ClassWithoutControl while retraining the ability to inject another instance.

I have looked at https://docs.jboss.org/weld/reference/latest/en-US/html_single/#_wrapping_an_injectiontarget but I do not quite understand how to create such a wrapper. So any help in that direction would be appreciated as well.


Solution

  • I dove into Weld (my current CDI implementation) to see if I could find a way to resolve this. Unfortunately I cannot provide the InjectionTarget due to the missing no-args constructor in the class I do not control. BeforeBeanDiscovery reveals that the bean for the class is being added by the Extension. However due to the fact that it is missing the no-args constructor an InjectionTarget is never created.

    I've attemped to resolve this by wrapping the AnnotatedType during ProcessAnnotatedType and inserting a AnnotatedConstructor and returning it with the constructors of the original AnnotatedType. This can be done, unfortunately AnnotatedConstructor has a getJavaMember() method which returns the original Constructor. In my case that Constructor (java-member) does not exist and seeing as you cannot instantiate a new instance of Constructor. This was a dead end as there are no other means of obtaining a custom instance of Constructor.

    I'm now exploring byte-code manipulation libraries such as byte-buddy. These would enable me to add a no-args constructor at run-time. There would be no repercussions for me as the no-args constructor sole purpose is to ensure that CDI marks the class as a valid Bean.