22 April 2019
Spring(2)----Bean Management
by Jerry Zhang
#Tip of the Day: IntellijIDEA -> Ctrl + Alt + Shift + s -> Modules -> click Mark as to change property/type of a folder/file.
In my last Blog, we concluded that Spring manages the objects/beans for us by a container.
The question is if we did not new
the Object, then how can we tell Spring what is our beans?
And how to tell Spring that we want to instantiate an object?
There are two ways to make this configuration we mentioned above. One is using xml configuration file, the other is using annotation. Annotation way is more popular and much easier, but I will talk about both ways because learning xml configuration can give us more understanding about what exactly happened behind annotation.
How did we create an object without Spring? At least three ways:
- Use a constructor, then
new Bean1()
- Use a static factory method,
Bean2Factory.getBean2()
- Use a instance factory method,
Bean3Factory bean3Factory = new Bean3Factory();
, thenBean3 bean3 = bean3Factory.getBean3();
In Spring world, it’s very similar to the above.
My project structure:
Maven dependencies:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
First Create an xml file in the resources folder:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="springioc.class005.Bean1" id="bean1"/>
<bean class="springioc.class005.Bean2Factory" factory-method="getBean2" id="bean2"/>
<bean class="springioc.class005.Bean3Factory" id="bean3Factory"/>
<bean class="springioc.class005.Bean3" factory-bean="bean3Factory" factory-method="getBean3" id="bean3"/>
</beans>
Secondly, create a constructor for each bean, and create factory methods:
public class Bean1 {
public Bean1() {
System.out.println("Bean1.Bean1");
}
}
public class Bean2Factory {
public static Bean2 getBean2(){
return new Bean2();
}
}
Finally, we can test our code:
public class Class005Test {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Bean1 bean1 = context.getBean("bean1", Bean1.class);
System.out.println("bean1 = " + bean1);
Bean2 bean2 = context.getBean("bean2", Bean2.class);
System.out.println("bean2 = " + bean2);
Bean3 bean3 = context.getBean("bean3", Bean3.class);
System.out.println("bean3 = " + bean3);
}
}
Use Annotation to create a bean
Next, I will explain how to use annotations to get the same result.
- Create a configuration class, add @ComponentScan annotation
@Configuration
@ComponentScan(value = "springioc.class013")
public class MyConfiguration {
}
- Add @Component annotation in the bean class
@Component
public class Bean1 {
}
@Component is just an generic annotation. Usually, we use @Controller, @Service, @Repository
- If you want to test:
public class Class013Test {
@Test
public void test(){
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
Bean1 bean1 = context.getBean("bean1", Bean1.class);
System.out.println("bean1 = " + bean1);
}
}
Next, we can start Bean/Dependency Injection using annotation
We create three classes, MyBean, AnotherBean, MyConfiguration.
Inject an Object, anotherBean
AnotherBean is an instance variable of Mybean. anotherBean1 is injected by constructor. anotherBean2 is injected by setter. anotherBean3 is injected directly.
Inject a List
stringList is injected by setter method in MyBean and a stringList method in
MyConfiguration class. We can assign an Id for the bean. If there was no id, Spring gives higher priority
to the corresponding type bean, String
in this example, and inject all the String beans into the stringList.
We can also control the order of the injected Sting in the List with @Order annotation.
Inject a Map
Similarly, if we need to inject a Map, first, register a Map bean in the configuration class. Then, put a
@Autowired annotation at the injected instance variable or its setter method.
Another way is creating some methods in the configuration class, and put @Bean at these methods. The returning
type must be the same as the value
type of the map. The key
of this Map must be String, because the
default id of the bean is the method name, which always is a String.
Inject primitive type
@Value at the instance variable
MyBean:
@Component
public class MyBean {
private AnotherBean anotherBean1;
private AnotherBean anotherBean2;
@Autowired
private AnotherBean anotherBean3;
private List<String> stringList;
private Map<String, Integer> integerMap;
private String string;
private ApplicationContext context;
public ApplicationContext getContext() {
return context;
}
@Autowired
public void setContext(ApplicationContext context) {
this.context = context;
}
public String getString() {
return string;
}
@Value("555")
public void setString(String string) {
this.string = string;
}
public Map<String, Integer> getIntegerMap() {
return integerMap;
}
@Autowired
public void setIntegerMap(Map<String, Integer> integerMap) {
this.integerMap = integerMap;
}
@Autowired
public void setAnotherBean2(AnotherBean anotherBean2) {
this.anotherBean2 = anotherBean2;
}
@Autowired
public MyBean(AnotherBean anotherBean1) {
this.anotherBean1 = anotherBean1;
}
public List<String> getStringList() {
return stringList;
}
@Autowired
@Qualifier("stringList")
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
@Override
public String toString() {
return "MyBean{" +
"anotherBean1=" + anotherBean1 +
", anotherBean2=" + anotherBean2 +
", anotherBean3=" + anotherBean3 +
", stringList=" + stringList +
", integerMap=" + integerMap +
", string='" + string + '\'' +
", context=" + context +
'}';
}
}
AnotherBean
@Component
public class AnotherBean {
}
MyConfiguration
@Configuration
@ComponentScan("springioc.class014")
public class MyConfiguration {
@Bean("stringList")
public List<String> stringList(){
ArrayList<String> list = new ArrayList<>();
list.add("111");
list.add("222");
return list;
}
@Bean
public Map<String, Integer> integerMap(){
Map<String, Integer> map = new HashMap<>();
map.put("aaa", 666);
map.put("bbb", 777);
return map;
}
@Bean
@Order(56)
public String string1(){
return "333";
}
@Bean
@Order(28)
public String string2(){
return "444";
}
@Bean
public Integer integer1(){
return 333;
}
@Bean
public Integer integer2(){
return 444;
}
}
Test:
public class Class014Test {
@Test
public void test(){
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyBean myBean = context.getBean("myBean", MyBean.class);
System.out.println("myBean = " + myBean);
for (String s : myBean.getStringList()) {
System.out.println("s = " + s);
}
for (Map.Entry<String, Integer> entry : myBean.getIntegerMap().entrySet()) {
System.out.println("entry = " + entry);
}
AnotherBean anotherBean = myBean.getContext().getBean("anotherBean", AnotherBean.class);
System.out.println("anotherBean = " + anotherBean);
}
}