简体   繁体   中英

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

Given that:

  • I am using Weld as my underlying CDI framework implementation.
  • I have a class ClassWithoutControl without a no-arg constructor (aka not a valid Bean).
  • I create a custom Bean for that class.
  • I add this bean via an Extension when a AfterBeanDiscovery event is triggered.
  • When create(CreationalContext<> ctx) is called on this bean I construct a new instance of class ClassWithoutControl.
  • I have created a set of InjectionPoints for that custom Bean.
  • One of those InjectionPoints is a field of ClassWithControl.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM