DEV Community

Asher Toqeer
Asher Toqeer

Posted on • Originally published at thebackendguy.com

Java Spring Framework Dependency Injection Without Spring Boot

Today, Spring Framework is used as an umbrella term for Spring projects and Spring ecosystem, However, Spring Framework is basically a Dependency Injection Framework and we can use its Dependency Injection capabilities in any Java project, without using Spring Boot or any other Spring project.

Example Java Project

Let's say we have a Java project with an EmployeeService that has a dependency on DepartmentService and an Application class to run the application:

package com.thebackendguy.com.service;

public class DepartmentService {

    public String getDepartmentName(String employeeId) {
        System.out.println(this + ": getDepartmentName");
        return "Accounts";
    }
}
Enter fullscreen mode Exit fullscreen mode
package com.thebackendguy.com.service;

public class EmployeeService {

    private final DepartmentService departmentService;

    public EmployeeService(DepartmentService departmentService) {
        this.departmentService = departmentService;
    }

    public String checkDepartment() {
        System.out.println(this + ": checkDepartment");
        return departmentService.getDepartmentName("EMP-0098");
    }
}
Enter fullscreen mode Exit fullscreen mode
package com.thebackendguy.com;

import com.thebackendguy.com.service.DepartmentService;
import com.thebackendguy.com.service.EmployeeService;

public class Application {

    public static void main(String[] args) {
        DepartmentService departmentService = new DepartmentService();
        EmployeeService employeeService = new EmployeeService(departmentService);
        System.out.println(employeeService.checkDepartment());
    }
}
Enter fullscreen mode Exit fullscreen mode

Here we are manually creating and injecting dependencies, we want Spring framework to do this, let's add Spring Framework to the project

Add Spring Framework Dependency

We only need spring-context dependency to be added in the project. It is will provide all dependency injection capabilities Spring Framework has to offer. At the time of writing this article, the latest stable version is 5.2.12.RELEASE.

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Creating and Managing Dependencies (Beans)

Spring managed dependencies are called Beans. Since EmployeeService depends on DepartmentService we want Spring to manage that dependency. We need to have a configuration class where we can define dependencies and let Spring know about them.

Spring Beans Configuration class:

Create a new ApplicationConfig class that will hold dependency (Bean) configurations:

package com.thebackendguy.com.config;

import com.thebackendguy.com.service.DepartmentService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // 1
public class ApplicationConfig {

    @Bean // 2
    public DepartmentService departmentService() {
        return new DepartmentService();
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. @Configuration is a Spring annotation to mark a class as Bean configuration class, it will help Spring framework to find Beans.
  2. @Bean is a Spring annotation to let Spring know about dependency. This annotation is used on a method that Spring will call to obtain that dependency. Here a new DepartmentService instance is returned for Spring to manage. That instance is now called a Bean.
Application Context:

Spring Framework use ApplicationContext to manage the beans. ApplicationContext represents a container, also called Spring Inversion of Control (IoC) container, that contains all of Spring managed instances or beans.

We need to create an Application Context for our application to access beans. Let's update our main Application class.

package com.thebackendguy.com;

import com.thebackendguy.com.config.ApplicationConfig;
import com.thebackendguy.com.service.DepartmentService;
import com.thebackendguy.com.service.EmployeeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Application {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class); // 1
        DepartmentService departmentService = context.getBean(DepartmentService.class); // 2
        System.out.println(departmentService); // 3

        EmployeeService employeeService = new EmployeeService(departmentService);
        System.out.println(employeeService.checkDepartment());
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Create a new ApplicationContext using our ApplicationConfig class (this class contains our DepartmentService bean). We need to specify configuration class(s) while creating context so that Spring can find our beans. We can use XML based configuration too instead of Annotation based configurations, but for simplicity we will stick with Annotation based configurations.

  2. We are getting DepartmentService bean from application context (or IoC container), instead of creating it here.

  3. We are simply printing departmentService object.

We are passing departmentService to EmployeeService same as before.

Auto Injecting Dependencies (Autowiring Beans)

So far we have created DepartmentService Bean and an ApplicationContext to manage that bean. We can access that Bean using context and pass it to another service, but it would be much cleaner if the Bean "gets injected" automatically, rather than we get it from the application context and inject manually.

This auto injection of dependencies (or Beans) is called Autowiring and Spring provides it out of the box, given that both of the instances (the dependent and the dependency) are Spring Beans. In other words, DepartmentService gets injected in EmployeeService automatically if both of them are Beans. It won't work otherwise. So, let's make EmployeeService a bean too.

Update ApplicationConfig class to add EmployeeService bean:

package com.thebackendguy.com.config;

import com.thebackendguy.com.service.DepartmentService;
import com.thebackendguy.com.service.EmployeeService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ApplicationConfig {

    @Bean
    public DepartmentService departmentService() {
        return new DepartmentService();
    }

    @Bean
    public EmployeeService employeeService() {
        return new EmployeeService(departmentService()); // 1
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. EmployeeService needs an instance of DepartmentService at the time of creation, so we are calling departmentService() to fulfill that dependency, but this will not create a new DepartmentService instance rather it will automatically Inject the previously existing DepartmentService bean. Both of the methods are actually Spring Bean configuration and default Bean scope is singleton (single shared instance per context) and Spring is smart enough to realize and inject existing beans where ever required. (For brevity of this article I am not going into detail of bean scope)

Let's update our Application class to get EmployeeService instance rather than creating it.

public class Application {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);

        EmployeeService employeeService = context.getBean(EmployeeService.class); // 1
        System.out.println(employeeService.checkDepartment()); 
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Now we don't need to manually create an instance of EmployeeService and inject DepartmentService dependency. It is now getting handled by the Spring framework.

These are the minimal steps required to use Spring Framework without Spring boot. Spring Framework Dependency Injection has a lot to offer in addition to these basic Dependency Injection capabilities.

Find code on GitHub: https://github.com/thebackendguy-code-examples/java-spring-demo-without-spring-boot

Top comments (1)

Collapse
 
elmuerte profile image
Michiel Hendriks

I've been using Spring Framework for so long, years before "boot" was even a concept, that this article feels really weird :)