A gentle introduction to CDI 2.0 in Java SE

Whenever we mention Context and Dependency Injection, we
automatically think about Java EE (Since CDI 1.0 focused strongly on Java EE). But, as you probably know, you don't necessarily need a Java EE (EE4J) application server to enjoy CDI magic. CDI 2.0 allows developers to use the same wiring model in both Java SE and Java EE and introduces new features to make CDI in Java SE more useful.

This blog post shows how to bootstrap CDI 2.0 in Java SE using Weld.

Why adding Java SE support

From the spec we can highlight 3 main reasons:

  • Align with many other Java EE spec (JPA, JAX-RS ...) which support Java SE bootstrapping
  • Boost CDI adoption for Spec and Frameworks
  • Provide a mean of building new stacks out of Java EE

Before CDI 2.0

Using Context Dependency Injection on Java SE applications is something not totally new. Here is how thing could be done using Weld.

First add weld dependency to your pom.xml:

<dependency>
  <groupId>org.jboss.weld.se</groupId>
  <artifactId>weld-se</artifactId>
  <version>2.4.6.Final	</version>
</dependency>

To activate CDI we need to create a beans archive and include beans.xml file in the META-INF directory of the classpath. We add an empty beans.xml file:

<beans xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd" >
</beans>

Then we could startup the container with like this:

public static void main(String[] args) throws IOException {
    Weld weld = new Weld();
    WeldContainer container = weld.initialize();
    Application application = container.instance().select(Application.class).get();
    application.run();
    weld.shutdown();
}

Although that does work, isn't it more accurate to have a standard way to boot CDI containers ?! That's what CDI 2.0 is about.

Bootstrap a CDI 2.0 container

CDI 2.0 introduced SeContainerInitializer, which is a new api to configure and bootstrap a CDI container under Java SE, it returns a SeContainer that implements Instance<Object> allowing programmatic lookup.

 SeContainerInitializer initializer = SeContainerInitializer.newInstance();
        /** disable discovery and register bean classes manually */
        try (SeContainer container = initializer.disableDiscovery().addBeanClasses(MyService.class).initialize()) {
            container.select(MyService.class);
        }

Notice that we called disableDiscovery() and manually pre-configures all CDI beans to start the CDI environment without the beans.xml file.

Also, The SeContainerInitializer class has a lot of methods to preconfigure you CDI container.

public abstract class SeContainerInitializer {

    /** Returns an instance of {@link SeContainerInitializer}. Each call returns a new instance */
    public static SeContainerInitializer newInstance() {...}

    /** Add provided bean classes to the synthetic bean archive. */
    public abstract SeContainerInitializer addBeanClasses(Class<?>... classes);

    /** All classes from the packages of the specified classes will be added to the set of bean classes for the synthetic bean archive. */
    public abstract SeContainerInitializer addPackages(Class<?>... packageClasses);

    /** Packages of the specified classes will be scanned and found classes will be added to the set of bean classes for the synthetic bean archive. */
    public abstract SeContainerInitializer addPackages(boolean scanRecursively, Class<?>... packageClasses);

    /** All classes from the specified packages will be added to the set of bean classes for the synthetic bean archive. */
    public abstract SeContainerInitializer addPackages(Package... packages);

    /** All classes from the specified packages will be added to the set of bean classes for the synthetic bean archive. */
    public abstract SeContainerInitializer addPackages(boolean scanRecursively, Package... packages);

    /** Add extensions to the set of extensions. */
    public abstract SeContainerInitializer addExtensions(Extension... extensions);

    /** Add extensions to the set of extensions. */
    public abstract SeContainerInitializer addExtensions(Class<? extends Extension>... extensions);

    /** Add interceptor classes to the list of enabled interceptors for the synthetic bean archive. */
    public abstract SeContainerInitializer enableInterceptors(Class<?>... interceptorClasses);

    /** Add decorator classes to the list of enabled decorators for the synthetic bean archive. */
    public abstract SeContainerInitializer enableDecorators(Class<?>... decoratorClasses);

    /** Add alternatives classes to the list of selected alternatives for the synthetic bean archive. */
    public abstract SeContainerInitializer selectAlternatives(Class<?>... alternativeClasses);

    /** Add alternative stereotype classes to the list of selected alternative stereotypes for the synthetic bean archive. */
    public abstract SeContainerInitializer selectAlternativeStereotypes(Class<? extends Annotation>... alternativeStereotypeClasses);

    /** Add a configuration property to the container */
    public abstract SeContainerInitializer addProperty(String key, Object value);

    /** Set all the configuration properties. */
    public abstract SeContainerInitializer setProperties(Map<String, Object> properties);

    /** By default, the discovery is enabled. However, it's possible to disable the discovery completely so that only the "synthetic" bean archive is considered. */
    public abstract SeContainerInitializer disableDiscovery();

    /** Set a {@link ClassLoader}. The given {@link ClassLoader} will be scanned automatically for bean archives if scanning is enabled. */
    public abstract SeContainerInitializer setClassLoader(ClassLoader classLoader);

    /** Initializes a CDI SeContainerInitializer. (Cannot be called within an application server). */
    public abstract SeContainer initialize();
}

To get started, check out this complete example on github, using CDI 2.0. Please feel free to comment below for suggestions and remark.