Why use CDI in Java EE
I know there are a lot of articles out there that explain how to use CDI in Java EE but I'm having trouble figuring out what advantage this actually brings. For example, suppose I have a class that currently uses an instance of Foo. I might either do
Foo myFoo = new Foo();
or
// Better, FooFactory might return a mock object for testing
Foo myFoo = FooFactory.getFoo();
I keep reading that with CDI I can do:
@Inject
Foo myFoo;
but why is this better than the previous factory based approach? I assume there is some other use case that I'm not aware of but I haven't been able to identify this.
If I've understood the responses below, the concept is that the DI framework acts as a master object factory that is configured centrally. Is this a reasonable interpretation?
Update
I've since started learning Spring and this now makes a lot more sense. The paragraph below is taken from Spring in Practice taking an example of an AccountService
class which in turn, uses an instance of AccountDao
. I apologise for the long quote but I think it really gets to the heart of why injected resources offer something over standard initialisation.
You could have constructed the AccountService using the new keyword, but the creation of service layer objects is rarely so straightforward. They often depend on DAOs, mail senders, SOAP proxies, and whatnot. You could instantiate each of those dependencies programmatically in the AccountService constructor (or through static initialization), but that leads to hard dependencies and cascading changes as they’re swapped out.
Additionally, you could create dependencies externally and set them on the AccountService via setter methods or constructor arguments. Doing so would eliminate the hard internal dependencies (as long as they were declared in the AccountService by interface), but you’d have duplicated initialization code everywhere. Here’s how you create a DAO and wire it up to your AccountService the Spring way:
<bean id="accountDao" class="com.springinpractice.ch01.dao.jdbc.JdbcAccountDao"/>
<bean id="accountService"
class="com.springinpractice.ch01.service.AccountService">
<property name="accountDao" ref="accountDao"/>
</bean>
Having configured the beans as above, your program can now request an instance of AccountService
from the Spring ApplicationContext and the Spring DI framework will look after instantiated everything that needs instantiating.
The people that wrote CDI gave you one big object factory; they did the work for you, better than you would. It's XML configuration or annotation driven, so you don't have to embed everything in code.
Dependency injection engines, like Spring, do a lot more than your factory. It'll take more than one factory class and one line of code to duplicate all that they offer.
Of course you don't have to use it. You are always free to invent your own wheel. And you should - if your purpose is to learn how to make wheels or eliminate dependencies.
But if you want to just develop applications, it's better to use the tools that others provide when they give you an advantage.
The seminal article on dependency injection was written by Martin Fowler. I'd recommend reading it; it's still great, eight years later.
"still not clear on what the more is"
Here are a few advantages:
- Looser coupling
- Easier testing
- Better layering
- Interface-based design
- Dynamic proxies (segue to AOP).
The purpose of using dependency injection is so that the code using the thing that's injected doesn't have a dependency on the factory. With your factory code example there's a static method call embedded in your code that is not needed there with the DI approach.
The thing that is being injected with myFoo
shouldn't have to know about the factory. The factory puts limits on your options for testing that aren't there with DI. Specifically if you have a Bar object that has a Foo, a test of that Bar object can directly create a Foo or mock Foo and put it on the Bar without needing to involve a FooFactory, and you don't have to write a FooFactory, which is boilerplate that doesn't do anything to implement application business logic.
This is an important and subtle question about what enterprise programming is all about.
The name is well chosen: contexts and dependencies.
CDI has nothing to do with better or cleaner code, it's about ensuring that large organizations can build complex, distributed software systems and share data. It's about making 100% sure that governments or other bureaucracies can indiscriminately distribute self-contained, well-documented packages for every piece of software they control. Remember that these days, virtually any POJO can be injected.
Let's say you're building a client app of some sort, and you want it to print the first name of the user in the corner.
The enterprise architects of this large company would like you to have this capability, but as a junior software engineer, there is no chance of you being handed the keys to the DB.
They would also like to secure the data across the network, but the company isn't paying any engineers to re-engineer an authentication client every time they need to share a scrap of data.
They would like for you to be able to be able to query and update this info, but would like transactions to be handled at a higher level than any one app.
They would like for you to be able to test your classes with trivial mocks in setup blocks.
They would like for coupling between classes to involve a minimum of static methods.
And on and on and on...
Most JSRs probably have a "EAs would like to be able to..." buried inside somewhere.
CDI is preferred because it allows apps of large (arbitrary?) horizontal and vertical scales to share contexts, dependencies, and therefore data.
In Fowler's words:
"The problem is how can I make that link so that my lister class is ignorant of the implementation class, but can still talk to an instance to do its work."
" But if we wish to deploy this system in different ways, we need to use plugins to handle the interaction with these services so we can use different implementations in different deployments."
"The approach that these containers use is to ensure that any user of a plugin follows some convention that allows a separate assembler module to inject the implementation into the lister."
In a nutshell, they allow for centralized "command and control" of complex enterprise applications. Java EE is a systematized, reliable process for abstraction and CDI is a an incarnation of it that works so well, it almost makes it invisible. It makes the stitching together of complex apps almost trivial.
Two more things:
Note that CDI exists peacefully alongside the "service locator pattern", known as JNDI in Java EE, which is preferable if the client developer will need to choose among many identically-typed alternatives.
CDI is more firepower than is needed in many cases, especially non-enterprise (literally) cases.
At a high level, as with most things on CompSci, it offers a level of indirection (or abstraction) that would otherwise be hardcoded in your application as Foo myFoo = new Foo();
. That indirection brings about loosely coupled code, which makes things modular, which makes it easy to replace, service, test etc classes or sub-systems in a simpler manner.
Note that there are many designs and patterns for indirection/abstraction - dependency injection is just one.
The other aspect of your question is "Why CDI?" - well, because someone has already done the work for you. You can always build your own stuff, but it's usually a waste of time when the objective is to build a real world system that must perform under budget and on time. Why bother with groceries and cooking when there is a Michelin starred chef who's willing to do that work for you?