Dependency Injection with Spring

As mentioned in the previous article I’m going to write some words about dependency injection — with Spring. As stated before there exist a lot of good books and articles about DI and I will only tell my opinion of the usage of DI. And I’ll focus on DI with Spring. Eventually I’ll take a look at JSR-330.

What is Dependency Injection?

To keep it small and not so simple: read the article from Martin Fowler about IoC and DI. And as you can see, inversion of control is not dependency injection. Dependency injection is a software design pattern which implements inversion of control. In this pattern you get the service you want to use injected in the object where you need it — and the object has not have to create his dependency.

And this is a good thing especially if you want to maintain singleton objects and use the only one instance of your service around your application.

Inversion of Control

As I mentioned before, IoC is not DI but DI is an IoC implementation. Without a further discussion about IoC I’ll mention the types of IoC implementations which you already heard of:

  • Factory Pattern
  • Service Locator Pattern
  • Contextualized Look-up
  • Template Design Pattern
  • Strategy Design Pattern
  • Dependency Injection Pattern

Configuration possibilities

There are many methods how you can configure your application, developers have their pros and cons on each one. Let’s have a look.

Constructor Injection

With constructor injection you declare your dependencies in your java class as instance variables and you provide a constructor to fill in those variables with the right values (mostly singleton instances).

Example Java code:

 private final GenericRepository repository;
 private final PersistenceQueryFactory persistenceQueryFactory;
 public ApplicationService(GenericRepository genericRepository, PersistenceQueryFactory persistenceQueryFactory)
 {
     this.repository = genericRepository;
     this.persistenceQueryFactory = persistenceQueryFactory;
 }
 

Example Spring XML:

<bean name="applicationService" class="biz.hahamo.dev.variations.controller.ApplicationService">
    <constructor-arg ref="biz.hahamo.dev.variations.controller.repository.GenericRepository"/>
    <constructor-arg ref="biz.hahamo.dev.variations.controller.repository.PersistenceQueryFactory"/>
</bean>
Setter Injection

With setter injection you declare your variables (like with constructor injection) as instance variables and you provide setter methods which set those variables.

Example Java code:

private SessionFactory sessionFactory;

//... code omitted

public void setSessionFactory(SessionFactory sessionFactory)
{
    this.sessionFactory = sessionFactory;
}

Example Spring XML:

<bean id="biz.hahamo.dev.variations.controller.repository.GenericRepository"
    class="biz.hahamo.dev.variations.controller.repository.internal.hibernate.GenericHibernateRepository"
    depends-on="liquibase">
        <property name="sessionFactory" ref="sessionFactory" />
        <constructor-arg ref="biz.hahamo.dev.variations.controller.repository.internal.hibernate.HibernateQueryCreator" />
</bean>

And why you do not see the setter method in my actual code is: this dependency is needed in the HibernateDaoSupport from Spring — and the setter method is located there too.

Field Injection

Field injection is a kind of setter injection where you can magically remove the setters from your code. Because I do not prefer field injection I’ll convert the constructor example to field injection.

Example code:

public class ApplicationService
{
    @Autowired
    private GenericRepository repository;
    @Autowired
    private PersistenceQueryFactory persistenceQueryFactory;

    // public ApplicationService(GenericRepository genericRepository, PersistenceQueryFactory persistenceQueryFactory)
    // {
    //     this.repository = genericRepository;
    //     this.persistenceQueryFactory = persistenceQueryFactory;
    // }

    public void loadData()
    {
    Driver d = repository.find(Driver.class, 1L);
       System.out.println(d);
       repository.findByQuery(persistenceQueryFactory.createJpqlQuery("all.drivers", "FROM Driver"));
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- Enable Annotations, e.g. @Autowired -->
    <context:annotation-config />

    <import resource="classpath:biz/hahamo/dev/variations/controller/repository/repository.spring.xml"/>

    <bean name="applicationService" class="biz.hahamo.dev.variations.controller.ApplicationService">
        <!-- <constructor-arg ref="biz.hahamo.dev.variations.controller.repository.GenericRepository"/> -->
        <!-- <constructor-arg ref="biz.hahamo.dev.variations.controller.repository.PersistenceQueryFactory"/> -->
    </bean>
</beans>

You see, you have to enable annotation support from the Spring Context library. I commented out the lines of code which are not needed to see the changes. And via the Reflection API Spring can wire private fields too.

And of course you can use the @Autowired annotation on constructors or setter methods.

My recommendation on configuration

I recommend constructor injection for all needed dependencies. Optional dependencies could be injected with setters. And if you want to ensure constructor injection you could make your instance variables final — so you have to initialize them in a constructor.

Some developers say that field injection is evil and I agree with them: you should expose your dependencies because if you need one “service” then you should not rely on developers to look at your code to find out which dependencies you need.

With constructor injection, if someone uses your “service” as a third party solution, (s)he will get a fat exception from Spring if not all parameters are set.

This is why I recommend using setter injection for optional dependencies — however because the dependencies are optional you should be prepared to handle if your dependencies are not set. Otherwise your code will send NullPointerExceptions through the brain of your application.

JSR-330

Naturally Spring implements the JSR-330 too which enables the easy change to another DI container.

What is in JSR-330? It defines dependency injection annotations and enables them to use across various DI container vendors who implemented this service request.

This enables us to write interchangeable code but has some limits too — you cannot fully use the Spring features (or them from other frameworks as Guice or PicoContainer).

I do not want to explain all the possibilities and how to use them, I just mention the annotations you can use to have a use of JSR 330: @Inject, @Named, @Qualifier. If you use them you can establish the same functionality you could with Spring annotations @Autowire, @Service or @Component, @Qualifier (yes, Spring has it’s own qualifier too).

Write your own

You could naturally write your own dependency injection container if you only need a quick and very lightweight DI service. So one is Afterburner developed by Adam Bien.

I won’t write my own DI mechanism, I’m satisfied with Spring and I’ll continue to use the whole Spring ecosystem — and so the DI container.

Next steps

Dont’t know. I guess I should continue to implement the application (who looks at the repository at GitHub already noticed the Hibernate connection and the generic repository) and explain the doings there.

So I’ll take a look at entities and managing their loading and saving with Hibernate. Stay tuned.

And I admit that this article could be confusing. I’m writing this since some weeks because of a busy schedule at work. If you find some problems or just want to tell something: use the comment box. I’ll eventually revisit this article and add updates to correct erroneous parts.

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s