A CDI Primer: Part 4

In the previous post, we learned that the only Contextuals that matter, really, are Beans, and we learned a little bit about injection points and loosely how they’re filled.

In this post we’ll look at how CDI actually discovers things, and how it normalizes them into Bean implementations, and how it marries them with Context implementations to figure out what their lifecycles are going to be.

The Container Lifecycle

CDI has a very well-specified lifecycle.  There is a general startup phase during which all the raw materials in the world are discovered, arranged and pared down to form a network of Bean implementations that produce and consume each other.  Once that startup phase has completed, the actual application—whatever it might be, that makes use of all these producers and consumers—starts, and runs, and does its thing.  Then there is a shutdown free-for-all where all Bean implementations can, indirectly, get a chance to clean up, and that’s all there is to it.

But what are you, the end-user, the ordinary developer, supposed to do with all this?  In your hand, you have a class that has some injection points in it (fields or methods annotated with Inject), and you want them filled.  What does this have to do with Bean implementations?  After all, your class doesn’t implement Bean.  We’ll see how internally, in a way, it sort of does.

Discovering Things

As part of the startup phase, CDI performs type discovery and bean discovery.

Type discovery is a fancy name for taking stock of what classes exist on the classpath.  Plugins, called portable extensions, can affect this; that’s a subject for another day.  For now, know that CDI will scan the classpath for particular classes and will add them to a set of such classes that represents its type world.

Once types have been discovered, CDI performs bean discovery, where some of those types it found lying around are normalized into Bean implementations internally.

It’s important to realize what’s going on here.  For the typical developer scenario, most classes that are discovered are turned into producers.  This can be a little counterintuitive—I don’t know about you, but that’s not usually what I think of when I have a class in my hand named with a noun, like Person or Customer.  Really?  Customer is a producer?  A producer of what?  Well, of instances of itself, it turns out.  Which of course we all actually know, because it has a constructor.

Managed Beans

So consider our ongoing stupid example of a Backpack class, with a simple zero-argument constructor, annotated with Singleton, and a Gorp-typed field annotated with Inject.  Let’s say that CDI discovers it.  Internally during bean discovery CDI creates a Bean to represent it:

A Bean implementation built internally like this is called a managed bean, and the class itself is frequently called a managed bean as well.

It’s important to realize that for a given managed bean one of its bean types and its bean class are always identical.

Producer Methods

Another very common kind of bean is a producer method.  In this case, CDI has done its type discovery, and has found a class called CandyShop also annotated with Singleton (I’m picking Singleton because it is a scope that everyone is intuitively familiar with.)  Let’s say this class has a method in it declared like this:

@Produces
@Singleton
public Candy produceCandy() {
  return new Candy();
}

This time, CDI will create a Bean internally for CandyShop that looks a lot like the one above, namely:

But then it will also create a Bean internally that represents the producer method:

Do you see that the bean class here is CandyShop but the getTypes() method returns a Set of bean types that includes Candy.class, not CandyShop.class?  The takeaway here is that usually when you’re considering a true CDI bean you are, as a normal developer, interested in one of its bean types, not its bean class.  In other words, you’re interested in (in this example) Candy.class, and normally not so interested in the fact that the producer method that creates its instances happens to be housed in a class called CandyShop.  The terminology in the documentation certainly does its best to make this about as clear as mud.

Producer Fields

Just as you can have a producer method whose bean class is one thing while its bean types are another, you can have a producer field.  Let’s say our CandyShop class from the example above also has a field in it declared like this:

@Produces
@Singleton
private PeanutButter peanutButter;

Once again, CDI will create a Bean for CandyShop, just as it did before, and here it will also create another Bean:

In practice, producer fields are comparatively rare.  They exist primarily to bridge the worlds of Java EE, where you might have a field that is annotated with Resource (so is being set by some kind of JNDI-aware machinery), and CDI where you can have the very same field annotated with Produces.  This lets CDI set up a Bean implementation under the covers that creates instances from Java EE-land without any other aspects of the CDI ecosystem really being aware that Java EE is even in the picture.

Decorators and Interceptors

Two other primary Bean implementations are Decorators and Interceptors.  I’m not going to talk about them because although it may look like it by this point I’m not writing a book.  Suffice it to say they are represented in the CDI ecosystem by Beans, and so are inherently producers of things, which can also be counterintuitive.

Custom Beans

Finally, without digging deep into portable extensions, we can at least say that portable extensions have a variety of ways to cause a Bean implementation to be created by hand at container startup time.  When Beans are added into the system this way, the portable extension has absolute full control over them.

Putting It Together

The biggest takeaway here is that everything in CDI is a Bean.  In the documentation, almost everything is a little-b bean.  A bean, represented by a Bean, is fundamentally a producer of something.  There are recipes that we’ve just enumerated that translate ordinary Java constructs into Bean implementations when bean discovery is performed: a creator and a destroyer (Contextual) together with bean types and a scope (BeanAttributes), a hosting bean class and a set of injection points (Bean).

There’s another thing that falls out of this.  Bean is a parameterized type: you have a Bean<T>.  The <T> is, however, always usage-specific, because it comes by way of Contextual, whose usage of its type parameter is the return type from its create method.  That is, <T> represents one of a Bean‘s bean types—one of the kinds of things it can make—not its bean class—the class hosting the producer method or constructor.

Consider a Bean<Person>.  Because a Bean<Person> is a Contextual<Person>, it follows that you can call its create method and get a Person back.  But it does not follow that calling its getBeanClass() method will return Person.class!  Perhaps the Bean<Person> you are looking at represents a producer method, or a producer field, for example.

Finally

So finally we can see how CDI makes @Inject work, which was what we set out to do in part 0:

  • CDI discovers types
  • For each type found during type discovery, CDI creates a Bean to represent it, or at least tries to
  • For each producer method and producer field (and other cases), CDI creates another Bean to represent it
  • For every InjectionPoint found in this pile of Beans, CDI performs typesafe resolution to see what Beans have bean types that “match” the InjectionPoint‘s type
  • Assuming that all slots get matched up to producers, whenever CDI needs to obtain an instance of something, it does so through the right Context which is in the business of handing out contextual instances
  • CDI can find the right Context by checking out the scope available from each Bean
  • Ultimately the Context, when asked to produce an instance of something, will call through to a Bean‘s create method

I’ve deliberately left out qualifiers, which are extremely important, but are deserving of their own article later.

There’s much more to say about CDI, but I think I’ll stop there for now.

Thanks for reading this primer!

Advertisements

Author: Laird Nelson

Devoted husband and father; working on Helidon at the intersection of Java, Jakarta EE, architecture, Kubernetes and microservices at Oracle; open source guy; Hammond B3 player and Bainbridge Islander.