1 缘起
作为一个应用开发人员而言,会使用某一个工具分为两个层次(个人观点): 第一个层次,知道工具,会使用这个工具解决问题; 第二个层次,理解工具的实现原理。 关于Spring的学习,还在第一个层次转悠,缺少原理的研究, 随着学习的深入,开始研究些Spring源码,配合IDEA调试, 逐渐理解一些Spring原理,先从创建Bean开始, 分享如下。
2 新建Bean
对于Spring学习、使用和研究人员而言, Bean必修课,Bean从何而来,又如何获取, 弄清楚这些,会加深对Spring的理解。 首先从创建Bean开始,常见的创建方式有3种:
- 注解@Bean方式
- Xml方式
- BeanDefinitionBuilder方式
Spring提供了多种方式创建Bean,这里的创建Bean是创建自定义的Bean, 不涉及Spring启动时需要创建的系统Bean, 但是殊途同归,最终都是通过BeanDefinition构建Bean, 创建自定义的Bean会经历两个核心步骤:
- 注册BeanDefinition
填充beanDefinitionMap和beanDefiinitionNames,为填充singletonObjects准备 - 创建单例Bean
填充singletonObjects,供后续获取Bean使用
2.1 @Bean方式
通过@Bean方式创建自定义Bean是最明显的方式, 直接在对应的方法上添加@Bean注解,表明这是Bean, 结合@Configuration,Spring会自动创建Bean, 测试样例及注释如下:
package com.monkey.springboottemplate.modules.bean_definition;
import com.monkey.springboottemplate.common.entity.UserEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanDefinitionByAnnotation {
private static final Logger logger = LoggerFactory.getLogger(BeanDefinitionByAnnotation.class);
@Bean
public UserEntity myUserBean() {
return new UserEntity("xiaohua", "欧洲");
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(BeanDefinitionByAnnotation.class);
applicationContext.refresh();
logger.info(">>>>>>>>>>应用程序上下文启动");
UserEntity user = applicationContext.getBean(UserEntity.class);
logger.info(">>>>>>>>Bean:{}", user);
applicationContext.close();
logger.info(">>>>>>>>关闭应用程序上下文");
}
}
2.2 XML方式
通过XML创建Bean,首先要构建对应的XML文件, 配置样例及注释如下:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<bean id="userA" class="com.monkey.springboottemplate.common.entity.UserEntity">
<property name="nickname" value="xiaoxml" />
<property name="address" value="欧洲" />
</bean>
</beans>
XML文件只是存储Bean的持久化配置文件, 想要使该Bean加载到Spring容器,仍需要通过相关类加载该XML文件, 测试样例以及注释如下:
package com.monkey.springboottemplate.modules.bean_definition;
import com.monkey.springboottemplate.common.entity.UserEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanDefinitionByXml {
private static final Logger logger = LoggerFactory.getLogger(BeanDefinitionByXml.class);
public static void main(String[] args) {
String beanConfig = "bean-creation.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(beanConfig);
UserEntity user = applicationContext.getBean("userA", UserEntity.class);
logger.info(">>>>>>>>Creation Bean using XML, User Bean :{}", user);
}
}
2.3 BeanDefinition
上面两种创建Bean的方式对于应用开发者而言是显式的, 而,更贴近Spring底层的方式是通过BeanDefinition创建Bean, Spring创建Bean的第一个过程即注册BeanDefinition,就是下面的样例, 通过显式的方式注册BeanDefinition, 然后再创建单例的Bean,测试样例及注释如下:
package com.monkey.springboottemplate.modules.bean_definition;
import com.monkey.springboottemplate.common.entity.UserEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class BeanDefinitionByBuilder {
private static final Logger logger = LoggerFactory.getLogger(BeanDefinitionByBuilder.class);
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserEntity.class);
beanDefinitionBuilder.addPropertyValue("nickname", "xiaoxiao");
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
applicationContext.registerBeanDefinition("myBean", beanDefinition);
applicationContext.refresh();
logger.info(">>>>>>>>>>应用程序上下文启动");
UserEntity user1 = applicationContext.getBean(UserEntity.class);
UserEntity user2 = (UserEntity) applicationContext.getBean("myBean");
logger.info(">>>>>>>>>>User:{}, user2:{}", user1, user2);
applicationContext.close();
logger.info(">>>>>>>>关闭应用程序上下文");
}
}
3 源码分析
3.1 创建Bean流程
Spring中创建自定义Bean的核心流程如下图所示, 由图可知,创建自定义Bean分成两个部分:
核心都是填充对应的数据结构, 最终把Bean添加到Spring构建的Bean容器中,供后续取用。 虽然干巴巴流程太流于表面,没有质感,但是,这些使用源码中抠出来的 虽然散,但是,易于理解和记忆, 后面会详细给出流程的源码及源码分析。 
3.2 BeanDefinition相关
先从注册BeanDefinition开始分析, BeanDefiniton,从命名即可知,是Bean的定义,用于描述Bean, Spring将BeanDefinition设计为一个interface,面向接口, BeanDefinition源码长这样,如下图所示, 这里仅需要知道BeanDefinition是用于描述Bean的即可, 会在其他文章详细讲解BeanDefinition。  为什么要注册BeanDefinition? 因为BeanDefinition用于描述Bean实例,因此,创建Bean的时候需要先注册BeanDefition,描述Bean,然后再创建Bean,两者是关联的。 注册BeanDefinition的核心是填充beanDefinitionMap和beanDefinitionNames。
-
beanDefinitionMap 位置:org.springframework.beans.factory.support.DefaultListableBeanFactory#beanDefinitionMap 源码如下图所示,由注释可知,beanDefinitionMap用于存储bean名称和beanDefintion,所谓的关联关系就在这里,(key, value)->(beanName, beanDefinition),为后续通过beanName获取beanDefinition做准备。  -
beanDefinitionNames 位置:org.springframework.beans.factory.support.DefaultListableBeanFactory#beanDefinitionNames 源码如下图所示,由注释可知,beanDefintionNames存储Bean名称,为后续填充singletonObjects做准备。 
3.2.1 流程
前置知识已介绍,下面进入正题,讲解流程。 源码调试流程如下图所示,感兴趣的可以参照流程图中打断点调试。 
(1)AnnotationConfigApplicationContext中注册BeanDefinition:registerBeanDefinition (2)GenericApplicationContext中注册BeanDefinition:registerBeanDefinition (3)DefaultListableBeanFactory中注册BeanDefinition:registerBeanDefinition (4)DefaultListableBeanFactory中填充beanDefinitionMap:beanDefinitionMap.put (5)DefaultListableBeanFactory中填充beanDefinitionNames:beanDefinitionNames.add
3.2.2 源码调试过程
3.2.2.1 AnnotationConfigApplicationContext中注册BeanDefinition:registerBeanDefinition

3.2.2.2 GenericApplicationContext中注册BeanDefinition:registerBeanDefinition

3.2.2.3 DefaultListableBeanFactory中注册BeanDefinition:registerBeanDefinition

3.2.2.4 DefaultListableBeanFactory中填充beanDefinitionMap:beanDefinitionMap.put &
3.2.2.5 DefaultListableBeanFactory中填充beanDefinitionNames:beanDefinitionNames.add
合并步骤4和5,填充对应的数据。 
3.3 Bean相关
完成上面的准备工作,接下来就要创建Bean, 这个Bean实际单例对象,在singletonObjects中存储,为后面获取Bean做准备。 singletonObjects: 位置:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#singletonObjects 源码如下图所示,由注释可知,singletonObjects缓存单例Bean实例。 
3.3.1 流程
下面开始讲解如何填充这个Map, 源码调试流程如下图所示,感兴趣的可以参照流程图中打断点调试。 
(1)AnnotationConfigApplicationContext中刷新相关的Bean:refresh (2)同步锁,AbstractApplicationContext中刷新相关Bean:refresh (3)结束BeanFactory初始化,AbstractApplicationContext初始化的最后一步,完成准备工作之后,需要固化相关的操作:finishBeanFactoryInitialization (4)预处理,DefaultListableBeanFactory实例化单体Bean:preInstantiateSingletons (5)获取Bean,AbstractBeanFactory根据Bean名称获取Bean:getBean(java.lang.String) (6)获取Bean,AbstractBeanFactory获取Bean:doGetBean (7)获取单例Bean,DefaultSingletonBeanRegistry根据Bean名称获取单例Bean:getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>) (8)添加单例Bean,DefaultSingletonBeanRegistry添加单例Bean:addSingleton (9)添加Bean,DefaultSingletonBeanRegistry:singletonObjects.put
3.3.2 源码调试过程
3.3.2.1 AnnotationConfigApplicationContext中刷新相关的Bean:refresh

3.3.2.2 同步锁,AbstractApplicationContext中刷新相关Bean:refresh

3.3.2.3 结束BeanFactory初始化,AbstractApplicationContext初始化的最后一步,完成准备工作之后,需要固化相关的操作:finishBeanFactoryInitialization

3.3.2.4 预处理,DefaultListableBeanFactory实例化单体Bean:preInstantiateSingletons
 
3.3.2.5 获取Bean,AbstractBeanFactory根据Bean名称获取Bean:getBean(java.lang.String)


3.3.2.6 获取Bean,AbstractBeanFactory获取Bean:doGetBean


3.3.2.7 获取单例Bean,DefaultSingletonBeanRegistry根据Bean名称获取单例Bean:getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)


3.3.2.8 添加单例Bean,DefaultSingletonBeanRegistry添加单例Bean:addSingleton


3.3.2.9 添加Bean,DefaultSingletonBeanRegistry:singletonObjects.put
 
3.4 获取Bean
通过上面的准备工作,填充了相关的数据, 下面可以通过应用上下文获取Bean。
3.4.1 流程
源码调试流程如下图所示,感兴趣的可以参照流程图中打断点调试。 
(1)获取Bean,AnnotationConfigApplicationContext:getBean (2)获取Bean,AbstractApplicationContext:getBean(java.lang.String) (3)获取Bean,AbstractBeanFactory:getBean(java.lang.String) (4)处理逻辑,获取Bean,AbstractBeanFactory:doGetBean (5)从填充的singletonObjects获取Bean,DefaultSingletonBeanRegistry:getSingleton(java.lang.String) (6)获取Bean,得到查询的Bean,DefaultSingletonBeanRegistry:getSingleton(java.lang.String, boolean)
3.4.2 源码调试
3.4.2.1 获取Bean,AnnotationConfigApplicationContext:getBean

3.4.2.2 获取Bean,AbstractApplicationContext:getBean(java.lang.String)

3.4.2.3 获取Bean,AbstractBeanFactory:getBean(java.lang.String)

3.4.2.4 处理逻辑,获取Bean,AbstractBeanFactory:doGetBean

3.4.2.5 从填充的singletonObjects获取Bean,DefaultSingletonBeanRegistry:getSingleton(java.lang.String)

3.4.2.6 获取Bean,得到查询的Bean,DefaultSingletonBeanRegistry:getSingleton(java.lang.String, boolean)
 最终返回查到的Bean。 
4 小结
(1)新建Bean的三种方式:XML、@Bean和BeanDefinition; (2)创建Bean的核心过程: (2.1)注册BeanDefinition,填充beanDefinitionMap和beanDefinitionNames; (2.2)注册Bean,填充singletonObjects; (3)通过应用上下文获取Bean,是通过singletonObjects查询获取。
|