Today’s following the error will feature Spring dependency injection framework. Spring is very popular in the Java world, and have a bunch of helpful classes.
There is a lot of documentation < http://static.springsource.org/spring/docs/2.5.3/reference/beans.html >
Lets start setting up some context.
Imagine this scenario: You want a program that will have Extractor’s, and a class that will pick all those Extractor’s and run it. It is quite simple.
So, to use it with Sprin beans, which are components that can be injected around your code, you annotate it with @Component. @Autowired annotations will try to assign components that match that type.
The get an object with the variables annotated filled with values you rely on the Spring Context.
I wont be explaining much of the context.xml, but that is what Spring will read to setup the beans and scan your code for @Component’s annotations.
Oh… So Spring can do that?!
So far, so good. Now we just need to feed that list of Extractor’s to the component. But how to do that?
A cool thing of the dependency injection system that Springs implements is that when you @Autowire a List, it will select all the beans that match the type of the List and put it there.
It means that if I create several different extractors, all of then will be injected into that list.
We just need to create our different beans to get our little app working.
That is pretty cool.
And the requirement changes.
We were happy that everything was coming up together, and the world changes. We need to have different Extractor’s for different Spring Profiles. We have one profile that includes 3 extractors, and another one that have 0.
"That is pretty easy. You just need to skip the creation of the beans."
Let the errors begin.
No. A stack trace. *phh*
We face that big pile of red lines.
Let’s see. Hum, no matching bean type. Oh, there is no Extractor’s now. It requires an Extractor to inject. But what I want is an empty list if it does not exist.
Lets see if we can say “Hey, that is not required”.
@Autowired( required = false )
We discover that the @Autowired annotation have an option to say it is not required. “Yes” - you say.
Ok, let’s try it out.
Not again… “But I just said it is not required”.
I tried searching on the documentation about another ways to say something is not required. There is a @Required tag, but it is only possible to use on fields, not on methods or type annotations.
Lets dive in the code.
After spending some time struggling with annotations and tweaking configurations I decide to take a look on the source code for Spring.
It is a big framework, but the last stacktrace have some clues on where to start digging.
That is where I start. The code is a bit dense to read, and after a while, reading both the code and the documentation I understand what the @Autowired( required = false ) means.
On the url < http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/factory/annotation/Autowired.html > you see that you can have multiple @Autowired’s constructors.
On the documentation there is this line < http://static.springsource.org/spring/docs/3.0.0.M3/reference/html/ch04s11.html >
but multiple non-required constructors can be annotated. In that case, each will be considered among the candidates and Spring will use the greediest constructor whose dependencies can be satisfied.
Reading the code something clicks in my mind. Saying that @Autowired is not require only means that it will skip the injection of the beans in case of failure, but because it is the constructor, it cannot create the component at all if it skips.
Fixing the code
We need to fix to things to keep coding. One is to create the component if there is no components and the other is to keep an empty list for the Extractor’s list.
To create a component without @Autowired, Spring tries to use the default constructor for the object. We create a constructor and annotate it with @Deprecated in order to inform other people that you shouldn’t use that constructor.
Spring is happily creating our component now.
Now that we know that a @Autowired(required = false) do, we can use it.
Because it will skip the initialisation using that method due to the error on the missing bean, Spring will fallback to the default constructor only in this case.
Then, we can initialise the list with an empty list, as we wanted before.