In the previous post, we learned that the only Contextual
s that matter, really, are Bean
s, 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:
- whose
create()
method calls the zero-argumentBackpack
constructor - whose
destroy()
method does nothing - whose
getTypes()
method returns aSet
that includesBackpack.class
- whose
getBeanClass()
method returnsBackpack.class
- whose
getScope()
method returnsSingleton.class
- whose
getInjectionPoints()
method returns aSet
that includes anInjectionPoint
whosegetType()
method returnsGorp.class
and whosegetMember()
method returns theField
in question
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:
- whose
create()
method calls the zero-argumentCandyShop
constructor - whose
destroy()
method does nothing - whose
getTypes()
method returns aSet
that includesCandyShop.class
- whose
getBeanClass()
method returnsCandyShop.class
- whose
getScope()
method returnsSingleton.class
But then it will also create a Bean
internally that represents the producer method:
- whose
create()
method calls theproduceCandy()
method - whose
destroy()
method does nothing - whose
getTypes()
method returns aSet
that includesCandy.class
- whose
getBeanClass()
method returnsCandyShop.class
- whose
getScope()
method returnsSingleton.class
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
:
- whose
create()
method returns the value of thepeanutButter
field - whose
destroy()
method does nothing - whose
getTypes()
method returns aSet
that includesPeanutButter.class
- whose
getBeanClass()
method returnsCandyShop.class
- whose
getScope()
method returnsSingleton.class
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.
Decorator
s and Interceptor
s
Two other primary Bean
implementations are Decorator
s and Interceptor
s. 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 Bean
s, and so are inherently producers of things, which can also be counterintuitive.
Custom Bean
s
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 Bean
s 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 ofBean
s, CDI performs typesafe resolution to see whatBean
s have bean types that “match” theInjectionPoint
‘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 eachBean
- Ultimately the
Context
, when asked to produce an instance of something, will call through to aBean
‘screate
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!