Unravelling the magic of Spring Dependency Injection

Pradipta Nag
3 min readApr 13, 2024

Dependency Injection is the main pillar on which the entire Spring Framework operates. Instead of developers creating the instances of classes and injecting them, it is the Spring Framework that takes the responsibility— which is why it is also known as Inversion of Control. But how it does under the hood, many developers may not be aware. But every developer needs to know at least on a high level how this magic works. This article is based on the YouTube video from the Sivalabs channel — https://www.youtube.com/watch?v=G35N2GfulFU. I have made a few changes to make the concepts even simpler.

Nowadays, we work with Spring Boot which does all the heavy lifting for us. So, for the sake of better understanding, let’s keep Spring Boot out of the discussion.

Create a simple Maven project and add the following dependency -

   <dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.5</version>
</dependency>
</dependencies>

Create two classes as follows -

import org.springframework.stereotype.Component;

@Component
public class IndependentClass {

public void executeMethod() {
System.out.println("Executing method");
}
}
package org.example.bean;

import org.springframework.stereotype.Component;

@Component
public class DependentClass {

private final IndependentClass independentClass;

public DependentClass(IndependentClass independentClass) {
this.independentClass = independentClass;
}

public void executeMethod() {
independentClass.executeMethod();
}
}

The dependency of IndependentClass has been injected into the Dependency class using a Constructor-based dependency injection. Unlike Setter-based dependency injection, Constructor-based dependency injection makes it easier to write tests and makes it ready for use as soon as dependencies are set.

Spring Application Context can be created and the application can be run in the following way -

//Spring way
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext("org.example.bean");
DependentClass dependentClass = annotationConfigApplicationContext.getBean(DependentClass.class);
dependentClass.executeMethod();

This code runs fine and gives the expected result. But if we deep dive into this, most probably Spring manages the dependency injection internally in the following steps -

Identify the Spring Beans to be instantiated in the given package

The following code scans all the classes in the given package and identifies the classes with @Component annotation.

ClassPathScanningCandidateComponentProvider scanner
= new ClassPathScanningCandidateComponentProvider(true);

scanner.addIncludeFilter(new AnnotationTypeFilter(Component.class));
Set<BeanDefinition> beanDefinitions
= scanner.findCandidateComponents("org.example.bean");

for(BeanDefinition beanDefinition : beanDefinitions) {
System.out.println(beanDefinition.getBeanClassName());
}

In the above code, the “org.example.bean” package gets scanned and all the classes with @Component annotation get identified.

Identify the Spring Bean dependencies using Java reflection

static void printConstructors(Class<?> type) {
Constructor<?>[] constructors = type.getConstructors();
Parameter[] parameters = constructors[0].getParameters();
for(Parameter parameter : parameters) {
System.out.println(parameter.getType());
}
}

In the above method, the constructors of all the identified classes in the above step, get checked. For the sake of simplicity, it is assumed that each class is having a single constructor in this case. To identify if dependencies need to be injected into a class, the parameters of the constructors also get checked. If there are no parameters, then no dependency to be injected. If there are parameters, then injection needs to be done accordingly. Based on this, the order of the instance creation to be done in the next step also gets decided.

Create instances of the beans using Java reflection

The instances of the beans get created and injected as per the identification in the previous step following the correct order.

static void createInstance() throws Exception {
IndependentClass independentClass =
(IndependentClass) Class.forName(IndependentClass.class.getName()).getDeclaredConstructors()[0].newInstance();

DependentClass dependentClass =
(DependentClass) Class.forName(DependentClass.class.getName()).getDeclaredConstructors()[0].newInstance(independentClass);

dependentClass.executeMethod();
}

These are the 3 main steps used by Spring Framework to manage the entire Dependency injection process. But to implement, the dependency injection pattern in your application, it is not mandatory to use Spring Framework especially if the application is small. You can create your custom Bean Factory similar to Spring in the following way -

public class CustomBeanFactory {
private Map<Class<?>, Object> beans = initBeans();

private Map<Class<?>, Object> initBeans() {
Map<Class<?>, Object> beanMap = new HashMap<>();
IndependentClass independentClass = new IndependentClass();
DependentClass dependentClass = new DependentClass(independentClass);
beanMap.put(IndependentClass.class, independentClass);
beanMap.put(DependentClass.class, dependentClass);
return beanMap;
}

public <T> T getBean(Class<?> clazz) {
return (T) beans.get(clazz);
}
}

In the initBeans() method, a Hashmap is created where all the instances of the application classes are kept mapped to their class types. The getBean() method is similar to the Spring Context’s getBean() method to get the required instance querying the BeanFactory passing the class type for which you need the instance in the following way -

CustomBeanFactory customBeanFactory = new CustomBeanFactory();
DependentClass dependentClassCustom = customBeanFactory.getBean(DependentClass.class);
dependentClassCustom.executeMethod();

The entire source code can be found in the following link -

https://github.com/pradz13/spring-boot-in-depth.git

--

--

Pradipta Nag

Software Engineer by profession, Singer and Blogger by passion