Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

CDI (Part 1): Factory With @Produces

DZone's Guide to

CDI (Part 1): Factory With @Produces

In the first part of this series, let's explore CDI 2.0, how it interacts with your code (a logger in this example), and how to use the @Produces annotation.

· Java Zone ·
Free Resource

The CMS developers love. Open Source, API-first and Enterprise-grade. Try BloomReach CMS for free.

Hello!

This is the Part 1 of the CDI Series in Java that contains:

  • Part 1: Factory in CDI with the @Produces annotation
  • Part 2: CDI Qualifiers
  • Part 3: Events and Observers in CDI
  • Part 4: CDI and Lazy Initialization
  • Part 5: Interceptors in CDI
  • Part 6: CDI Dependency Injection and Alternatives
  • Part 7: Working with CDI Decorators

In this post, we're going to see how to use the @Produces annotation that is probably one of the most used annotations in the CDI world in Java!

Let's contextualize our challenge here! We must create the following steps in our program:

  • Create a Checkout class to... you know... finish a checkout
  • Log the operations in the console
  • Create a Logger class to keep this log in its own class, keeping the cohesion
  • Customize this Logger to receive a Configuration that indicates which log mode should be used

Pretty simple, right? Let's jump into code!

Step 1: Creating the Special Main Class in CDI 2.0

Before creating the main class to execute the CDI code, let's create the important Checkout class:

public class Checkout {

    public void finishCheckout() {
        System.out.println("Finishing Checkout");
    }

}


Great!

In this example, we're going to use CDI 2.0, which allows us to use CDI in a standalone fashion!

To create a CDI Container that will bring to us CDI power, just create the code:

CDI<Object> container = new Weld().initialize()


Nice! Let's see the complete code to have an instance of the Checkout class:

public class MainApplication {

    public static void main(String[] args) {
        try (CDI<Object> container = new Weld().initialize()) {
            Checkout checkout = container.select(Checkout.class).get();

            checkout.finishCheckout();
        }
    }
}


Pretty easy, isn't? Notice the following code:

Checkout checkout = container.select(Checkout.class).get();


That code is the same as the following:

@Inject 
private Checkout checkout;


Now, it's time to execute our CDI example, to see that the environment is ok!

You will see in your console something like this:

INFO: WELD-ENV-002003: 
Weld SE container STATIC_INSTANCE initialized Finishing Checkout


Great! Everything is up and running!

Step 2: Creating a Specialized Class for Logger

Our code should have good cohesion, so we should create a Class that is responsible for logging some messages. This class will be called SpecialLogger, as you can see below:

public class SpecialLogger {

    public void log(String message) {
        System.out.println("LOG: " + message);
    }

}


Perfect! Now we'd like to update the Checkout class to use the Logger class:

public class Checkout {

    private SpecialLogger logger;

    public void finishCheckout() {
        logger.log("Finishing Checkout");
    }

}


Sounds good, because now the Checkout class doesn't know how a Log works under the hood, we're just using it!

Step 3: Configuration Mode to Log Messages

Sometimes we would like to change the mode that the log is printed out in. These modes could be:

  • Debug Mode
  • Info Mode
  • Error Mode
  • Etc.

Our SpecialLogger should receive the desired Log and print out the message using this Configuration Mode.

It's time to create the Configuration class that will receive the Log mode:

public class LogConfiguration {

    private boolean infoMode;

    private boolean debugMode;

    public LogConfiguration(boolean infoMode, boolean debugMode) {
        this.infoMode = infoMode;
        this.debugMode = debugMode;
    }

    public boolean isInInfoMode() {
        return infoMode;
    }

    public boolean isInDebugMode() {
        return debugMode;
    }

}


I know, that could be better, but this is just to keep the example clean and simple.

Let's change the SpecialLogger class to be able to Log a message based on a Configuration mode:

public class SpecialLogger {

    @Inject
    private LogConfiguration configuration;

    public SpecialLogger(LogConfiguration configuration) {
        this.configuration = configuration;
    }

    public void log(String message) {
        if (configuration.isInDebugMode()) {
            System.out.println("DEBUG LOG: " + message);
        }
        else if (configuration.isInInfoMode()) {
            System.out.println("DEBUG LOG: " + message);
        } else {
            System.out.println("DEFAULT LOG: " + message);
        }
    }

}


As you can see, we're checking if the log should use the Debug or Info mode, otherwise the message must be printed out by using the Default mode.

Let's try to run it:

WELD-001408: 
Unsatisfied dependencies for type SpecialLogger with qualifiers @Default
 at injection point [BackedAnnotatedField] 
 @Inject 
 private com.hackingcode.cdi.produces.Checkout.logger


Yes, an exception will be thrown in our face. And that makes sense. CDI does not know how to inject the SpecialLogger object.

CDI will try the following steps:

  • Hey, I can see that you need a SpecialLogger object. That's ok for me. I'll try to find this class
  • I found the SpecialLogger class
  • I'll try to create a new SpecialLogger object for you right now
  • Ohhh, I can't do that! The SpecialLogger receives a LogConfiguration in its constructor. I don't know what it is and I don't know how to create this !

Let's teach CDI how to use the LogConfiguration class in the next step!

Step 4: Creating a Factory With the @Produces Annotation in CDI

It's time to create a Factory to create the SpecialLogger based on the LogConfiguration:

public class SpecialLoggerFactory {

    public SpecialLogger createLogger() {
        LogConfiguration logInDebugMode = new LogConfiguration(true, false);

        return new SpecialLogger(logInDebugMode);
    }

}


It's a pretty simple Factory class. It has the method createLogger(), which returns a LogConfiguration object.

But CDI doesn't know about this class yet, or even its method! Let's use the annotation @Produces.

public class SpecialLoggerFactory {

    @Produces
    public SpecialLogger createLogger() {
        LogConfiguration logInDebugMode = new LogConfiguration(true, false);

        return new SpecialLogger(logInDebugMode);
    }

}

@Produces indicates to CDI that the method createLogger() should be used when a LogConfiguration object is needed

The @Produces annotation will say to CDI:

Hey CDI, please use the createLogger() method when you need to Inject a SpecialLogger object in some place

Of course, the returned object of the method createLogger() must be the same type that you're interested in, in this case the SpecialLogger.

That's it! I really hope that this article could be helpful to you!

In the next part we're going to hack a lot of code with CDI - Qualifiers

Don't forget to see all parts :)

Follow us to keep up to date!

Thanks!

BloomReach CMS: the API-first CMS of the future. Open-source & enterprise-grade. - As a Java developer, you will feel at home using Maven builds and your favorite IDE (e.g. Eclipse or IntelliJ) and continuous integration server (e.g. Jenkins). Manage your Java objects using Spring Framework, write your templates in JSP or Freemarker. Try for free.

Topics:
java ,cdi 2.0 ,factory methods ,produces annotation ,tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}