3
votes

I have a class that is instantiated by 3rd party code (it uses reflection to create the object.) I provide the implementation of their interface and they create the object. In my implementation I want to use CDI to inject a service that performs logic. What is the correct way to do this?

public interface ThirdPartyInterface {
    public void DoSomething();
}

public class InjectedService {
    public void DoSomeLogic() { ... }
}

public class MyImplementation implements ThirdPartyInterface {
    @Inject InjectedService service;

    @Override
    public void DoSomething() {
        service.DoSomeLogic();
    }
}

I originally thought this would work through the magic of CDI, but found my service object to be null.

The only thing I've come up with so far is to inject the service manually in the constructor

 public MyImplementation() {
     CDI<Object> cdi = CDI.current();
     service = cdi.select(InjectedService.class).get();
 }

Is this the correct/only/best way of obtaining the instance? I am using Weld for my CDI implementation.

I have also found this to work in the constructor:

    public MyImplementation() {
        CDI<Object> cdi = CDI.current();
        BeanManager bm = cdi.getBeanManager();
        AnnotatedType<MyImplementation> myType = bm.createAnnotatedType(MyImplementation.class);
        Set<Bean<?>> beans = bm.getBeans(MyImplementation.class);
        Bean<?> bean = bm.resolve(beans);
        @SuppressWarnings("unchecked")
        CreationalContext<MyImplementation> cc = (CreationalContext<MyImplementation>)bm.createCreationalContext(bean);
        bm.createInjectionTarget(myType).inject(this, cc);
   }
1
you pretty much found it. you either need to deliberately get CDI to offer you a managed version or you need to look up the bean via JNDI. if the enclosing bean is created via reflection, it is the same as using "new", which will not be managed from a CDI standpoint.him

1 Answers

1
votes

So long as someone creates the object manually, CDI will not, by default, inject anything into it.

You approach with the constructor injection is probably going to work, unless you get into EARs and such where CDI.current() may not do what you would expect.

There is a way to make CDI inject into manually created objects - the 3rd party would have to take this step to make it work. You need BeanManager and an instance you want to inject into:

        BeanManager beanManager = ...; // get hold of bean manager, can be injected
        CreationalContext<Object> ctx = beanManager.createCreationalContext(null);
        @SuppressWarnings("unchecked")
        InjectionTarget<MyImplementation> injectionTarget = (InjectionTarget<MyImplementation>) beanManager
            .getInjectionTargetFactory(beanManager.createAnnotatedType(myImplementationInstance.getClass())).createInjectionTarget(null);
        injectionTarget.inject(myImplementationInstance, ctx);

Note - by doing this you take responsibility to clean up after the object once you no longer need it. Store the CreationContext somewhere and call release() method on it in order to dispose of it properly (with all possible pre destroy calls and such).

Alternatively, since you are already using CDI, why doesn't 3rd party simply @Inject the bean you provide?