Close

Spring - Required dependency checking

[Last Updated: Dec 22, 2023]

For a large project where many developers work in parallel, it's desirable to have some framework level facilities to check that all mandatory dependencies have been set or not. This check should ideally be performed at compile time, if not possible then as early as start up time, before we start having NullPointerException on missing values. Spring offers various dependency checking mechanism during start up time.

Let's explore some aspects and options provided by Spring framework regarding dependency checking.


Constructor vs setter injection

As we discussed in the tutorial, different ways to do dependency injection, we should always use constructors based injection for the mandatory properties (with programmatic validation of arguments) and setter based injection for optional properties.

There might still be many reasons we want to use setters for mandatory properties rather than constructor. May be our constructor is getting too complex, may be we want to reconfigure some properties later (of course they cannot be final in that case but still are mandatory at wiring time).

Dependency injection via constructors

Spring throws UnsatisfiedDependencyException if the required dependency is missing for the target constructor

package com.logicbig.example;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@Configuration
@ComponentScan(useDefaultFilters = false,
        includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "ConstructorDiExample"))
public class ConstructorDiExample {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(ConstructorDiExample.class);
        ClientBean bean = context.getBean(ClientBean.class);
        bean.doSomething();
    }

    @Component
    private static class ClientBean {
        private final ServiceBean serviceBean;

        private ClientBean(ServiceBean serviceBean) {
            this.serviceBean = serviceBean;
        }

        void doSomething() {
            System.out.println("doing something with: " + serviceBean);
        }
    }

    //uncomment @service to get rid of UnsatisfiedDependencyException
   // @Service
    private class ServiceBean {

    }
}

Output

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.logicbig.example.ConstructorDiExample$ServiceBean' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}


How to check required properties in setter injection?

If we are doing setter injection for required dependencies then we have following options for dependency checking:


Setter injection with @Autowire

For setter injection, we are not required to use @Autowire. But for dependency checking we need to explicitly use this annotation. The annotation @Autowire#required attribute is true by default. So Spring throws UnsatisfiedDependencyException if the dependency via setter is not provided.

package com.logicbig.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@Configuration
@ComponentScan(useDefaultFilters = false,
        includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "SetterAutowiredExample"))
public class SetterAutowiredExample {

    public static void main(String... strings) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
                SetterAutowiredExample.class);
        ClientBean bean = context.getBean(ClientBean.class);
        bean.doSomething();
    }

    @Component
    public static class ClientBean {
        private ServiceBean serviceBean;

        @Autowired
        public void setServiceBean(ServiceBean serviceBean) {
            this.serviceBean = serviceBean;
        }

        public void doSomething() {
            System.out.println("doing something with : " + serviceBean);
        }
    }

    //uncomment following to  get rid of UnsatisfiedDependencyException
    //@Service
    public static class ServiceBean {
    }
}

Output

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.logicbig.example.SetterAutowiredExample$ClientBean': Unsatisfied dependency expressed through method 'setServiceBean' parameter 0: No qualifying bean of type 'com.logicbig.example.SetterAutowiredExample$ServiceBean' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

Setter injection without @Autowire but with @PostConstruct validation

Performing validation manually in PostConstruct method:

package com.logicbig.example;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import jakarta.annotation.PostConstruct;

@Configuration
public class PostConstructExample {

    @Bean
    public ClientBean clientBean (ServiceBean serviceBean) {
        ClientBean clientBean = new ClientBean();
        //uncomment following to get rid of IllegalArgumentException
        //clientBean.setServiceBean(serviceBean);
        return clientBean;
    }

    @Bean
    public ServiceBean serviceBean () {
        return new ServiceBean();
    }

    public static void main (String... strings) {
        AnnotationConfigApplicationContext context =
                            new AnnotationConfigApplicationContext(
                                                PostConstructExample.class);
        ClientBean bean = context.getBean(ClientBean.class);
        bean.doSomething();
    }

    public static class ClientBean {
        private ServiceBean serviceBean;

        @PostConstruct
        public void myPostConstructMethod () throws Exception {
            // we can do more validation than just checking null values here
            if (serviceBean == null) {
                throw new IllegalArgumentException("ServiceBean not set");
            }
        }

        public void setServiceBean (ServiceBean serviceBean) {
            this.serviceBean = serviceBean;
        }

        public void doSomething () {
            System.out.println("doing something with: " + serviceBean);
        }
    }

    public static class ServiceBean {
    }
}

Output

Caused by: java.lang.IllegalArgumentException: ServiceBean not set

Using InitializingBean interface

Definition of InitializingBean

(Version: spring-framework 6.1.2)
package org.springframework.beans.factory;
   ........
public interface InitializingBean {
    void afterPropertiesSet() throws Exception; 1
}
1Invoked by the Spring container after it has set all bean properties. This method allows the bean instance to perform validation of its overall configuration and final initialization when all bean properties have been set.

Example:

package com.logicbig.example;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class InitializingBeanExample {

    @Bean
    public ClientBean clientBean() {
        return new ClientBean();
    }

    //remove following comment to fix java.lang.IllegalArgumentException: ServiceBean not set
    //@Bean
    public ServiceBean serviceBean() {
        return new ServiceBean();
    }

    public static void main(String... strings) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(
                        InitializingBeanExample.class);
        ClientBean bean = context.getBean(ClientBean.class);
        bean.doSomething();
    }

    private static class ClientBean implements InitializingBean {
        private ServiceBean serviceBean;

        public void setServiceBean(ServiceBean serviceBean) {
            this.serviceBean = serviceBean;
        }

        public void doSomething() {
            System.out.println("doing something with: " + serviceBean);
        }

        @Override
        public void afterPropertiesSet() throws Exception {
            if (serviceBean == null) {
                throw new IllegalArgumentException("ServiceBean not set");
            }
        }
    }

    private static class ServiceBean {
    }
}

Output

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'clientBean' defined in com.logicbig.example.InitializingBeanExample: ServiceBean not set
Caused by: java.lang.IllegalArgumentException: ServiceBean not set

Example Project

Dependencies and Technologies Used:

  • spring-context 6.1.2 (Spring Context)
     Version Compatibility: 4.3.0.RELEASE - 6.1.2Version List
    ×

    Version compatibilities of spring-context with this example:

    • 4.3.0.RELEASE
    • 4.3.1.RELEASE
    • 4.3.2.RELEASE
    • 4.3.3.RELEASE
    • 4.3.4.RELEASE
    • 4.3.5.RELEASE
    • 4.3.6.RELEASE
    • 4.3.7.RELEASE
    • 4.3.8.RELEASE
    • 4.3.9.RELEASE
    • 4.3.10.RELEASE
    • 4.3.11.RELEASE
    • 4.3.12.RELEASE
    • 4.3.13.RELEASE
    • 4.3.14.RELEASE
    • 4.3.15.RELEASE
    • 4.3.16.RELEASE
    • 4.3.17.RELEASE
    • 4.3.18.RELEASE
    • 4.3.19.RELEASE
    • 4.3.20.RELEASE
    • 4.3.21.RELEASE
    • 4.3.22.RELEASE
    • 4.3.23.RELEASE
    • 4.3.24.RELEASE
    • 4.3.25.RELEASE
    • 4.3.26.RELEASE
    • 4.3.27.RELEASE
    • 4.3.28.RELEASE
    • 4.3.29.RELEASE
    • 4.3.30.RELEASE
    • 5.0.0.RELEASE
    • 5.0.1.RELEASE
    • 5.0.2.RELEASE
    • 5.0.3.RELEASE
    • 5.0.4.RELEASE
    • 5.0.5.RELEASE
    • 5.0.6.RELEASE
    • 5.0.7.RELEASE
    • 5.0.8.RELEASE
    • 5.0.9.RELEASE
    • 5.0.10.RELEASE
    • 5.0.11.RELEASE
    • 5.0.12.RELEASE
    • 5.0.13.RELEASE
    • 5.0.14.RELEASE
    • 5.0.15.RELEASE
    • 5.0.16.RELEASE
    • 5.0.17.RELEASE
    • 5.0.18.RELEASE
    • 5.0.19.RELEASE
    • 5.0.20.RELEASE
    • 5.1.0.RELEASE
    • 5.1.1.RELEASE
    • 5.1.2.RELEASE
    • 5.1.3.RELEASE
    • 5.1.4.RELEASE
    • 5.1.5.RELEASE
    • 5.1.6.RELEASE
    • 5.1.7.RELEASE
    • 5.1.8.RELEASE
    • 5.1.9.RELEASE
    • 5.1.10.RELEASE
    • 5.1.11.RELEASE
    • 5.1.12.RELEASE
    • 5.1.13.RELEASE
    • 5.1.14.RELEASE
    • 5.1.15.RELEASE
    • 5.1.16.RELEASE
    • 5.1.17.RELEASE
    • 5.1.18.RELEASE
    • 5.1.19.RELEASE
    • 5.1.20.RELEASE
    • 5.2.0.RELEASE
    • 5.2.1.RELEASE
    • 5.2.2.RELEASE
    • 5.2.3.RELEASE
    • 5.2.4.RELEASE
    • 5.2.5.RELEASE
    • 5.2.6.RELEASE
    • 5.2.7.RELEASE
    • 5.2.8.RELEASE
    • 5.2.9.RELEASE
    • 5.2.10.RELEASE
    • 5.2.11.RELEASE
    • 5.2.12.RELEASE
    • 5.2.13.RELEASE
    • 5.2.14.RELEASE
    • 5.2.15.RELEASE
    • 5.2.16.RELEASE
    • 5.2.17.RELEASE
    • 5.2.18.RELEASE
    • 5.2.19.RELEASE
    • 5.2.20.RELEASE
    • 5.2.21.RELEASE
    • 5.2.22.RELEASE
    • 5.2.23.RELEASE
    • 5.2.24.RELEASE
    • 5.2.25.RELEASE
    • 5.3.0
    • 5.3.1
    • 5.3.2
    • 5.3.3
    • 5.3.4
    • 5.3.5
    • 5.3.6
    • 5.3.7
    • 5.3.8
    • 5.3.9
    • 5.3.10
    • 5.3.11
    • 5.3.12
    • 5.3.13
    • 5.3.14
    • 5.3.15
    • 5.3.16
    • 5.3.17
    • 5.3.18
    • 5.3.19
    • 5.3.20
    • 5.3.21
    • 5.3.22
    • 5.3.23
    • 5.3.24
    • 5.3.25
    • 5.3.26
    • 5.3.27
    • 5.3.28
    • 5.3.29
    • 5.3.30
    • 5.3.31
    • Compatible Java Version: 17+
    • 6.0.0
    • 6.0.1
    • 6.0.2
    • 6.0.3
    • 6.0.4
    • 6.0.5
    • 6.0.6
    • 6.0.7
    • 6.0.8
    • 6.0.9
    • 6.0.10
    • 6.0.11
    • 6.0.12
    • 6.0.13
    • 6.0.14
    • 6.0.15
    • 6.1.0
    • 6.1.1
    • 6.1.2

    Versions in green have been tested.

  • jakarta.jakartaee-api 10.0.0 (Eclipse Foundation)
  • JDK 17
  • Maven 3.8.1

Spring - Required dependency checking Select All Download
  • spring-dependency-checking
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • PostConstructExample.java

    See Also