A CDI Primer: Part 3

In the previous post, we learned about typesafe resolution (the process of matching producers (Contextual implementations) with consumers) and where, exactly, Contexts fit in.

We also learned that you can “label” a Context with a scope, an annotation class standing in for the kind of lifecycle you want a Context whose getScope method returns it to implement.

We also learned that in some unspecified way you can apply the same annotation to a Contextual implementation.

The net result was that we sketched out our first hazy picture of how a Gorp-typed slot in a consumer (Backpack) might receive a singleton Gorp produced by a Contextual implementation whose type parameter is Gorp.  (If you’re wondering what all this is about, I heartily recommend you start at the beginning with part 0.)

Nevertheless, as we did this, we waved our hands furiously over certain details.  Specifically, while describing typesafe resolution, we made many references to CDI linking together producers and consumers without describing exactly how this happens, and we spoke a lot about Contextuals, but never really covered what they are, exactly.  We’ll look into that in some detail in this article.

Contextual Implementations: Beans

Throughout this series of articles, we’ve talked about Contextuals as being CDI’s implementation of producers in the dependency injection mindset (which we described in part 0).

Contextuals produce contextual instances.  And contextual instances are what Contexts supply to fill @Inject-annotated slots.  You get all that by this point I’m sure.  But we haven’t discussed what Contextualare.

Strictly speaking, of course, a Contextual implementation only has to implement the create and destroy methods.  Great.

Let’s say you write a two-method-long class that does that.  Now what?  Nothing, that’s what.

It turns out that CDI doesn’t really give you an “in” using this interface: you can compile a Contextual implementation, stick it somewhere, and it will happily sit there until the end of time waiting to be discovered and used, which will never happen.

But a subclass (subinterface) of Contextual is Bean, and we’ve all heard that term before.

As it turns out, the only direct subtype of Contextual that CDI inherently knows about is Bean.

So in most cases where I’ve written about Contextuals, you can substitute Beans, and be just fine.  A Bean “is a” Contextual, and CDI deals primarily in Beans.  (There are mechanisms throughout CDI for getting Bean implementations “into” the system, but none for directly getting “raw” Contextuals that are not Beans into the system.  We’ll cover this interesting structural fiesta below.)

So if that’s all true, why have I been talking about Contextuals all this time, and not Beans?

Because Contextual is the part of the Bean interface that deals with production (it consists solely of create and destroy methods, after all).  The rest of the Bean interface, as we’ll see, deals with other things.

We’ve backed into this series of articles from the production end: we have been shining a light on exactly how “CDI makes @Inject work”.  So production has been front and center, and that’s what Contextuals give you.

Now with the production facet of Beans under our belts, we can turn our attention to the other facets.

Let’s start with one such facet—the BeanAttributes interface.

BeanAttributes

CDI was defined originally back in an era where inheritance was used a lot, and composition…not so much.  As a result some compositional concerns in CDI are expressed—conveniently but a little opaquely—in terms of inheritance.  This is true of the trio formed by Bean, BeanAttributes and Contextual.  Luckily we can tease them apart.

Here is a class diagram showing that Bean inherits from both Contextual and BeanAttributes:

CDI Primer 3

This is certainly obviously how the interfaces are actually structured, but it hides the more important fact that each of the collections of methods defined by each interface is really a facet of the same underlying object.  With only a few rare exceptions, CDI assumes that the object “behind the curtain” of a Contextual interface, for example, will also implement Bean.

That is, a Bean “is a” Contextual, sure, but really a Bean “has a” Contextual facet: it has a facet that deals with production.  We’ve covered Contextual extensively.

A Bean also “is a” BeanAttributes, but really, again, Bean “has a” BeanAttributes facet: it has a facet that deals only with discovery- and typesafe resolution-related concerns.  (You’ll note in particular that the T type parameter used by create and destroy is not used anywhere in the BeanAttributes interface!)

A BeanAttributes is a facet of a Bean that is concerned with its overall place in the CDI world.  We’ll look at its methods selectively and carefully, ignoring those that don’t yet make any sense.

(And finally the collection of methods defined by the Bean interface itself is a facet of a Bean implementation that is concerned with its role as a consumer.  We’ll cover this below.)

getScope()

The first BeanAttributes method of interest we’ll consider is getScope(), which returns an annotation class representing a scope.

Here we can finally see explicitly what we handwaved over in part 2: effectively in CDI, every Contextual implementation is always also a BeanAttributes implementation, so every Contextual is capable of essentially reporting which Context manages the lifecycle of its produced objects.  See part 2 for more on this.

This lets BeanAttributes that are producers (which is to say all of them, since in CDI’s ecosystem they’re all also effectively Contextuals) still have a say in the lifecycle management of their produced objects, but they don’t actually have to implement that management themselves.  Nice.

getTypes()

The next one is getTypes(), which reports the Set of Types that a producer can make.  Again, this method only makes sense when it is tacitly understood that BeanAttributes and Contextual are always facets of the same underlying object, i.e. that all BeanAttributes are always producers (Contextuals).

So, for example, if your Contextual implementation gets instantiated with a type parameter value of Object, then if your Contextual implementation is also a BeanAttributes implementation, as it should be, you can specify with this method exactly what subtypes of Object your create method produces.

CDI calls this method to gain insight into how to “fill” a consumer’s @Inject-annotated slot with an appropriately-typed contextual instance.  When trying to locate a suitable producer it can winnow the field by looking at the return value from this method and perform typesafe resolution using the results.  (For more on typesafe resolution, see part 2.)

Other BeanAttributes Methods

We’re going to table discussion of the other BeanAttributes methods for now, as they’re really another layer on top of these fundamentals.  We’ll come back to them in a later article.

Bean

Finally, the third facet of the Bean interface consists of the methods it defines itself apart from those defined by Contextual and BeanAttributesThese methods are related to a producer’s also being a consumer.

On Consuming Producers, Producer Classes and InjectionPoints

Back in part 0, we saw that there’s nothing wrong with a producer also being a consumer!  Producers frequently need to consume raw materials to do their jobs.

We also saw that producers come in many abstract flavors: methods, fields and constructors.

A producer also needs a “host class” (a method, field or constructor has to “live in” a class, after all), and in part 0 we decided to call such host classes producer classes for want of a better term.  (CDI calls them “bean classes” for reasons we are only now capable of understanding and which we’ll look into below.)

We’ve also talked throughout this article series about consumers and producers in terms of “slots” and “filling slots”.  I have a Backpack; it has an @Inject-annotated Gorp-typed “slot”; CDI performs automatic wiring and “fills” the “slot” with a contextual instance of Gorp sourced from the right Context by way of a Contextual implementation.  (We went into some detail on this example in part 1.)

In the spirit of less handwaving, let’s call these slots what they’re actually called in CDI: injection points.

An injection point is a slot that can be filled with a contextual instance.  It is a point at which CDI performs (dependency) injection.  Injection points are represented in CDI by the appropriately-named interface InjectionPointInjectionPoints have a type (and some other stuff, which we’ll cover another day).

So our Backpack consumer has a Gorp-typed injection point.

While it does not explicitly use these terms, CDI says that a consumer simply is something with InjectionPoints: it is a Thing With Slots That Have To Be Filled.

As we’ve seen, effectively all Contextuals—all producers—are also Beans.

And we’ve seen that a producer can be a consumer.

And finally we’ve seen that a producer is housed in a producer class, which I’ve told you CDI calls a bean class.

So it should come as no surprise that this consumer facet of a Bean is represented by two methods:

getInjectionPoints is pretty simple: it returns the set of “slots” that the producer needs values for in order to do its job.  If the producer is a method, for example, then the injection points will represent the method’s parameters.  If the producer is ultimately represented by a constructor, for a more complicated example, the injection points will represent possibly the constructor itself as well as any other @Inject-annotated fields and methods in the class.

getBeanClass is a little trickier, and is in my experience one of the most misunderstood methods in all of CDI.

​​getBeanClass()

getBeanClass returns the producer class, not the class of the thing being produced.

That is, if you have a producer that is a method, getBeanClass returns its “host class”—its declaring class—not the class of its return type.  This will become important when we talk about CDI producer methods.

Similarly, if you have a producer that is a field, getBeanClass returns its declaring class, not the class of the field.  This will become important when we talk about CDI producer fields.

Finally, if you have a producer that is fundamentally a constructor, obviously a constructor makes instances of its own declaring class, so getBeanClass will return the producer class, sure, but that will also be the class of the thing being constructed!  This will become important when we talk about the most common kind of Bean implementation found in CDI: the managed bean.

The most important things to take away from these CDI internals so far are:

  • CDI has many ways of discovering things, but all producers and consumers from the dependency injection mindset can be represented in CDI as Bean implementations
  • a Bean is fundamentally a producer of things, not the things produced, and a consumer only secondarily
  • all Contextual implementations normally found in a CDI application are Bean implementations too

Automatic Wiring (Almost Entirely) Demystified

Our picture of how CDI accomplishes automatic wiring just got a little clearer.  Every producer in the system now has a facility for reporting:

  • what kinds of things it makes (getTypes())
  • what Context the things it makes should “belong” to (getScope())
  • what it needs to do its job (getInjectionPoints())
  • what class it “lives in”, of which an instance might need to be produced for the producer to be invoked (getBeanClass())

If CDI could find Bean implementations in various flavors lying around, and then normalize them into true Bean implementations in memory, it could match the Gorp-typed InjectionPoint of the Backpack-typed Bean implementation to hopefully the one Bean implementation whose getTypes method contains Gorp.class in its return value.  It could then locate the appropriate Context implementation, by looking at the return value of the getScope method, and could ask that Context to get a Gorp.  It could then use the InjectionPoint to set the actual Gorp value.

That is, of course, exactly what CDI does.

In the next post, we’ll get rid of the remaining handwavy bits and will look at how CDI:

  • “normalizes” “Bean implementations in various flavors lying around” into “true Bean implementations in memory”
  • “locate[s] the appropriate Context implementation”
  • Uses “the InjectionPoint to set the actual Gorp value”

Thanks for reading along so far!

Advertisement

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.

One thought on “A CDI Primer: Part 3”

Comments are closed.

%d bloggers like this: