Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

JavaWorld Daily Brew

Thinking Inside a Bigger Box

Welcome to Johannes Brodwall's blog. I use this space to work on articles mostly about software development, with a focus on Java, SOA, and Agile software development. Many of the articles you will find here are not much more than drafts, and I certainly appreciate input on how to make them better.

If you wonder about the title of this blog, Thinking Outside the Box may answer your questions.

I work as the lead software architect of BBS, the company that handles interbank services in Norway. In my copious free time, I develop software and consult companies in development practices and architecture. For more about the services I can offer, please see my resume.


This dependency injection madness must end!

 

Or: Poor man’s dependency injection: Singleton-initialized field

As dependency injection frameworks like Spring have become central in software applications in the last few years, the conventional view is now that the Singleton pattern is evil and should never be used. Instead, relationships between objects in your system should be managed by a dependency injection container. I have started disliking the consequence of this strategy very much: All coupling in my system becomes implicit and harder to understand. I have instead reverted to using design patterns like the Singleton pattern, but with a slight twist.

The Singleton-initialized field

Here is an example of a Singleton-initialized field:

public class PersonService {
 
private PersonDao personDao;
 
public PersonService(PersonDao personDao) {
this.personDao = personDao;
}
 
public PersonService() {
SessionFactory sessionFactory =
MyHibernateContext.getSessionFactoryInstance();
this.personDao = new HibernatePersonDao(sessionFactory);
}
 
public void someServiceMethod() {
// Do something with personDao
}
 
}

When I write a test for this class, the test code overrides the PersonDao by passing it to the non-default constructor.

@Test
public void shouldDoSomething() {
PersonService service = new PersonService(mockPersonDao);
service.someServiceMethod();
verify(mockPersonDao).findAllPeople();
}

Compared: The dependency injection way

The now-conventional way of doing the same thing is with dependency injection:

public class PersonService {
 
private PersonDao personDao;
 
@Inject
public PersonService(PersonDao personDao) {
this.personDao = personDao;
}
 
public void someServiceMethod() {
// Do something with personDao
}
}

And the test will set up the dependencies using a container:

@RunWith(SpringTestRunner.class)
@Configuration("src/test/resources/test-context.xml")
public class PersonServiceTest {
 
@Resource
private PersonService service;
 
@Resource
private PersonDao mockPersonDao;
 
@Test
public void shouldDoSomething() {
service.someServiceMethod();
verify(mockPersonDao).findAllPeople();
}
}

Somewhere in the realms of test-context.xml we will configure the fact that PersonDao should be provided by a mock:

public class MyDependencyConfigTest extends MyDependencyConfig {
@Bean
public PersonDao personService() {
return new MockPersonDao();
}
}

The problem: Declarative programming

With a dependency injection container, the components in my code are loosely coupled. Indeed, this is the main selling point of dependency injection. But sometimes, loosely coupled code is the problem.

First, understanding which dependency is actually used (and why!) can require a lot of brain power: It will require you to consider all possible configurations when you look at a dependency and it will not work well with the normal code navigation functionality of your IDE. Second, the configuration will tend to deteriorate: When you no longer use a dependency, will you check whether you can remove it, or will you just leave it there to be safe?

Lastly, it can be hard to spot errors in the configuration:

I once had a service that needed had a @Resource TransactionManager and a @Resource DataSource. However, the system had a pair of each. The test code was correct, but in the production code, I had by accident configured the wrong TransactionManager. The effect was that the Service didn’t run in a transaction for the correct data source. That is: It didn’t really run in a transaction. The problem with this is that you only discover the problem if you scrutinize the contents of the database after the transaction was supposed to be rolled back.

Dependency injection in specific and declarative programming in general mean More Magic. More Magic is seldom a good thing, at least not when there are simple, time-tested strategies that work. Even if said strategies have fallen out of fashion.

This dependency injection madness must end!