第三章:Spring IoC 容器概览

image-20220302124843613

1. Spring IoC 依赖查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="user" class="com.cruise.thinking.in.spring.ioc.container.overview.domain.User">
<property name="id" value="1"></property>
<property name="name" value="Cruise"></property>
</bean>

<!-- 使用 ObjectFactory 的实现 TargetBeanObjectFactory 实现延迟加载 -->
<!-- TargetBeanObjectFactory 是 ObjectFactoryCreatingFactoryBean的 内部类-->
<bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<!-- 配置关联的 beanName -->
<property name="targetBeanName" value="user"></property>
</bean>

<!-- 使用 parent 继承 user的属性 -->
<bean id="superUser" class="com.cruise.thinking.in.spring.ioc.container.overview.domain.SuperUser"
parent="user" primary="true">
<property name="address" value="Beijing"></property>
</bean>
</beans>
1
2
3
4
// 配置 XML 配置文件
// 启动 Spring 应用上下文
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");

根据 Bean 名称查找

实时查找

1
2
3
4
private static void lookupInRealTime(BeanFactory beanFactory) {
User user = (User) beanFactory.getBean("user");
System.out.println("实时查找:" + user);
}

延迟查找

1
2
3
<bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName" value="user"/>
</bean>
1
2
3
4
5
private static void lookupInLazy(BeanFactory beanFactory) {
ObjectFactory<User> objectFactory = (ObjectFactory<User>) beanFactory.getBean("objectFactory");
User user = objectFactory.getObject();
System.out.println("延迟查找:" + user);
}

根据 Bean 类型查找

单个 Bean 对象

1
2
3
4
private static void lookupByType(BeanFactory beanFactory) {
User user = beanFactory.getBean(User.class);
System.out.println("实时查找:" + user);
}

集合 Bean 对象

1
2
3
4
5
6
7
private static void lookupCollectionByType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
System.out.println("查找到的所有的 User 集合对象:" + users);
}
}

根据 Bean 名称 + 类型查找

1
2
3
4
public static void lookupByNameAndType(BeanFactory beanFactory) {
User supperUser = beanFactory.getBean("superUser", User.class);
System.out.println("按照名称和类型查找:" + supperUser);
}

根据 Java 注解查找

1
2
3
4
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Super {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Super
public class SuperUser extends User {

private String address;

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

@Override
public String toString() {
return "SuperUser{" +
"address='" + address + '\'' +
"} " + super.toString();
}
}

单个 Bean 对象

集合 Bean 对象

1
2
3
4
5
6
7
private static void lookupByAnnotationType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = (Map) listableBeanFactory.getBeansWithAnnotation(Super.class);
System.out.println("查找标注 @Super 所有的 User 集合对象:" + users);
}
}

2. Spring IoC 依赖注入

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
46
47
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
https://www.springframework.org/schema/util/spring-util.xsd">

<!-- 导入 -->
<import resource="dependency-lookup-context.xml"/>

<!-- 根据名称注入 -->
<bean id="userRepositoryByName"
class="com.cruise.thinking.in.spring.ioc.container.overview.repository.UserRepositoryByName"
autowire="byName"/>

<!-- 根据类型注入 -->
<bean id="userRepositoryByType"
class="com.cruise.thinking.in.spring.ioc.container.overview.repository.UserRepositoryByType"
autowire="byType">
<!--<property name="users">
<util:list>
<ref bean="user"/>
<ref bean="superUser"/>
</util:list>
</property>-->
</bean>

<!-- 注入容器内建Bean 不指定 autowire="byType" 就无法注入-->
<bean id="repositoryByInnerBean"
class="com.cruise.thinking.in.spring.ioc.container.overview.repository.RepositoryByInnerBean"
autowire="byType"/>

<!-- 设置为原型bean -->
<bean id="student" class="com.cruise.thinking.in.spring.ioc.container.overview.domain.Student" scope="prototype"/>
<!-- 延迟注入 -->
<bean id="repositoryLazy"
class="com.cruise.thinking.in.spring.ioc.container.overview.repository.RepositoryLazy"
autowire="byType"/>

<!-- 注入BeanFactory -->
<bean id="repositoryForBeanFactory"
class="com.cruise.thinking.in.spring.ioc.container.overview.repository.RepositoryForBeanFactory"
autowire="byType"/>
</beans>
1
2
BeanFactory beanFactory =
new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");

根据 Bean 名称注入

用户仓库,演示通过名称注入属性user和superUser

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
public class UserRepositoryByName {
/**注入User类型中beanName是user的Bean*/
private User user;
/**注入User类型中beanName是superUser的Bean*/
private User superUser;

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}

public User getSuperUser() {
return superUser;
}

public void setSuperUser(User superUser) {
this.superUser = superUser;
}

@Override
public String toString() {
return "UserRepositoryByName{" +
"user=" + user +
", superUser=" + superUser +
'}';
}
}

dependency-injection-context.xml

autowire = “byName”

1
2
3
4
<!-- 根据名称注入 -->
<bean id="userRepositoryByName"
class="com.kesen.thinking.in.spring.ioc.container.overview.repository.UserRepositoryByName"
autowire="byName"/>

dependency-lookup-context.xml

1
2
3
4
5
6
7
8
9
10
<bean id="user" class="com.kesen.thinking.in.spring.ioc.container.overview.domain.User">
<property name="id" value="1"></property>
<property name="name" value="kesen"></property>
</bean>

<!-- 使用 parent 继承 user的属性 -->
<bean id="superUser" class="com.kesen.thinking.in.spring.ioc.container.overview.domain.SuperUser"
parent="user" primary="true">
<property name="address" value="Chengdu"></property>
</bean>
1
2
3
4
5
6
private static void injectionByName(BeanFactory beanFactory) {
UserRepositoryByName repository = beanFactory.getBean(UserRepositoryByName.class);
System.out.println("根据名称注入Bean:" + repository);
}

// 根据名称注入Bean:UserRepositoryByName{user=User{id=1, name='kesen'}, superUser=superUser{address='Chengdu'} User{id=1, name='kesen'}}

根据 Bean 类型注入

User类型 是集合

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
public class UserRepositoryByType {
/**注入所有User类型的Bean*/
private List<User> users;
/**应该注入的是SuperUser类型的Bean*/
private SuperUser superUser;

public List<User> getUsers() {
return users;
}

public void setUsers(List<User> users) {
this.users = users;
}

public SuperUser getSuperUser() {
return superUser;
}

public void setSuperUser(SuperUser superUser) {
this.superUser = superUser;
}

@Override
public String toString() {
return "UserRepositoryByType{" +
"users=" + users +
", superUser=" + superUser +
'}';
}
}

autowire = “byType”

1
2
3
4
5
6
7
8
9
10
11
<!-- 根据类型注入 -->
<bean id="userRepositoryByType"
class="com.kesen.thinking.in.spring.ioc.container.overview.repository.UserRepositoryByType"
autowire="byType">
<!--<property name="users">
<util:list>
<ref bean="user"/>
<ref bean="superUser"/>
</util:list>
</property>-->
</bean>

单个 Bean 对象

1
2
3
4
private static void injectionByType(BeanFactory beanFactory) {
UserRepositoryByType repository = beanFactory.getBean(UserRepositoryByType.class);
System.out.println("根据类型注入单个Bean:" + repository.getSuperUser());
}

根据类型注入单个Bean:superUser{address=’Chengdu’} User{id=1, name=’kesen’}

集合 Bean 对象

1
2
3
4
5
private static void injectionByType(BeanFactory beanFactory) {
UserRepositoryByType repository = beanFactory.getBean(UserRepositoryByType.class);
System.out.println("根据类型注入单个Bean:" + repository.getSuperUser());
System.out.println("根据类型注入集合Bean:" + repository.getUsers());
}

根据类型注入单个Bean:superUser{address=’Chengdu’} User{id=1, name=’kesen’}
根据类型注入集合Bean:[User{id=1, name=’kesen’}, superUser{address=’Chengdu’} User{id=1, name=’kesen’}]

注入容器內建 Bean 对象

1
2
3
4
5
6
7
8
9
10
11
12
public class RepositoryByInnerBean {

private Environment environment;

public Environment getEnvironment() {
return environment;
}

public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
1
2
3
4
<!-- 注入容器内建Bean 不指定 autowire="byType" 就无法注入-->
<bean id="repositoryByInnerBean"
class="com.kesen.thinking.in.spring.ioc.container.overview.repository.RepositoryByInnerBean"
autowire="byType"/>
1
2
3
4
5
6
7
8
9
/**
* {@link Environment}并不是我们自定义的业务Bean 如果能够注入说明是容器内建的Bean
*
* @param beanFactory
*/
private static void injectionByInnerBean(BeanFactory beanFactory) {
RepositoryByInnerBean repository = beanFactory.getBean(RepositoryByInnerBean.class);
System.out.println("注入容器内建Bean:" + repository.getEnvironment());
}

注入非 Bean 对象

1
2
3
4
5
6
7
8
9
private static void injectionByNonBean(BeanFactory beanFactory) {
try {
BeanFactory factoryBean = beanFactory.getBean(BeanFactory.class);
// 报错了 无法获取 说明BeanFactory不是一个Bean对象而是一个容器内建依赖
System.out.println("注入非Bean对象:" + factoryBean);
} catch (NoSuchBeanDefinitionException e) {
e.printStackTrace();
}
}

注入类型

实时注入

延迟注入

1
2
3
4
5
6
7
8
9
10
11
12
public class RepositoryLazy {

private ObjectFactory<Student> objectFactory;

public ObjectFactory<Student> getObjectFactory() {
return objectFactory;
}

public void setObjectFactory(ObjectFactory<Student> objectFactory) {
this.objectFactory = objectFactory;
}
}
1
2
3
4
5
6
<!-- 设置为原型bean -->
<bean id="student" class="com.kesen.thinking.in.spring.ioc.container.overview.domain.Student" scope="prototype"/>
<!-- 延迟注入 -->
<bean id="repositoryLazy"
class="com.kesen.thinking.in.spring.ioc.container.overview.repository.RepositoryLazy"
autowire="byType"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 通过间接的方式注入,不直接注入依赖
* <p>比如希望延迟依赖注入Student,可以通过依赖注入ObjectFactory<Student>来实现延迟注入Student</p>
* 由于{@link Student}是一个原型Bean,而且是延迟依赖注入,所以在{@link ObjectFactory#getObject()}时才会注入{@link Student}
*
* @param beanFactory
*/
private static void injectionLazy(BeanFactory beanFactory) {
RepositoryLazy repositoryLazy = beanFactory.getBean(RepositoryLazy.class);
ObjectFactory<Student> objectFactory = repositoryLazy.getObjectFactory();
Student student = objectFactory.getObject();
System.out.println("延迟注入:" + student);
}

3. Spring IoC 依赖来源

bean与非bean

自定义Bean

容器内建Bean对象

Environment

容器内建依赖

BeanFactory

4. Spring IoC 配置元信息

id/class

4.1 Bean 定义配置

基于 XML 文件

标签

基于 Properties 文件

基于 Java 注解

@Value

基于 Java API(专题讨论)

BeanDifinition

4.2 IoC 容器配置

非核心的特性,

基于 XML 文件

基于 Java 注解

基于 Java API (专题讨论)

4.3 外部化属性配置

基于 Java 注解

5. Spring IoC 容器

BeanFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* {@link ApplicationContext}继承了 {@link BeanFactory},
* {@link AbstractApplicationContext}实现了{@link ApplicationContext}
* 在{@link AbstractApplicationContext}中还组合了一个{@link BeanFactory}的实现{@link DefaultListableBeanFactory}
* {@link ApplicationContext}中对Bean的管理还是通过的{@link BeanFactory}的实现,类似于代理模式
* {@link ApplicationContext}含有{@link BeanFactory}的所有特性
*/
public static void whoIsIoC() {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");
RepositoryForBeanFactory repository = applicationContext.getBean(RepositoryForBeanFactory.class);
BeanFactory beanFactory = repository.getBeanFactory();
System.out.println(beanFactory == applicationContext);//false
System.out.println(beanFactory == applicationContext.getAutowireCapableBeanFactory());//true
}

6. Spring 应用上下文

ApplicationContext

ApplicationContext 除了 IoC 容器角色,还有提供:

  • 面向切面(AOP)
  • 配置元信息(Configuration Metadata)
  • 资源管理(Resources)
  • 事件(Events)
  • 国际化(i18n)
  • 注解(Annotations)
  • Environment 抽象(Environment Abstraction)

7. 使用 Spring IoC 容器

dependency-lookup-context.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<bean id="user" class="com.cruise.thinking.in.spring.ioc.container.overview.domain.User">
<property name="id" value="1"></property>
<property name="name" value="Cruise"></property>
</bean>

<!-- 使用 ObjectFactory 的实现 TargetBeanObjectFactory 实现延迟加载 -->
<!-- TargetBeanObjectFactory 是 ObjectFactoryCreatingFactoryBean的 内部类-->
<bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<!-- 配置关联的 beanName -->
<property name="targetBeanName" value="user"></property>
</bean>

<!-- 使用 parent 继承 user的属性 -->
<bean id="superUser" class="com.cruise.thinking.in.spring.ioc.container.overview.domain.SuperUser"
parent="user" primary="true">
<property name="address" value="Beijing"></property>
</bean>

1、使用xml

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

/**
* 使用BeanFactory就无法使用事件机制、AOP等这些高级特性了
*
*/
public static void beanFactoryAsIoCContainer(){
// 创建 BeanFactory 容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// 加载配置
int count = reader.loadBeanDefinitions("classpath:/META-INF/dependency-lookup-context.xml");
System.out.println("加载的Bean数量"+count);
User user = beanFactory.getBean(User.class);
System.out.println("通过BeanFactory获取Bean:"+user);
}

2、使用注解

1
2
3
4
5
6
7
@Bean
public User user(){
User user = new User();
user.setId(2L);
user.setName("Tom");
return user;
}
1
2
3
4
5
6
7
8
9
10
11
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类 AnnotationApplicationContextAsIoCContainerDemo 作为配置类(Configuration Class)
applicationContext.register(AnnotationApplicationContextAsIoCContainerDemo.class);
// 启动应用上下文
applicationContext.refresh();
// 依赖查找集合对象
lookupCollectionByType(applicationContext);

// 关闭应用上下文
applicationContext.close();
1
2
3
4
5
6
7
private static void lookupCollectionByType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
System.out.println("查找到的所有的 User 集合对象:" + users);
}
}

8. Spring IoC 容器生命周期

启动

运行

停止

9. 面试题

什么是 Spring IoC容器?

答:Spring Framework implementation of the Inversion of Control (IoC) principle.

IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean

BeanFactory 与 FactoryBean?

答: BeanFactory 是 IoC 底层容器 FactoryBean 是 创建 Bean 的一种方式,帮助实现复杂的初始化逻辑

Spring IoC 容器启动时做了哪些准备?

答:IoC 配置元信息读取和解析、IoC 容器生命周期、Spring 事件发布、 国际化等