Next Presso (CDI, Java EE and friends)

Non-Contextual Instances in CDI 2.0

Posted by Antoine Sabot-Durand on Jun 06, 2017 | Comments
fishbowl

The king components in CDI are the Beans (check this older post to all exisitng kind of beans). In CDI a bean is managed from A to Z by the container, which controls lifecycle of its instance and add all CDI magic on it (injection, interception, etc..).

But sometimes you need to have more control on your components, because you need to create it or destroy it yourself or because it is provided by another container or framework, for instance.

Yet, you’d like to have access to some CDI features for your component like dependency injection or interception. In that case you’ll need to use CDI "non-contextual instance" feature.

To achieve CDI integration, some Java EE spec use the non-contextual instance feature. For instance, you can think about being able to perform CDI injection in servlets or in entity listeners. These component are not managed by CDI container, but they are enhanced with some CDI features.

Two kind of non-contextual instances and two kind of classes

CDI Non-contextual instance may benefit the following services:

  • Call of @PostConstruct and @Predestroy lifecycle callbacks.

  • Dependency injection.

  • Destruction of dependent beans instances when the instance is destroyed.

  • Interceptors and Decorators.

We can distinguish 2 kinds of non-contextual instances.

The "official" (from the spec point of view) non-contextual instance which is created (i.e. instantiated) by the container and the one instantiated outside the CDI container.

This last kind has no official name in the spec (yet it is fully supported), in this post I’ll call them "user instantiated non-contextual instance" This second kind of non-contextual instance may benefit the following CDI services:

  • call of @PostConstruct and @Predestroy callbacks.

  • CDI dependency injection

  • Destruction of dependent beans when the instance is destroyed

  • Interception thanks to the new InterceptionFactory SPI in CDI 2.0

Both kind of non-contextual instances are enhanced through the InjectionTargetFactory SPI. The classic non-contextual instance is also created and destroyed with the same SPI, but CDI provides an helper class to perform this seamlessly if the class you want to use has already all required annotation (@Inject and qualifiers)

Three different use cases

Thus, when you need to use non-contextual instances, you should first answer to 2 questions

  1. Can I Let the CDI container create the instance for me?

  2. Does the class of the instance has already all annotations (@Inject, qualifiers, Interceptor bindings) at class level?

Your answers will make non-contextual instances more or less easy to use:

Class has all required annotations Class lacks some annotations

Container can instantiate

You can use Unmanaged helper class

You’ll have to use InjectionTargetFactory and AnnotatedTypeConfigurator SPI

You provide the instance

You’ll have to use InjectionTargetFactory SPI

Use case 1: Non-contextual instance for a class having all required annotations

This is the simplest use case.

Since CDI 1.1, the API provide the Unmanaged helper class that hides all the work done with InjectionTargetFactory produce a non-contextual instance:

public void doSomethingWithContextualMyClass() {
    Unmanaged<MyClass> unmanagedMyClass = new Unmanaged<MyClass>(MyClass.class); (1)
    Unmanaged.UnmanagedInstance<MyClass> umc = unmanagedMyClass.newInstance(); (2)
    umc.produce().inject().postConstruct(); (3)
    MyClass myInstance = umc.get(); (4)

    //Do what you need with myInstance

    umc.preDestroy(); (5)
    umc.dispose(); (6)
}
1 Instantiate an instance of Unmanaged for MyClass
2 Request a new instance handler (providing all services and data for a given instance)
3 These invocation create the instance, perform dependency injection and call the @PostConstruct lifecycle call back
4 retrieving the effective instance
5 call the @Predestroy lifecycle callback
6 perform destruction of the instance context (i.e. release all @Dependent instances injected in the instance)

Keep in mind that letting the container producing the instance (produce() method) activate optional interceptors and decorators on the instance.

In this case, the class shouldn’t be unproxyable as detailed in the spec.

Unmanaged.UnmanagedInstance is an important handler that gives you access to all CDI services for the instance you want to obtain, but it should also be kept to perform the dispose() task, which release all dependent beans instances that were created with your instance. Without this call you may face memory leaks in your application

Use case 2: Non-contextual instance whose class doesn’t have the required annotation

Unfortunately, Unmanaged doesn’t provide access to the underlying AnnotatedType for the instance class.

So if you need to add annotations to the metadata model because they are missing on the original class, you’ll have to use the InjectionTargetFactory provided by the container. Note that Unmanaged does the same under the hood.

To request an InjectionTargetFactory from the container, you’ll need first to access the BeanManager.

If you are in CDI programming model (i.e in a CDI bean) simply inject the BeanManager to access it

@Inject
BeanManager bm;

If are not CDI programming model, the easiest way to access the BeanManager is ot use the CDI class available since CDI 1.1 (note that it also works in CDI programming model even if direct injection is still preferred to static call done with CDI.current()).

BeanManager bm = CDI.current().getBeanManager();

In certain circumstance, you may want to retrieve the BeanManager from a JNDI lookup through the java:comp/BeanManager, JNDI name.

The following example show how to create a non-contextual instance from MyClass in which you need to create an injection point (add @Inject) on the field MyField

public void doSomethingWithContextualMyClass() {
        BeanManager bm = CDI.current().getBeanManager();  (1)
        InjectionTargetFactory<MyClass> itf = bm
                .getInjectionTargetFactory(bm.createAnnotatedType(MyClass.class)); (2)
        itf.configure() (3)
                .filterFields(f -> "MyField".equals(f.getJavaMember().getName()))
                .findFirst()
                .ifPresent(f -> f.add(InjectLiteral.INSTANCE)); (4)
        InjectionTarget<MyClass> it = itf.createInjectionTarget(null); (5)
        CreationalContext<MyClass> cctx = bm.createCreationalContext(null); (6)
        MyClass myInstance = it.produce(cctx); (7)
        it.postConstruct(myInstance); (7)
        it.inject(myInstance,cctx); (7)

        //Do what you need with myInstance

        it.preDestroy(myInstance); (8)
        cctx.release(); (9)
}
1 retrieving the BeanManager
2 requesting an InjectionTargetFactory from the BeanManager
3 using the new AnnotatedTypeConfigurator SPI in CDI 2.0 to configure the underlying AnnotatedType. Before CDI 2.0 you’d have to implement AnnotatedType to add your annotation and use it in previous step (2)
4 looking for the MyField field and adding @Inject to it (we use the new InjectLiteral introduced in CDI 2.0)
5 creating the InjectionTarget. As it’s for a non-contextual instance, we create it by passing null (no bean) to the method
6 creating the CreationalContext. As it’s for a non-contextual instance, we create it by passing null (no bean) to the method
7 creating the instance, performing @PostConstruct lifecycle call back and injection
8 call the @Predestroy lifecycle callback
9 release the CreationalContext and all the dependents bean instances

Note, that, we could also have added interceptor bindings to the AnnotatedTypeConfigurator during step (3). In this case, MyClass shouldn’t be unproxyable as detailed in the spec.

Use case 3: User Instantiated non-contextual instance

If the instance is provided by the user, code is roughly the same

public void doSomethingWithContextualMyClass() {
        BeanManager bm = CDI.current().getBeanManager();
        InjectionTargetFactory<MyClass> itf = bm.getInjectionTargetFactory(bm.createAnnotatedType(MyClass.class));
        itf.configure()
                .filterFields(f -> "MyField".equals(f.getJavaMember().getName()))
                .findFirst()
                .ifPresent(f -> f.add(InjectLiteral.INSTANCE));
        InjectionTarget<MyClass> it = itf.createInjectionTarget(null);
        CreationalContext<MyClass> cctx = bm.createCreationalContext(null);
        MyClass myInstance = new MyClass(); (1)
        it.postConstruct(myInstance);
        it.inject(myInstance,cctx);

        //Do what you need with myInstance

        it.preDestroy(myInstance);
        cctx.release();
}
1 instance is not created by the container

In that case the instance won’t have interceptor applied on it since the container didn’t create it.

In CDI 2.0, you can use the new InterceptorFactory SPI to fix that.

public void doSomethingWithContextualMyClass() {
        BeanManager bm = CDI.current().getBeanManager();
        InjectionTargetFactory<MyClass> itf = bm.getInjectionTargetFactory(bm.createAnnotatedType(MyClass.class));
        itf.configure()
                .filterFields(f -> "MyField".equals(f.getJavaMember().getName()))
                .findFirst()
                .ifPresent(f -> f.add(InjectLiteral.INSTANCE));
        InjectionTarget<MyClass> it = itf.createInjectionTarget(null);
        CreationalContext<MyClass> cctx = bm.createCreationalContext(null);
        InterceptionFactory<MyClass> ifm = bm.createInterceptionFactory(cctx, MyClass.class); (1)
        ifm.configure() (2)
                .add(new AnnotationLiteral<Transactional>() {
                });

        MyClass myInstance = ifm.createInterceptedInstance(new MyClass()); (3)
        it.postConstruct(myInstance);
        it.inject(myInstance,cctx);

        //Do what you need with myInstance

        it.preDestroy(myInstance);
        cctx.release();
    }
1 requesting an InterceptionFactory for MyClass.
2 configure the annotation on the underlying class. Here we add @Transactional on the class but we could have done it on a given method
3 Instantiating MyClass and applying interceptor on it

Conclusion

So we covered all the use cases for non-contextual instance creation and management in CDI.

All these use cases can also be implemented in CDI 1.1 with more verbose code (except the last example, since InterceptionFactory was only introduced in 2.0).

Keep in mind that except for Unmanaged, all the SPI elements shown in this post are also very useful when creating custom bean.

InterceptionFactory is also very useful to apply interceptors in a producer.