Spring 从容器中获取Bean

1. 案例:通过ID从容器中获取Bean

1.1. 添加依赖

根据依赖传递,Spring核心容器只需要添加spring-context即可。在Spring5之前,核心容器还依赖commons-logging日志包,Spring5现在将commons-logging的内容集成到spring-jcl这个包下了。根据依赖传递,spring-jcl这个依赖也已经添加了,所以最后也只用添加spring-context这个依赖

1
2
3
4
5
6
<!-- Spring 核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>

1.2. 编写Bean类

1
2
3
4
5
6
7
8
9
package bean;

import lombok.Data;

@Data
public class Person {
private String name;
private Integer age;
}

1.3. 创建Spring Bean配置文件

在classpath下创建Bean配置文件。普通Java工程就放到src目录下,Maven工程就放到src/main/resources目录下

Eclipse如果安装了STS插件,可以直接New->Spring Bean Configuration File

1
2
3
4
5
6
<?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">

</beans>

1.4. 在Bean配置文件中注册Bean

1
2
3
4
5
6
7
8
9
<!-- 
<bean>: 注册一个bean,Spring会自动创建该对象,放入容器中
id: bean的标识
class: 全类名
-->
<bean id="person" class="bean.Person">
<property name="age" value="10" />
<property name="name" value="foo" />
</bean>

1.5. 代码测试从容器中取出Bean

1
2
3
4
5
6
7
8
@Test
public void testIoc() throws Exception {
// 创建容器,指定Bean配置文件的的路径
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
// 在创建容器时,Bean就被创建了,此时可以直接根据id取出bean
Person person = (Person) ioc.getBean("person");
System.out.println(person);
}

2. 案例:通过类对象从容器中获取Bean

除了ID,还可以通过类对象从容器中获取Bean。这种方式可以发现一个好处,即返回值不用类型转换

1
2
3
4
5
6
7
@Test
public void testIoc() throws Exception {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过类对象获取Bean
Person person = ioc.getBean(Person.class);
System.out.println(person);
}

3. 案例:同时指定ID和类对象,从容器中获取Bean

1
2
3
4
5
6
7
8
<bean id="person1" class="bean.Person">
<property name="age" value="10" />
<property name="name" value="foo" />
</bean>
<bean id="person2" class="bean.Person">
<property name="age" value="20" />
<property name="name" value="bar" />
</bean>
1
2
3
4
5
6
7
8
9
@Test
public void testIoc() throws Exception {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
// 第1个参数指定ID,第2个参数指定类型,返回值不必类型转换
Person person1 = ioc.getBean("person1", Person.class);
Person person2 = ioc.getBean("person2", Person.class);
System.out.println(person1);
System.out.println(person2);
}

4. 案例总结

4.1. 谁创建了Bean?

我没有主动创建Person对象,是谁创建的呢?是Spring IOC容器

4.2. Bean是什么时候被创建的?是如何被创建的?

在IOC容器被创建时,即执行new ClassPathXmlApplicationContext时,所有在配置文件中注册过的Bean都会被创建

这里要注意,Spring有两种工厂。BeanFactory是在获取bean时才创建bean,而ApplicationContext容器在创建时就会创建所有配置的Bean

Bean又是如何被创建的呢?我们可以在Person中编写空参构造方法,输出一些内容

1
2
3
4
5
6
7
8
@Data
public class Person {
public Person() {
System.out.println("Person被创建了");
}
private String name;
private Integer age;
}

再写一个测试方法。根据运行结果可知,IOC容器在创建时就创建了Bean,而且是通过反射,调用Bean的空参构造方法,创建一个实例

1
2
3
4
@Test
public void testIoc() throws Exception {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
}

4.3. 同一个Bean组件在IOC容器中是单实例的?

两次使用同一ID,从容器中取出Bean组件,得到的两个对象是相同的,说明Bean组件在IOC容器是单实例的

1
2
3
4
5
6
7
@Test
public void testIoc() throws Exception {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person1 = (Person) ioc.getBean("person");
Person person2 = (Person) ioc.getBean("person");
System.out.println(person1 == person2);
}

4.4. Bean的属性是如何被设置的?

在配置文件中,我们是通过property标签为Bean设置属性的。但这只是配置,我们并不知道IOC容器在程序中具体是如何为Bean设置属性的

1
2
3
4
<bean id="person" class="bean.Person">
<property name="age" value="10" />
<property name="name" value="foo" />
</bean>

给Person添加一个setter,输出一些内容

1
2
3
4
5
6
7
8
9
@Data
public class Person {
private String name;
private Integer age;
public void setName(String name) {
System.out.println("setName");
this.name = name;
}
}

运行测试方法,setter被调用了,可知IOC容器是通过反射,调用Bean的setter,将属性注入

1
2
3
4
@Test
public void testIoc() throws Exception {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
}

4.5. 如果容器中没有你要的组件,会出现什么现象?

先猜一下,如果没有你要的组件,可能会返回null

编写测试方法,取一个没有注册过的组件

1
2
3
4
5
@Test
public void testIoc() throws Exception {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
Object bean = ioc.getBean("abc");
}

运行。结果并不是返回null,而是直接抛出异常

1
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'abc' available

4.6. 如果定义了两个同类型的Bean组件,通过类对象获取Bean会出现什么现象

注册两个同类型的Bean,但是id不同

1
2
3
4
5
6
7
8
<bean id="person1" class="bean.Person">
<property name="age" value="10" />
<property name="name" value="foo" />
</bean>
<bean id="person2" class="bean.Person">
<property name="age" value="20" />
<property name="name" value="bar" />
</bean>

编写测试方法,通过类对象获取Bean

1
2
3
4
5
6
7
@Test
public void testIoc() throws Exception {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过类对象获取Bean
Person person = ioc.getBean(Person.class);
System.out.println(person);
}

运行,抛出异常。即通过类对象获取Bean时,要求容器中该类型的Bean组件只能有1个,如果有多个,就会报错。

1
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'bean.Person' available: expected single matching bean but found 2: person1,person2

在这种情况下,只能通过ID获取

1
2
3
4
5
6
7
8
@Test
public void testIoc() throws Exception {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person1 = (Person) ioc.getBean("person1");
Person person2 = (Person) ioc.getBean("person2");
System.out.println(person1);
System.out.println(person2);
}

panchaoxin wechat
关注我的公众号
支持一下