In the previous article, we dabbled briefly in looking at the surface of the iceberg that is the CDI machine for making
@Inject work as a way of answering some basic questions about what, exactly, CDI does and why you might want to use it.
We also, much more importantly, looked into the dependency injection mindset. We talked about abstract notions of producers and consumers and wiring them together. We introduced the idea that if you look through the right lenses, fields, methods and constructors can all be producers housed in particular producer classes. And finally we pointed out that producers can be consumers and vice versa.
Now let’s talk about lifecycles.
Object Lifecycles and “Producer Proxies”
When a producer of any kind “wants” to make or supply or acquire an object, two decisions have to be made:
- how to make or supply or acquire the object
- when to make or supply or acquire the object regardless of how that happens
Some producers, as we saw in the previous article, are, as often found in the trenches of enterprise Java development, what I’ll call singleton suppliers. They acquire the (mostly!) One True Instance™ of the object in question somehow, sometimes creating it once if necessary, sometimes by interrogating some ancient snarling hairball of a legacy system, and then from that time forward in the whole life of the application, that’s the object you get if you ask them to supply it to you.
Other producers, like constructors, create a new object each time one is to be produced because they can’t do anything else, or they’re kind of uninterested in the ways that they might be called, so they shrug and say, hey, if you want a new object, call me; otherwise, don’t; I don’t store or cache nothin’.
What’s interesting about producers of any kind, in isolation, without any other technologies such as CDI or its subsystems in play, is: producers in isolation combine how an object is produced with when it is produced.
For example, what I’ll call singleton suppliers—those all-too-common “enterprisey” methods that somehow acquire a Singleton From Elsewhere™ and then return it when asked—are combining the mechanics of how the singleton is produced (maybe it’s looked up from some other system) with when it is produced (maybe this system lookup only happens once and then the result is stored as a
Or constructors: if you acquire an object from a constructor—a kind of producer, remember—then no matter what you do and no matter what it does you’ll get a new instance of the produced object each time because that’s what constructors do.
What would be nice is to let producers do what they do—acquire or make things when called for—and have there be some other subsystem that controls when a produced object is handed to a consumer. Something that can go “back to the well”—the producer “well”—when needed, but not when not needed, and is in full control of when that happens, but does not itself know how the manufactured items it is storing or caching are made. Something that is kind of like a proxy for producers: something that can stand in front of them and hand out their results when appropriate, regardless of how they were made or from what system they were acquired, according to its own notions of lifecycle.
In such a situation a producer can focus on how to acquire an object but not on how to cache it; a producer proxy can focus on when to cache an object, if at all, and when to clear the cache; a consumer, in grand dependency injection mindset fashion, can remain blissfully ignorant of both of these things.
Also in grand dependency injection mindset fashion, we want this producer proxy (my term, incidentally, not CDI’s) to not look up or acquire or otherwise scrounge around for its relevant producer(s). We want it to simply declare somehow that it needs one, and then punt the problem of getting one to its caller (we’ll talk about who that might be in a moment).
CDI implements this producer proxy concept with a construct called a
Context and it is the first CDI construct we’ll look at in depth.
The first thing to know about a
Context is that as a CDI end user you’ll interact with it many, many times—and as a consumer you’ll never see it or know that’s what you’re doing.
Context, in other words, is part of the internal CDI plumbing by which automatic wiring between producers and consumers is implemented.
Context is the nexus where a consumer needing an object of a certain kind is wired in an abstract fashion to a producer that is capable of producing objects of that kind, and where the lifecycle of such a produced object is managed.
Inside the depths of CDI, when you mark a field or a parameter with
@Inject, CDI asks a particular
Context for the kind of object you want. That
Context, in turn, ends up asking a producer, when necessary, to make or acquire the kind of object that should go in your
From the standpoint of a
Context, a producer is represented by something kind of odd called a
Contextual is simply a producer that can also destroy the things it makes. A
Contextual can make any number of different things of a given type (so, for example, a
Contextual whose type parameter is
Object could make
PeanutButter or whatever). Most of the time, though, a given
Contextual makes one kind of thing.
Contextual should not have, as its core concern, or any concern if at all possible, how long an object should live—it is a “pure” producer: it just makes ’em, ma’am, it doesn’t hang onto ’em.
Here is what the contents of the
Contextual interface look like, in their entirety, and for now we can ignore the second of these two methods:
|public T create(CreationalContext<T> creationalContext);|
|public void destroy(T instance, CreationalContext<T> creationalContext);|
I hope you can see how simple that is.
Contextual is another one of those interfaces that is in the internals of CDI. You rarely, if ever, implement it directly. But you could. And you do in some cases, usually indirectly, as we’ll see much later.
For example, ignoring
CreationalContext—a subject for a later post—you could see that you might implement the
create method in such a way that it wraps a constructor invocation (you’d be implementing a producer that is constructor-based). Or you could see that you could implement the
create method in such a way that it wraps a method invocation (you’d be implementing a producer that is method-based), though consuming other dependencies in this case might be a little trickier. Or a field access. And in many cases your
destroy implementation might not have to do anything.
The takeaway here is that you can represent all producers as
Contextuals of a particular kind.
Context, then, uses
Contextuals to produce the objects it will then manage the lifecycle of. Here are the (relevant at the moment) contents of this interface, and trust me when I tell you that for now we can ignore the second method:
|public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext);|
|public <T> T get(Contextual<T> contextual);|
Again, very simple.
The first method is the real workhorse. It takes in a
Contextual and a
CreationalContext, as you can see. Once again, we’ll ignore the
CreationalContext. (The second method is for certain cases where CDI wants to ensure that no creation at all happens: it just wants the
Context to supply a cached instance if one exists; it doesn’t want the
Context to make a new one. We’ll ignore it for the sake of mental clarity.)
The first method’s contract is to acquire and return a
T. You’ll note that
Context itself is not a parameterized type (it doesn’t have any angle brackets or type parameters in its name). So you can see here that a
get a whole variety of different kinds of objects.
You can also probably squint and see that if I tell you (as I have) that a
Context‘s responsibility is to manage the lifecycle of objects, not actually make them, then when it decides that it needs a new one it can (and should) just call the
create method on the
Contextual that has just been handed to it. The
Context knows “when”; the
Contextual knows “how”.
If it already has an existing instance and has determined that this incoming demand can be satisfied by the existing instance, well, then it can just hand it back.
That’s all very convenient. So a
Context as a consumer remains blissfully ignorant of what kind of
Contextual (producer) has been handed to it, and can use this raw material if it wants in order to fulfill its contract of supplying
T instances. And the
Contextual can remain blissfully ignorant of lifecycle and caching concerns and can just return Its Thing™, whatever that might be, when asked to make it.
…and Dependency Injection
CDI stands for Contexts and Dependency Injection. And now with the introduction of
Contexts we have an incomplete and blurry view—but a view nonetheless—of how the major parts work together, and why “Contexts” is important enough to be listed in the title of the specification:
- A consumer of some kind (your business-critical class) gets wired in some currently opaque-to-us way (involving
@Inject) to a
Contextimplementation that supplies it with the dependency it needs (via the
Contextimplementation gets wired in some opaque way to a
Contextualimplementation that knows how, but not necessarily when, to make those kinds of things.
Contextualis a kind of producer but everybody but the
Contextualimplementation itself doesn’t know what kind.
The result is that CDI is now conceptually capable of injecting dependencies, taking into account desired lifecycles, and letting producers and consumers and producer proxies all focus on what they do best, and on nothing else.
I’ve spoken very loosely about producers making things of a particular kind. And I’ve spoken equally loosely about consumers needing things of a particular kind. And I’ve spoken just as loosely about the wiring process where a producer of a thing of a particular kind gets wired in some fashion to a consumer that needs those things, mediated by a producer proxy—a neutral term I invented to describe the lifecycle/lifespan-managing component that decouples when something is handed out from how it is made or acquired.
Now we’ve also learned that producers in CDI are represented by
Contextuals and are effectively “fronted” by
Contexts, and that producer proxies are represented by
Contexts, and that
Contexts therefore are the sources of instances that producers—
Contextuals—make. Consequently when a
get method hands you (indirectly) an object, that object is known throughout CDI’s literature as a contextual instance. It is an instance of something that a given
Still speaking loosely, when you ask for a
Gorp to be supplied to you (injected) by CDI by using the
@Inject annotation, you’re going to get a contextual instance of
Gorp in the annotated “slot”.
Or, equivalently: if an object “comes out of” a
Context, then it is a contextual instance, and its existence must be owed to the fact that CDI invoked
get on a
Context and the
Context ultimately invoked the
create method of a
Contextual that produced it. CDI “knows about” all contextual instances. CDI does not know about (for the most part) objects that are not contextual instances.
In the next post, we’ll look at the mechanics of how CDI wires
Contextual implementations that can make certain contextual instances of particular kinds to
Context implementations that implement a lifecycle, and how CDI wires consumers of contextual instances to
Context instances that can provide them.