In this article we will discuss making use of Java source code instead of configuring our container using XML. For a reminder, there are three ways of configuring a Spring container:
Using XML configuration, here we list all our beans in the XML file,
Using XML component scan, here we make use of Java annotation, which simply scans and looks for classes with @Component class,
Using Java Configuration class, which we will discuss in this article.
To configure our application using Java Code, we must follow some processes, which are:
Create a Java class and annotate as @Configuration
Add component scanning support using @ComponentScan
Read the Spring Java configuration class
Retrieve bean from Spring container
To begin, let’s create a JAVA EE project using eclipse where we can code out concepts.
After creating your project go ahead and create a new class named SchoolConfig.java which we are going to use for the configuration of our project.
Now to follow our first step, Create a Java class and annotate as @Configuration, go ahead and annotate the class with @Confuguration annotation. By doing this we tell Spring that this is a Java configuration.
1
2
3
4
package com.nikhil.anno_demo;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SchoolConfig {}
Now to follow the second step, add @ComponentScan annotation and simply provide the actual package that we want to use for Spring to start scanning, and that works exactly like XML component scanning. Spring will scan this package, find all the classes that have the @Compoent annotation, and then register them in the Spring container. Here com.nikhil.anno_demo is our package name.
1
2
3
@Configuration
@ComponentScan("com.nikhil.anno_demo")
public class SchoolConfig {}
We know that two main concepts, inversion of control and dependency injection, can also be configured using annotations. You can find much more about this on the Topcoder Platform, so to save time we will cut short the process and make some interfaces and classes using dependency injection and inversion of control with Java Annotations. Below you can see those classes:
Teacher.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
package com.nikhil.anno_demo;
public interface Teacher {
public String getDailyHomework();
public String getDailyQuote();
}
QuoteService.java
package com.nikhil.anno_demo;
public interface QuoteService {
public String getQuote();
}
MotivationalQuoteService.java
package com.nikhil.anno_demo;
import org.springframework.stereotype.Component;
@Component
public class MotivationalQuoteService implements QuoteService {
@Override
public String getQuote() {
return "kl kare se aj kr aj kre so ab";
}
}
ChemistryTeacher.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
package com.nikhil.anno_demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ChemistryTeacher implements Teacher {
@Autowired
private QuoteService quoteService;
public ChemistryTeacher() {
System.out.println(">> Inside the default contructor");
}
@Override
public String getDailyHomework() {
return "Revise thermodyamics";
}
@Override
public String getDailyQuote() {
return quoteService.getQuote();
}
}
For the next steps, go ahead and create a demo class with the main method, in our case it is JavaConfigApp.java. All the steps are the same as they are while using an XML configuration except the fact that now we have to provide the configuration class instead of the XML file.
JavaConfigApp.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.nikhil.anno_demo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class JavaConfigApp {
public static void main(String[] args) {
// read spring config file
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SchoolConfig.class);
// get the bean from the spring container
Teacher chem = context.getBean("chemistryTeacher", Teacher.class);
// call method
System.out.println(chem.getDailyHomework());
// call method to get the daily quote
System.out.println(chem.getDailyQuote());
// close context
context.close();
}
}
Console:
Everything works here. The only difference is that instead of reading the configuration from an XML file we are actually reading it from a Java class.
We can even define beans using Java code and the configuration class we made earlier without the need for XML. For this, we will create a new implementation of the Teacher interface named DrawingTeacher.java and we will also have a QuoteService implementation that we will inject using Java code.
Steps to define beans using Java code:
Exposing beans using methods
Inject bean dependencies
Read Spring Java configuration class
Retrieve bean from Spring container
Go ahead and create our DrawingTeacher class implementing Teacher, create a private variable for the dependency injection QuoteService, and define the constructor for dependency injection.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.nikhil.anno_demo;
public class DrawingTeacher implements Teacher {
private QuoteService quoteService;
private DrawingTeacher(QuoteService quoteService) {
this.quoteService = quoteService;
}
@Override
public String getDailyHomework() {
return "Try skeching Mona Lisa";
}
@Override
public String getDailyQuote() {
return quoteService.getQuote();
}
}
Here we have to define two beans, one for the implementation class and the second for the dependency that we will provide to the implementation class using the constructor. For this, we use @Bean annotation for both methods.
1
2
3
4
@Bean
public QuoteService motivationalQuoteService() {
return new MotivationalQuoteService();
}
Both the method names are the bean_id that Spring will use to assign it to the container. In the other bean, we are actually calling the above bean, and Spring will intercept and give us a reference to the object accordingly based on the bean scope.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.nikhil.anno_demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.nikhil.anno_demo")
public class SchoolConfig {
@Bean
public QuoteService motivationalQuoteService() {
return new MotivationalQuoteService();
}
@Bean
public Teacher drawingTeacher() {
return new DrawingTeacher(motivationalQuoteService());
}
}
Now for the other two steps, go over to the demo class and just change the bean id that we were retrieving earlier to “drawingTeacher”.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.nikhil.anno_demo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class JavaConfigApp {
public static void main(String[] args) {
// read spring config file
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SchoolConfig.class);
// get the bean from the spring container
Teacher draw = context.getBean("drawingTeacher", Teacher.class);
// call method
System.out.println(draw.getDailyHomework());
// call method to get the daily quote
System.out.println(draw.getDailyQuote());
// close context
context.close();
}
}
Console:
And it works perfectly. Now you can go ahead and disable the component scanning in the config class and you will notice everything remains intact.
Rather than hard-coding values, we can provide values to our fields using a property file here that contains the name and email address of our drawing teacher. We are now going to inject these values into our drawing teacher and make use of them.
To get started, create a properties file named study.properties with the following key-value pairs.
1
2
de.email = nksingh @gmail.com
de.name = nikhil singh
To make use of this file, load it into the config class using the @PropertySource like below, providing the classpath of our properties file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.nikhil.anno_demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:study.properties")
public class SchoolConfig {
@Bean
public QuoteService motivationalQuoteService() {
return new MotivationalQuoteService();
}
......
}
Now we can use the reference values from the properties file, open the drawing teacher class, and define two new private variables, email and name. Use @value annotation to inject the actual value from the properties file using field injection, and in the process, also define the getter methods to see how our concept spans.
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
40
41
42
43
44
45
package com.nikhil.anno_demo;
import org.springframework.beans.factory.annotation.Value;
public class DrawingTeacher implements Teacher {
private QuoteService quoteService;
@Value("{de.email}")
private String email;
@Value("{de.name}")
private String name;
public DrawingTeacher(QuoteService quoteService) {
this.quoteService = quoteService;
}
@Override
public String getDailyHomework() {
return "Try skeching Mona Lisa";
}
@Override
public String getDailyQuote() {
return quoteService.getQuote();
}
public String getEmail() {
return email;
}
public String getName() {
return name;
}
}
Now go ahead and call the getter on the demo class.
// get the bean from the spring container
DrawingTeacher draw = context.getBean("drawingTeacher", DrawingTeacher.class);
// call method
System.out.println(draw.getName());
System.out.println(draw.getEmail());
Console:
Voila! Everything works fine!!