Help

I've seen a couple of folks wondering why CDI requires a beans.xml file in every bean archive. If there's no alternatives, interceptors, or decorators to declare, why do you need to have the empty file?

Well, there's two things about CDI that we need to keep in mind:

  • CDI does not require any special declaration for a Java class to be injected - that's right, no annotation or XML declaration at all!
  • CDI does not define any special kind of module - CDI beans can be deployed in a library jar, EJB jar, war, rar, or JVM classpath directory.

The CDI specification calls the process of identifying beans in modules bean discovery.

So there are potentially a lot of classes in the classpath which might be beans! We don't want to force the container to scan every one of those classes at startup and build its internal metamodel for each of them. This really could make bean discovery very slow.

But there's another reason we need to give the user some control over which classes are available for injection. We don't want every class in the classpath to potentially match an injection point, including classes that were never intended to be injected into things. This would force the developer to have to use qualifiers much more often to disambiguate injection points.

So we have two choices. We could have the developer:

  1. explicitly exclude modules which do not contain beans, or
  2. explicitly declare modules which do contain beans.

I think it's clear that the second option is a much better way to go. Thus, CDI has the notion of a bean archive. A bean archive is just a module that has a file named beans.xml in the metadata directory. The container looks for beans in bean archives. It ignores other modules.

Now, you might be wondering if we've got the granularity wrong here. Why should module be the right criteria to use for including/excluding a class. Why not consider:

  • a class-level annotation,
  • the package,
  • some type it implements or extends, or
  • some naming convention.

Well, I think we've got the first option covered. Annotate a bean @Alternative, or with an alternative stereotype, and it will be considered disabled by CDI, as long as you don't explicitly enable it in beans.xml. That's not quite the same thing as excluding the class from scanning altogether, but it's close. (One difference is that a portable extension with still get a ProcessAnnotatedType event for that class.)

Excluding a package makes sense to me, and a future version of CDI might allow you to declare excluded packages in beans.xml.

Excluding a bean by type or naming convention doesn't appeal to me at all. In the world of CDI, we use stereotypes for identifying architectural roles. We don't use marker interfaces or naming conventions. I strongly disapprove of having names affect functionality.

We'll experiment with giving you some finer grained control over bean discovery in Weld. However, my expectation is that the current solution is going to work great for most people.

4 comments:
 
21. Dec 2009, 06:15 CET | Link
Piero Sartini | piero(AT)sartini.de

I am not sure I like this behaviour. IMHO it would be way better to have the possibility to define top level packages that are used while discovering beans. In general I like to define what should be included, not what should be excluded.

While prototyping a webapp, it's convenient to have all my classes inside one war - and split them into different modules when they get more stable.Also, how does CDI play together with other IoC containers? Take Tapestry as an example: someone would need to exclude all tapestry-ioc managed classes (pages, components, services, ...) in order to use CDI inside the war. Not very developer friendly.

Maybe I am missing something.. so please take this as the oppinion of someone who just started to learn CDI.

 
28. Jan 2010, 05:21 CET | Link
Thomas

Gavin, you saved my day! I was searching for 3 days, why my @Injects are not working, reading this, I know why.

Greets from Germany

 
12. Apr 2010, 03:26 CET | Link

One thing I am struggling with in CDI is the fact that there is basically only one global scope. If I understand correctly, when I have one interface X and an implementation XImpl in the same bean archive then XImpl will be injected when '@Inject X x' is used.

However, when I add a new bean archive to my classpath that contains another implementation of X, then all of a sudden a different implementation would be used. This might be desired behavior but could also be very unwelcome.

In other words, what I would like to be able to define is something like an injection domain, where an injection domain would consist of an explicit list of packages or even classes. Then, for a given injection I could define the injection domain explicitly.

Another question I have is w.r.t. unit testing. In unit test code, I typically create a mock object (for instance using Mockito), like so: X x = mock(X.class);

Then I want to verify interactions on x and want to make sure that my mock is injected instead of the normal implementation. The only way I see that this is currently possible is by defining an alternative and then creating a separate beans.xml only for unit test to enable a mock alternative. Of course, the mock defined in beans.xml is not the mock I create in my unit test (I don't even know the classname of this mock). So from the unit test I would need to make my mock object available as static and then from the mock class declared in the beans.xml delegate to my mock object. This approach is rather cumbersome. Is there perhaps another way to do this?

 
12. Apr 2010, 03:28 CET | Link

Small correction: adding another bean archive with another implementation would lead to an ambiguity that I would need to resolve.