Jens Schauder is software developer since 1997. He loves software development for the constant challenges and constantly changing environment. A great chance to learn and teach. He is also blogger, author of various articles and speaker at conferences. Jens is a DZone MVB and is not an employee of DZone and has posted 86 posts at DZone. You can read more from them at their website. View Full User Profile

The One Correct Way to do Dependency Injection

01.10.2012
| 7919 views |
  • submit to reddit

A couple of weeks ago a coworker told me that they have a little utility in their projects in order to set private fields for tests. He kind of claimed they needed that since they are using Spring, which in production sets the dependency. I was a little confused. But after looking into it I realized that there is an (anti)pattern going on that needs some fighting against. Lets have a look at the pattern.

Spring (and other DI frameworks) can inject dependencies into private fields. It looks like people like to use this because it combines some compelling properties:

  • Very little boilerplate code. All you need is a simple annotation on the field.
  • Since the field is private nobody outside the class can change it.

But now when you want to test your class and want to assign e.g. a mock to the field you have to either setup a Spring context in your tests or use reflection in order to access the field (just as Spring does).

Now something is really starting to smell bad here. One of the purposes of Dependency Injection is to decouple code, which in turn should make the code easier to test. Now that didn’t work out to well.

So how can we clean up this mess? We can write a setter for the field. That solves the testing problem. It adds a little boiler plate code, but hey we are talking Java here, so you should be used to that. But now we have a setter which anybody can call at any time. At the very best that doesn’t make any sense at all in the production environment. In the worst case somebody actually uses it and creates some ugly bug.

So what do you do when you don’t want anybody to change a field? Correct you make it final. Great, now you have a compile time error in the setter.You can only set a final field in a constructor. This leads naturally to the solution of all our problems (and possibly to world peace): Make your dependency a constructor argument!

This is the only place where dependencies really belong. If you try to stick to that rule I’d expect you’ll encounter the following two problems.

  1. You’ll find classes with lots and lots of constructor arguments. I’ll bet that class does a lot of different things and really needs to get broken down into smaller pieces.
  2. You’ll find cases where a class A needs an instance of B and B needs an instance of  A. This is a typical case of a circular dependency and is obviously bad. In my experience the solution is either to make B a part of A when the two are so strongly dependent that they really should be one class. More often though there is at least one more class C hiding in there so that B doesn’t need A but only C.

So really the problems you’ll find when doing constructor based dependency injection are problems that are present in your current code already. So don’t shoot the messenger, but fix the problems in your code and go for constructor arguments for dependency injection.

 

From http://blog.schauderhaft.de/2012/01/01/the-one-correct-way-to-do-dependency-injection/

Published at DZone with permission of Jens Schauder, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Tomasz Nurkiewicz replied on Tue, 2012/01/10 - 3:26am

The pragmatic problem with constructor injection is that it requires you to declare dependencies three times: in the constructor argument list, as a field and in the assignment from the former to the latter. This is really ugly and not very DRY. Fortunately in Scala constructor arguments used outside of the constructor are automatically changed to final fields:

class Foo @Autowired() (
      bar: Bar,
      jdbcOperations: JdbcOperations) {
 
  def serverTime() = 
      bar.format(jdbcOperations.queryForObject("SELECT now()", classOf[Date]))
 
}
See my article for details.

Andrew Spencer replied on Tue, 2012/01/10 - 4:17am in response to: Tomasz Nurkiewicz

Tomasz, I agree, and there is another problem with constructors, which is that parameters aren't named (as they are with setter injection).  You can get round this by using a builder but then you have even more repetition (so much so that I've never yet found it worthwhile to write one).

BTW I think that shorthand for combining constructor arguments + final fields is Scala's nicest feature when used purely as an improved Java (that is, aside from the features like FP that are totally alien to Java).

Jens, nice article and I will be bookmarking it for use in future arguments debate.

Michal Xorty replied on Tue, 2012/01/10 - 4:47am

Dhanji was writing a lot about this problematic in his book "Dependency Injection" and he covered all pros/cons of wiring.

Perosnally I really fancy having @Inject or @Autowired directly at field (even thought I don't use it often since I also perfer constructor injection for mentioned reasons), but I'd like to have something like this:

@Inject(style = Injecting.CONSTRUCTOR) private SomeService service;

Annotation is therefore moved from constructor to field which makes code more readable for me.

Mladen Girazovski replied on Tue, 2012/01/10 - 5:00am

Just as Jens said,

the "Problems" (if you want to call it that way) with constructor injection are not DI problems.

 

@Michal

The issues with field injection mentioned in the article (and in every beginners guide for DI also) is that it isn't as easily testable as it should be, just try to replace your service with a mock for a test...

 

Michal Xorty replied on Tue, 2012/01/10 - 5:05am

@Mladen I am well aware of issues I just like to point out that I would like to use constructor injection while having annotation on field with saying to container, that it should inject it using constructor :)

Mladen Girazovski replied on Tue, 2012/01/10 - 5:10am

I see your point now, alright, it solves the mentioned Java problem of verbosity :)

Rogerio Liesenfeld replied on Tue, 2012/01/10 - 8:15am

Most current mocking tools for Java (JMockit, Mockito, and Unitils Mock, specifically) have support for automatic field injection of mock objects, so it's not necessarily the case that using "private field injection" makes unit testing harder.

Michal Xorty replied on Tue, 2012/01/10 - 8:33am

@Rogerio
That's usually not refactoring friendly (since you must specify private field name as String) and you should test public API not internals. It might not do unit testing harder, but it makes your object design less valid.

Mladen Girazovski replied on Tue, 2012/01/10 - 10:05am

@Rogerio

True, many "modern" Mockingframeworks can do amazing (or rather scary?) stuff and make previously un-testable code testable (like PowerMock, intercepting the new Operator, etc.).

Those "tricks" don't necessary make the test code simplier, easier to understand, maintain oder faster.

I prefer Constructor and even Setter Injection instead of tricky mocking libs.

Howard Lewis Ship replied on Tue, 2012/01/10 - 11:17am

Tapestry's IoC container will inject into fields, or via constructor arguments, or a mix of the two.

In addition, Tapestry can handle the mutual dependency situation: since Tapestry proxies services (for lazy initialization, and other reasons), each service implementation will be injected with a proxy to the other. As long as the proxies' methods are not invoked from the constructor, all will work (otherwise, Tapestry will produce a detailed report pinpointing the problem).

I prefer constructor injection; however, in Tapestry components, injection has been through fields. It gets a bit murky; in Tapestry component classes, a bytecode transformation step means that injection can take place into fields without using reflection. Field injection into services, which are not bytecode transformed, came later and does use reflection.

More details on the Tapestry home page. Tapestry IoC is designed to be used independently from the Tapestry web framework.

John J. Franey replied on Tue, 2012/01/10 - 9:34pm

You wrote:
We can write a setter for the field. That solves the testing problem. It adds a little boiler plate code, but hey we are talking Java here, so you should be used to that. But now we have a setter which anybody can call at any time. At the very best that doesn’t make any sense at all in the production environment. In the worst case somebody actually uses it and creates some ugly bug.

I agree with the former, except in the case where setting the property does make sense. Maybe the injected object is a default that is suitable for most but not all cases.

I also agree with the latter. However, the risk would be reduced by using default access level on the setter. Still a bit messy, but I think I prefer it to constructor injection.

Michal Gruca replied on Wed, 2012/01/11 - 3:43am

It should be simple. If the object needs some services (or beans as spring call them), then you should pass it in the constructor. 

If the dependency is  needed for one of the methods, then pass it to that method directly. If there is a group of methods that use one set of dependencies and other group that use other set of dependencies then you got 2 objects, that want to come out. Don't stop them and refactor them out.

If you stick to that simple rule it's easy to test as you always know which fields should be initialized. It's refactoring friendly and you can mark fields as final (what sometimes is useful, when writing inner class 

Michal Gruca replied on Wed, 2012/01/11 - 3:46am

On 'view layer' I just tend to inject by annotation, as I don't unit test it

Fabrizio Giudici replied on Wed, 2012/01/11 - 2:19pm

I agree that injection by constructors is better, but not because of the dependency of some facility in tests. Frankly I don't see your argument at all. You're blaming if tests depend on a library, such as Spring or Guice, or a third party small tool, while you seem to forget that tests depend on JUnit, or TestNG or other. I mean, let's not become paranoid wanting 0 dependencies because it's ok to depend on something that makes sense...

Anyway I prefer injection by constructors too because it enables final fields, thus possibly immutable objects. BTW, with Lombok you can just annotate with @RequiredArgsConstructor the class and the compiler will generate automatically a constructor for all the private final fields that are not initialized. Thus, the thing is perfectly DRY. 

The point is that sometimes you just can't use constructors because of a framework that manages the object life cycle and doesn't support custom constructors. JEE seems to me the most evident, but even Android is another good example. Thus, I don't think we can avoid @Inject and stuff that does injection by reflection and private fields.

Carla Brian replied on Mon, 2012/05/14 - 8:44am

Good thing I saw this post. I have the same problem as well. I am a newbie on this. - Instant Tax Solutions Ratings

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.