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:
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.
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.
So, firstly let's create ExaminationService interface and EnglishExaminationService and HistoryExaminationService implementation classes as below.
packagecom.spring.core.spring.dependency.injection.java.source.code;publicclassEnglishExaminationServiceimplementsExaminationService{@OverridepublicStringgetExamination(){return"Focus and take English examination in 3 hours";}}
HistoryExaminationService.java
1 2 3 4 5 6 7 8 910
packagecom.spring.core.spring.dependency.injection.java.source.code;publicclassHistoryExaminationServiceimplementsExaminationService{@OverridepublicStringgetExamination(){return"Focus and take History examination in 3 hours";}}
Then let's create the Coach interface and EnglishCoach and HistoryCoach implementation class as below.
packagecom.spring.core.spring.dependency.injection.java.source.code;publicclassEnglishCoachimplementsCoach{@OverridepublicStringgetDailyHomeWork(){return"Spend 1 hour to practise Speaking Skill!";}@OverridepublicStringgetExamination(){returnnull;}}
HistoryCoach.java
1 2 3 4 5 6 7 8 9101112131415
packagecom.spring.core.spring.dependency.injection.java.source.code;publicclassHistoryCoachimplementsCoach{@OverridepublicStringgetDailyHomeWork(){return"Spend 20 minutes to read history books";}@OverridepublicStringgetExamination(){returnnull;}}
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 9101112131415161718192021
packagecom.spring.core.spring.dependency.injection.java.source.code;publicclassEnglishCoachimplementsCoach{privateExaminationServiceexaminationService;publicEnglishCoach(ExaminationServiceexaminationService){this.examinationService=examinationService;}@OverridepublicStringgetDailyHomeWork(){return"Spend 1 hour to practise Speaking Skill!";}@OverridepublicStringgetExamination(){returnthis.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 91011121314151617181920
packagecom.spring.core.spring.dependency.injection.java.source.code;publicclassHistoryCoachimplementsCoach{privateExaminationServiceexaminationService;@OverridepublicStringgetDailyHomeWork(){return"Spend 20 minutes to read history books";}@OverridepublicStringgetExamination(){returnthis.examinationService.getExamination();}publicvoidsetExaminationService(ExaminationServiceexaminationService){this.examinationService=examinationService;}}
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.
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.
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.
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 910111213
packagecom.spring.core.spring.dependency.injection.java.source.code;importcom.spring.core.spring.dependency.injection.java.source.code.config.ApplicationConfig;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassSpringApplication{publicstaticvoidmain(String[]args){//Create a spring containerAnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(ApplicationConfig.class);}}
In which the AnnotationConfigApplicationContext will be created with ApplicationConfig that we created above.
packagecom.spring.core.spring.dependency.injection.java.source.code;importcom.spring.core.spring.dependency.injection.java.source.code.config.ApplicationConfig;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassSpringApplication{publicstaticvoidmain(String[]args){//Create a spring containerAnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(ApplicationConfig.class);//Retrieve bean from the spring containerCoachenglishCoach=context.getBean("englishCoach",Coach.class);CoachhistoryCoach=context.getBean("historyCoach",Coach.class);//Use beanSystem.out.println(englishCoach.getDailyHomeWork());System.out.println(englishCoach.getExamination());System.out.println(historyCoach.getDailyHomeWork());System.out.println(historyCoach.getExamination());//Close contextcontext.close();}}
Finally, we just simply run the main class then we can see the content in the spring bean as below.
123456
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
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.
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.
In the example that we made before, we are creating an EnglishCoach and injecting the englishExaminationService() using the same information presented earlier.
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.
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:
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 "=".
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.
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}"
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.
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.
packagecom.spring.core.spring.dependency.injection.annotation;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.beans.factory.annotation.Qualifier;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Component;@ComponentpublicclassHistoryCoachimplementsCoach{privateExaminationServiceexaminationService;@Value("${coach.team.history.email}")privateStringteamEmail;@OverridepublicStringgetDailyHomeWork(){return"Spend 20 minutes to read history books";}@OverridepublicStringgetExamination(){returnthis.examinationService.getExamination();}@AutowiredpublicvoidsetExaminationService(@Qualifier("historyExaminationService")ExaminationServiceexaminationService){this.examinationService=examinationService;}publicStringgetTeamEmail(){returnteamEmail;}publicvoidsetTeamEmail(StringteamEmail){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.
packagecom.spring.core.spring.dependency.injection.annotation;importorg.springframework.context.support.ClassPathXmlApplicationContext;publicclassSpringApplication{publicstaticvoidmain(String[]args){//create Spring Container with configuration fileClassPathXmlApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");//Get bean from containerCoachenglishCoach=context.getBean("englishCoach",Coach.class);CoachhistoryCoach=context.getBean("historyCoach",Coach.class);CoachscienceCoach=context.getBean("scienceCoach",Coach.class);EnglishCoachenglishCoachDetail=context.getBean("englishCoach",EnglishCoach.class);HistoryCoachhistoryCoachDetail=context.getBean("historyCoach",HistoryCoach.class);//use beansSystem.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 containercontext.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