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