Skip to content

Spring Bean Configuration With Java Code#

Development Process With Java Code#

  • In Spring Inversion Of Control Java Code we knew how to configure the Spring Container without using the XML configuration file and we also use the annotations to make the Spring Framework scan and inject the Spring Beans automatically. In this section, we will use the Java Code for configuring the Spring Bean manually without the powerful support of annotations. Then we will have a closer look at Spring Framework configuration.

  • To configure the Spring Beans with Java Code, we should follow these steps:

    • Define the dependency interface and class
    • Define method to expose bean.
    • Inject bean dependencies.
    • Read Spring Java configuration class.
    • Retrieve bean from Spring Container.

Example For Spring Bean Configuration#

Dependencies#

  • Like we took an example with Spring Inversion Of Control, we will need to add the dependency spring-context for creating the Spring Container and configuring Constructor Injection Type.
pom.xml
1
2
3
4
5
6
7
8
9
....  

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

....

Define the dependency interface and class#

  • So now, let's create 2 interfaces Coach and ExanminationService and they will be implemented by 4 java classes EnglishCoach, HistoryCoach, EnglishExaminationService and HistoryExaminationService correspondingly. In which.

    • The class EnglishCoach will need to use EnglishExaminationService as a dependency for it's method.
    • The class HistoryCoach will need to use HistoryExaminationService as a dependency for it's method.
  • Let's see the diagram below for more details.

 #zoom

  • So, firstly let's create ExaminationService interface and EnglishExaminationService and HistoryExaminationService implementation classes as below.
ExaminationService.java
1
2
3
4
5
6
7
package com.spring.core.spring.dependency.injection.java.source.code;  

public interface ExaminationService {  

    public String getExamination();  

}
EnglishExaminationService.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package com.spring.core.spring.dependency.injection.java.source.code;  


public class EnglishExaminationService implements ExaminationService {  

    @Override  
    public String getExamination() {  
        return "Focus and take English examination in 3 hours";  
    }  
}
HistoryExaminationService.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package com.spring.core.spring.dependency.injection.java.source.code;

public class HistoryExaminationService implements ExaminationService {

    @Override
    public String getExamination() {
        return "Focus and take History examination in 3 hours";
    }

}
  • Then let's create the Coach interface and EnglishCoach and HistoryCoach implementation class as below.
Coach.java
1
2
3
4
5
6
7
8
9
package com.spring.core.spring.dependency.injection.java.source.code;  

public interface Coach {  

    public String getDailyHomeWork();  

    public String getExamination();  

}
EnglishCoach.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.spring.core.spring.dependency.injection.java.source.code;  

public class EnglishCoach implements Coach {  

    @Override  
    public String getDailyHomeWork() {  
        return "Spend 1 hour to practise Speaking Skill!";  
    }  

    @Override  
    public String getExamination() {  
        return null;  
    }  

}
HistoryCoach.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.spring.core.spring.dependency.injection.java.source.code;

public class HistoryCoach implements Coach {

    @Override
    public String getDailyHomeWork() {
        return "Spend 20 minutes to read history books";
    }

    @Override
    public String getExamination() {
        return null;
    }

}

Create constructor or setter for injections#

  • Now, in the EnglishCoach, let's create a constructor for injecting the ExaminationService and use it for getExamination() method as below.
EnglishCoach.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.spring.core.spring.dependency.injection.java.source.code;  

public class EnglishCoach implements Coach {  

    private ExaminationService examinationService;  

    public EnglishCoach(ExaminationService examinationService) {  
        this.examinationService = examinationService;  
    }  

    @Override  
    public String getDailyHomeWork() {  
        return "Spend 1 hour to practise Speaking Skill!";  
    }  

    @Override  
    public String getExamination() {  
        return this.examinationService.getExamination();  
    }  

}
  • Then in the HistoryCoach, let's create a setter method for injecting the ExaminationService and use it for getExamination() method as below.
HistoryCoach.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.spring.core.spring.dependency.injection.java.source.code;

public class HistoryCoach implements Coach {

    private ExaminationService examinationService;

    @Override
    public String getDailyHomeWork() {
        return "Spend 20 minutes to read history books";
    }

    @Override
    public String getExamination() {
        return this.examinationService.getExamination();
    }

    public void setExaminationService(ExaminationService examinationService) {
        this.examinationService = examinationService;
    }
}

Define method to expose bean#

  • Now, let's create the ApplicationConfig java class and put the configuration for exposing the bean as below.
ApplicationConfig.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.spring.core.spring.dependency.injection.java.source.code.config;

import com.spring.core.spring.dependency.injection.java.source.code.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ApplicationConfig {

    @Bean
    public ExaminationService englishExaminationService() {
        return new EnglishExaminationService();
    }

    @Bean
    public ExaminationService historyExaminationService() {
        return new HistoryExaminationService();
    }

}
  • As you can see, For every type of bean we have to create the corresponding method and return the new instance of it. Then we will add the annotation @Bean on these methods to make returned instances become the beans and by default the method name will be the bean id.
  • We define these two beans englishExaminationService and historyExaminationService first because they don't contain any dependency there.

Inject bean dependencies#

  • Then for injecting the dependencies between beans, we will reference methods that we exposed beans into the constructor or setter while creating the new instances for beans with dependencies as below.
ApplicationConfig.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.spring.core.spring.dependency.injection.java.source.code.config;

import com.spring.core.spring.dependency.injection.java.source.code.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ApplicationConfig {

    @Bean
    public ExaminationService englishExaminationService() {
        return new EnglishExaminationService();
    }

    @Bean
    public ExaminationService historyExaminationService() {
        return new HistoryExaminationService();
    }

    @Bean
    public EnglishCoach englishCoach() {
        return new EnglishCoach(englishExaminationService());
    }

    @Bean
    public HistoryCoach historyCoach() {
       HistoryCoach historyCoach = new HistoryCoach();
       historyCoach.setExaminationService(historyExaminationService());
       return historyCoach;
    }

}
  • As you can see, the EnglishCoach instance will need to inject the bean englishExaminationService from englishExaminationService() method by constructor while the HistoryCoach instance will need to inject the historyExaminationService from historyExaminationService() method by setter method.

Read Spring Java configuration class#

  • Now, let's use AnnotationConfigApplicationContext for creating the applicationContext which is known as Spring Container as below:
SpringApplication.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.spring.core.spring.dependency.injection.java.source.code;

import com.spring.core.spring.dependency.injection.java.source.code.config.ApplicationConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringApplication {
    public static void main(String[] args) {

        //Create a spring container
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);

    }
}
  • In which the AnnotationConfigApplicationContext will be created with ApplicationConfig that we created above.

Retrieve bean from Spring Container#

  • Now, we can use the bean in the Spring Container by using getBean() method.
SpringApplication.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.spring.core.spring.dependency.injection.java.source.code;

import com.spring.core.spring.dependency.injection.java.source.code.config.ApplicationConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringApplication {
    public static void main(String[] args) {

        //Create a spring container
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);

        //Retrieve bean from the spring container
        Coach englishCoach = context.getBean("englishCoach", Coach.class);
        Coach historyCoach = context.getBean("historyCoach", Coach.class);

        //Use bean
        System.out.println(englishCoach.getDailyHomeWork());
        System.out.println(englishCoach.getExamination());
        System.out.println(historyCoach.getDailyHomeWork());
        System.out.println(historyCoach.getExamination());

        //Close context
        context.close();

    }
}

Testing#

  • Finally, we just simply run the main class then we can see the content in the spring bean as below.
1
2
3
4
5
6
Spend 1 hour to practise Speaking Skill!
Focus and take English examination in 3 hours
Spend 20 minutes to read history books
Focus and take History examination in 3 hours

Process finished with exit code 0

How @Bean Works Behind The Scences?#

For Defining methods to expose beans#

  • As in the example above you may wonder how does the @Bean annotation works behind? Let's deep dive into it with following steps below.
  • For the code creating the bean in the ApplicationConfig.java as below.
ApplicationConfig.java
1
2
3
4
5
6
7
8
....

    @Bean
    public ExaminationService englishExaminationService() {
        return new EnglishExaminationService();
    }

....
  • At a high-level, Spring creates a bean component manually. By default the scope is singleton. So any request for an englishExaminationService bean, will get the same instance of the bean since singleton is the default scope.
  • The @Bean annotation tells Spring that we are creating a bean component manually. We didn't specify a scope so the default scope is singleton.
  • The method public ExaminationService englishExaminationService() specifies that the bean will have the bean id englishExaminationService. The method name determines the bean id. The return type is the Coach interface. This is useful for dependency injection. This can help Spring find any dependencies that implement the Coach interface.
  • The @Bean annotation will intercept any requests for englishExaminationService bean. Since we didn't specify a scope, the bean scope is singleton. As a result, it will give the same instance of the bean for any requests.
  • The code new EnglishExaminationService(); will create a new instance of the EnglishExaminationService.
  • The code return new EnglishExaminationService(); returns an instance of the englishExaminationService.

  • Now let's step back and look at the method in it's entirety.

ApplicationConfig.java
1
2
3
4
5
6
7
8
....

    @Bean
    public ExaminationService englishExaminationService() {
        return new EnglishExaminationService();
    }

....
  • It is important to note that this method has the @Bean annotation. The annotation will intercept ALL calls to the method englishExaminationService(). Since no scope is specified the @Bean annotation uses singleton scope. Behind the scenes, during the @Bean interception, it will check in memory of the Spring container (applicationContext) and see if this given bean has already been created.

  • If this is the first time the bean has been created then it will execute the method as normal. It will also register the bean in the application context. So that is knows that the bean has already been created before. Effectively setting a flag.

  • The next time this method is called, the @Bean annotation will check in memory of the Spring container (applicationContext) and see if this given bean has already been created. Since the bean has already been created (previous paragraph) then it will immediately return the instance from memory. It will not execute the code inside of the method. Hence this is a singleton bean.

  • The code inside the method return new EnglishExaminationService(); is not executed for subsequent requests to the method public ExaminationService englishExaminationService() . This code is only executed once during the initial bean creation since it is singleton scope.

For Dependency Injection#

  • In the example that we made before, we are creating an EnglishCoach and injecting the englishExaminationService() using the same information presented earlier.
ApplicationConfig.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
....

@Bean  
public ExaminationService englishExaminationService() {  
    return new EnglishExaminationService();  
}

@Bean  
public EnglishCoach englishCoach() {  
    return new EnglishCoach(englishExaminationService());  
}

....
  • In the code below, we define a bean for the englishExaminationService. Since the bean scope is not specified, it defaults to singleton.
ApplicationConfig.java
1
2
3
4
5
6
7
8
....

    @Bean
    public ExaminationService englishExaminationService() {
        return new EnglishExaminationService();
    }

....
  • Any calls for englishExaminationService, the @Bean annotation intercepts the call and checks to see if an instance has been created. First time through, no instance is created so the code executes as desired. For subsequent calls, the singleton has been created so @Bean will immediately return with the singleton instance.

  • Now for the code return new EnglishCoach(englishExaminationService());. It creates an instance of EnglishCoach. Note the call to the method englishExaminationService(). We are calling the annotated method above. The @Bean will intercept and return a singleton instance of englishExaminationService. The englishExaminationService is then injected into the EnglishCoach instance by using EnglishCoach constructor.

  • For the setter injection, the behavior will be almost the same, just different in using setter method instead of using constructor.

ApplicationConfig.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
....

@Bean  
public ExaminationService historyExaminationService() {  
    return new HistoryExaminationService();  
}

@Bean  
public HistoryCoach historyCoach() {  
   HistoryCoach historyCoach = new HistoryCoach();  
   historyCoach.setExaminationService(historyExaminationService());  
   return historyCoach;  
}

....
  • This is effectively dependency injection. It is accomplished using all Java configuration (no xml).

Injecting Values From Properties File#

  • So in the Spring Dependency Injection XML and Spring Dependency Injection Annotation we knew how to inject values from the properties files by using xml and annotation and although using anotation but we still maintain the xml Spring Config file. So in this section we will inject values from properties files by using Java-Base configuration file.
  • To injecting values from the properties files, we should follow these steps:
    • Create Properties File.
    • Load Properties file in Spring Config.
    • Reference values from Properties File.

Create Properties File#

  • In this step we just simply create a properties file in the resources folder of our project and put the name-value pair as in the example below. The name is before "=" and the value is after "=".
team-info.properties
1
2
coach.team.english.email=englishCoach@example.com  
coach.team.history.email=historyCoach@example.com

Load Properties File In Spring Config#

  • In this step we will load the properties file that we created above into our Spring Config. We will create a java-base configuration file and annotation it with @Configuration annotation. Then to load the properties file we will use @PropertySource annotation with input is the properties file name that we created in the resources folder.
TeamInfoConfig.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package com.spring.core.spring.dependency.injection.java.source.code.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource("classpath:team-info.properties")
public class TeamInfoConfig {

}

Reference values from Properties File#

  • Finally, like we did before with Inject Values From Properties File By Annotation we just need to references values from the properties file using the annotation @Value and the actual name of the property that we defined in the properties file. The name of the property should be place inside the syntax "${<nameOfProperty>}", Ex: "${coach.team.history.email}"
HistoryCoach.class
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class HistoryCoach implements Coach {  

    private ExaminationService examinationService;  

    @Value("${coach.team.history.email}")  
    private String teamEmail;

        .......

}

Example Injecting Values From Properties File With Java Code#

  • We will continue with the example that we did in the section Example For Spring Bean Configuration above for injection values from properties file.

 #zoom

Create Properties File#

  • So, firstly let's create a properties file with any name (In this example we will use team-info.properties) in the resources folder and put some properties for it as below.
team-info.properties
1
2
coach.team.english.email=englishCoach@example.com  
coach.team.history.email=historyCoach@example.com

Load Properties File In Spring Config#

  • Now, let's create a Java-Base configuration file name TeamInfoConfig.java by using the annotation @Configuration. Then we will use the @PropertySource annotation with input is the properties file that we created in the step above.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package com.spring.core.spring.dependency.injection.java.source.code.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource("classpath:team-info.properties")
public class TeamInfoConfig {

}

Reference values from Properties File#

  • Now, we will reference values from the properties file into EnglishCoach and HistoryCoach beans using annotation injection as below.
EnglishCoach.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.spring.core.spring.dependency.injection.annotation;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class EnglishCoach implements Coach {

    private final ExaminationService examinationService;

    @Value("${coach.team.english.email}")
    private String teamEmail;

    @Autowired
    public EnglishCoach(@Qualifier("englishExaminationService") ExaminationService examinationService) {
        this.examinationService = examinationService;
    }

    @Override
    public String getDailyHomeWork() {
        return "Spend 1 hour to practise Speaking Skill!";
    }

    @Override
    public String getExamination() {
        return this.examinationService.getExamination();
    }

    public String getTeamEmail() {
        return teamEmail;
    }

    public void setTeamEmail(String teamEmail) {
        this.teamEmail = teamEmail;
    }
}
HistoryCoach.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.spring.core.spring.dependency.injection.annotation;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class HistoryCoach implements Coach {

    private ExaminationService examinationService;

    @Value("${coach.team.history.email}")
    private String teamEmail;

    @Override
    public String getDailyHomeWork() {
        return "Spend 20 minutes to read history books";
    }

    @Override
    public String getExamination() {
        return this.examinationService.getExamination();
    }

    @Autowired
    public void setExaminationService(@Qualifier("historyExaminationService") ExaminationService examinationService) {
        this.examinationService = examinationService;
    }

    public String getTeamEmail() {
        return teamEmail;
    }

    public void setTeamEmail(String teamEmail) {
        this.teamEmail = teamEmail;
    }

}
  • As you can see, we will create a field name teamEmail with the getter and setter methods in the EnglishCoach and HistoryCoach, then we will use the @Value annotation with the actual name of the property that we defined in the properties file to inject the value there.

Testing#

  • Finally, To check injecting values from the properties file we will get beans englishCoach and historyCoach and get the teamEmail value as below.
SpringApplication.class
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.spring.core.spring.dependency.injection.annotation;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringApplication {

    public static void main(String[] args) {

        //create Spring Container with configuration file
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //Get bean from container
        Coach englishCoach = context.getBean("englishCoach", Coach.class);
        Coach historyCoach = context.getBean("historyCoach", Coach.class);
        Coach scienceCoach = context.getBean("scienceCoach", Coach.class);

        EnglishCoach englishCoachDetail = context.getBean("englishCoach", EnglishCoach.class);
        HistoryCoach historyCoachDetail = context.getBean("historyCoach", HistoryCoach.class);

        //use beans
        System.out.println(englishCoach.getDailyHomeWork());
        System.out.println(englishCoach.getExamination());
        System.out.println(historyCoach.getDailyHomeWork());
        System.out.println(historyCoach.getExamination());
        System.out.println(scienceCoach.getDailyHomeWork());
        System.out.println(scienceCoach.getExamination());

        System.out.println(englishCoachDetail.getTeamEmail());
        System.out.println(historyCoachDetail.getTeamEmail());

        //close container
        context.close();
    }

}
  • Then let's start our application then we can see the result as below, values from the properties file injected successfully and printed out.
Spend 1 hour to practise Speaking Skill!
Focus and take English examination in 3 hours
Spend 20 minutes to read history books
Focus and take History examination in 3 hours
Spend 40 minutes to read science books
Focus and take Science examination in 3 hours
englishCoach@example.com
historyCoach@example.com

Process finished with exit code 0

See Also#

References#