Simple IOC container implementation

Keywords: Java Spring github Gradle Junit


This article is written to learn the execution process of the Spring IOC container. It can't fully represent the Spring IOC container, but simply implements the dependency injection and control inversion functions of the container, which can't be used in production. It can only play a certain role in understanding the Spring container.


Create project

Create the Gradle project and modify build.gradle

plugins {
    id 'java'
    id "io.franzbecker.gradle-lombok" version "3.1.0"

group 'io.github.gcdd1993'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'

Create BeanFactory

BeanFactory is the core interface used to store bean instances and get beans in IOC. Its core methods are getBean and overload methods of getBean. Here, two methods of getBean are simply implemented.

package io.github.gcdd1993.ioc.bean;

 * bean factory interface
 * @author gaochen
 * @date 2019/6/2
public interface BeanFactory {

     * Get bean by bean name
     * @param name bean Name
     * @return bean
    Object getBean(String name);

     * Get bean by bean type
     * @param tClass bean type
     * @param <T>    Generic T
     * @return bean
    <T> T getBean(Class<T> tClass);


Create ApplicationContext context

ApplicationContext, as we often say, is actually the Spring container itself.

We create the ApplicationContext class and implement the BeanFactory interface.

public class ApplicationContext implements BeanFactory {

getBean method

Since it's a container, there must be a place to hold our bean instance. Use two maps as containers.

 * Group by beanName
private final Map<String, Object> beanByNameMap = new ConcurrentHashMap<>(256);

 * Group by bean class
private final Map<Class<?>, Object> beanByClassMap = new ConcurrentHashMap<>(256);

Then, we can finish our getBean method first.

public Object getBean(String name) {
    return beanByNameMap.get(name);

public <T> T getBean(Class<T> tClass) {
    return tClass.cast(beanByClassMap.get(tClass));

Is it easy to get bean instances directly from the Map? Of course, in a real Spring container, it won't be so simple, but this time we want to simplify the complexity and understand the IOC container.


Spring provides @ ComponentScan to scan the components under the package. For convenience, we directly specify the package to scan in the constructor.

private final Set<String> basePackages;
 * Default constructor, scan the current package by default
public ApplicationContext() {
    this(new HashSet<>(Collections.singletonList(ApplicationContext.class.getPackage().getName())));

 * All parameter constructor
 * @param basePackages List of scanned package names
public ApplicationContext(Set<String> basePackages) {
    this.basePackages = basePackages;

refresh method

The process of refresh basically follows the following process

  1. Scan all classes with @ Bean annotation (in Spring, @ Component annotation) under the specified package.
List<Class> beanClasses = PackageScanner.findClassesWithAnnotation(packageName, Bean.class);
System.out.println("scan classes with Bean annotation : " + beanClasses.toString());

for (Class beanClass : beanClasses) {
    try {
    } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
  1. Traverse the class, get the constructor of the class and all fields.
Constructor constructor = beanClass.getDeclaredConstructor();
Object object = constructor.newInstance();
Field[] fields = beanClass.getDeclaredFields();
  1. Determine whether the field is dependent on the injected or normal field.

  2. If it is a normal field, initialize the field through the field type and try to get the Value from the @ Value annotation and plug it into the field.

Value value = field.getAnnotation(Value.class);
if (value != null) {
    // injection
    // Some type conversion is needed, from String to corresponding type
    field.set(object, value.value());
  1. If it is a dependency injected field, try to get the corresponding instance from beanByClassMap. If not, first instantiate the corresponding type of the field.
Autowired autowired = field.getAnnotation(Autowired.class);
if (autowired != null) {
    // Dependency injection
    String name =;
    // Inject by name
    Object diObj;
    if (!name.isEmpty()) {
        diObj = beanByNameMap.get(name) == null ?
                createBean(name) :
    } else {
        // Inject by type
        Class<?> aClass = field.getType();
        diObj = beanByClassMap.get(aClass) == null ?
                createBean(aClass) :
    // injection
    field.set(object, diObj);

Test our IOC container

Create Address

public class Address {
    private String longitude;

    private String latitude;

Create Person and inject Address

public class Person {
    private Address address;

    private String name;

    private String age;

Create test class ApplicationContextTest

public class ApplicationContextTest {

    public void refresh() {
        Set<String> basePackages = new HashSet<>(1);
        ApplicationContext ctx = new ApplicationContext(basePackages);

        Person person = ctx.getBean(Person.class);

        Object person1 = ctx.getBean("Person");

The console will output:

scan classes with Bean annotation : [class io.github.gcdd1993.ioc.util.Address, class io.github.gcdd1993.ioc.util.Person]
scan classes with Bean annotation : [class io.github.gcdd1993.ioc.util.Address, class io.github.gcdd1993.ioc.util.Person]
Person(address=Address(longitude=2222, latitude=1111), name=gaochen, age=27)
Person(address=Address(longitude=2222, latitude=1111), name=gaochen, age=27)

As you can see, we successfully injected the Address instance into the Person instance and stored them in our own IOC container. In fact, the principle of Spring container is basically the same. In order to cope with enterprise level development, it provides many convenient functions, such as bean scope, bean custom methods and so on.

Get source code

The full source code can be obtained from my github repository Simple-IOC-Container

Posted by ExpendableDecoy on Mon, 10 Feb 2020 05:03:49 -0800