Best Practices for Dependency Injection with Spring

Best Practices for Dependency Injection with Spring

8 Comments

In this post, I’m going to show you how to use Project Lombok for best practices in dependency injection with the Spring Framework.

The Spring Framework itself has a variety of different ways we can perform dependency injection. The flexibility of options is a strength of the Spring Framework. However, not all of the dependency injection options are considered best practices. Some, are actually very poor.

Dependency Injection Examples

I’ve setup examples for us to review the different dependency injection options we have to work with.

Let’s use an example Spring Service. For our purposes, the service has one method that returns a string.  We’ll take our ‘service’ and use Spring to inject it into some faux controllers. Keep in mind, we’re just exploring how we can do dependency injection with the Spring Framework.

Example Service

@Service
public class MyService {

    public String getHello(){
        return "Hello";
    }
}

Our field controller has one public property for the service. We can annotate this field, and Spring will inject an instance of the service.

Property Injection

Field Controller

@Controller
public class FieldController {

    @Autowired
    MyService myService;

    public String saySomething(){
        return myService.getHello();
    }
}

This is just a public property, and does not have a setter. Clearly, this is not a good practice. Nor is it recommended.

We can improve on this slightly, and make the access to the field private. The Spring Framework does allow you to autowire private fields. You do see people doing this. And Spring will perform some reflection magic to perform dependency injection.

Private Field Controller

@Controller
public class PrivateFieldController {
    @Autowired
    private MyService myService;

    public String saySomething(){
        return myService.getHello();
    }
}

While better than just using a private field, testing becomes a headache. You either need to bring up the Spring Context, or use some Spring utilities to perform dependency injection for testing. Not the end of the world, but generally annoying.

We can improve upon this by providing a setter for the private property. Getters and setters are generally considered best practices in object oriented programming. Its trivial to instruct Spring to use the setter for dependency injection by annotating the setter method.

Method Injection

Setter Controller

@Controller
public class SetterController {
    private MyService myService;

    @Autowired
    public void setMyService(MyService myService) {
        this.myService = myService;
    }

    public String saySomething(){
        return myService.getHello();
    }
}

This is a clear improvement upon using a private field. Some will complain this is too much code to write. But reality, tasks like this have been automated in modern IDEs since season one of South Park.

Constructor Injection

The next option is to use a constructor. This is the best method we have looked at so far. When using a constructor to set injected properties, you do not have to provide the autowire annotation. This is a nice feature, which saves us a bit of typing. Annotation of constructors for dependency injection has been optional since Spring Framework version 4.2.

Constructor Controller

@Controller
public class ConstructorController {
    private MyService myService;

    public ConstructorController(MyService myService) {
        this.myService = myService;
    }

    public String saySomething(){
        return myService.getHello();
    }
}

Constructor based dependency injection is certainly considered a best practice. There was a time I personally favored setter based injection, but have come around to constructor based.

We can still improve our example. There are two main concerns right now. One, the type of our service is a concrete type. Dependency injection of a hard type is not considered a best practice.

The second problem is, the property we are injecting is not declared final. Thus, in theory, the class could modify the injected property after it was instantiated.

Dependency Injection Best Practices

Best practices for dependency injection is to utilize interfaces, constructors, and final properties.

I’ve setup a ‘best practice’ service interface, and provided a service implementation – which is annotated with the Spring Service annotation.

Best Practice Service Interface

public interface BpService {
    String getHello();
}

Best Practice Service Implementation

@Service
public class BpServiceImpl implements BpService {
    @Override
    public String getHello() {
        return "The Best Hello!";
    }
}

Using Project Lombok

Now, the secret sauce using Project Lombok for best practices in dependency injection is to:

  • declare a final property of the interface type
  • annotate the class using Project Lombok’s required args constructor

Now, Project Lombok will generate a constructor for all properties declared final. And Spring will automatically use the Lombok provided constructor to autowire the clase.

Lombok Controller

@RequiredArgsConstructor
@Controller
public class BpFinalConstructorController {
    private final BpService bpService;

    public String saySomething(){
        return bpService.getHello();
    }
}

This is a real nice way of doing this. Your code stays very clean. When working with Spring, it’s not uncommon to need several autowired properties.

When you need to add another bean, simply declare a final property.

If you refactor, and no longer need a Spring managed dependency, just delete the final property.

You’re no longer maintaining setters or constructor code. Project Lombok alleviates this mundane task from you.

I’ve been using this technique for sometime now in my day to day coding. It’s definitely a timesaver. And leads to cleaner code. Gone are unused properties and unused constructor parameters. Refactoring is just a little less painful now!

Source code for this post is available here on GitHub.

 

About jt

    You May Also Like

    8 comments on “Best Practices for Dependency Injection with Spring

    1. December 18, 2019 at 9:21 am

      The best approach. At least at my point of view. I’m used to using it this way and have to agree with John’s argues. So clean, so testable, so maintainable, fun ;).
      Nice article btw, thank you John

      Reply
    2. December 18, 2019 at 2:07 pm

      Thank you for the article jt.
      You wrote: “There was a time I personally favored setter based injection, but have come around to constructor based.”
      I still and I think that the majority still favor the setter based injection.
      But you didn’t tell us why the constructor based is better, especially if does not use lombock.

      Reply
      • December 20, 2019 at 8:18 am

        the constructor is better than setters because u save a lot of typing. imagine having 5 dependencies. would u prefer 5 setters or 1 constructor with 5 params?!
        it is peobably easier to test too

        Reply
        • December 30, 2019 at 11:41 am

          I think the main reason the constructor injection is best is that if, for some reason, the bean to be injected doesn’t exist, you will know about it at the time the object that requires the dependency is created. In the above example, if the service doesn’t exist, the controller will not be instantiated and you’ll know that there is a problem close to where it occurred. With setter and field injection, you’ll find out that something is wrong long after the injection had failed. Plus, unlike with field injection when Spring context needs to be up, when you are using constructor (and setter) injections, you can test without Spring running. Just instantiate your service bean in the test harness and then pass it either to a constructor or a setter of the controller.

          Reply
    3. January 16, 2020 at 3:38 pm

      “While better than just using a private field, testing becomes a headache. You either need to bring up the Spring Context, or use some Spring utilities to perform dependency injection for testing. Not the end of the world, but generally annoying.”
      What about private fileld testing problems in the Lombok solution? Lombok supports your test environment too?
      Thank You, Gabor

      Reply
    4. July 21, 2020 at 12:53 am

      Thanks for the post, very useful and clean when using Lombok

      Best regards,
      Thiago Peçanha

      Reply
    5. October 22, 2020 at 3:02 am

      Nicer article for Spring Boot learner
      After completion of Spring Boot, people can check this YouTube playlist where I posted Developing Billing System using Spring Boot. People who want real-time project experience, please follow this channel

      Reply
    6. January 3, 2022 at 5:46 pm

      Hi,
      Is construction injection only recommended because it helps save code, or is there a particular motivation behind it?
      Does Spring Container work better with this kind of injection?
      Thank you.

      Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.