Spring IOC 探究
概述
BeanFactory 接口,典型功能有:
- getBean

到底什么是 BeanFactory
- 它是 ApplicationContext 的父接口
- 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory
一个key value
beanFactory内存储着bean对象
BeanFactory 能干点啥
- 表面上只有 getBean
- 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供
- 例子中通过反射查看了它的成员变量 singletonObjects,内部包含了所有的单例 bean
java
ublic class A01 {
private static final Logger log = LoggerFactory.getLogger(A01.class);
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
/*
2. BeanFactory 能干点啥
- 表面上只有 getBean
- 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能, 都由它的实现类提供
BeanFactory (接口) ← 定义 getBean 等
↑
ListableBeanFactory (接口) ← 定义 getBeansOfType 等
↑
ConfigurableBeanFactory (接口) ← 提供注册作用域等功能
↑
HierarchicalBeanFactory (接口) ← 父子容器
↑
DefaultListableBeanFactory (实现类)
↑
├── 继承 DefaultSingletonBeanRegistry (管理单例缓存)
├── 继承 AbstractAutowireCapableBeanFactory (负责创建 Bean 实例)
└── 实现上述接口们
*/
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
ConfigurableApplicationContext (接口)
└── AbstractApplicationContext
└── GenericApplicationContext
└── DefaultListableBeanFactory
└── AbstractAutowireCapableBeanFactory
└── AbstractBeanFactory
└── DefaultSingletonBeanRegistry ← ← ← 你要的类! private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
singletonObjects.setAccessible(true);
//獲取私有字段并且允許訪問
//獲取容器内的所有bean 同時過濾掉被component修飾的bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
.forEach(e -> {
System.out.println(e.getKey() + "=" + e.getValue());
});
SpringApplication.run(...) 是 Spring Boot 中的启动方法:
- 它会启动一个 Spring 应用上下文(ApplicationContext);
- 会进行 Spring 的自动配置(根据 classpath 中的依赖自动加载配置);
- 会扫描并加载 Spring Bean;
- 会初始化各种自动装配的组件、运行
@ComponentScan等; - 最后会启动一个 Tomcat Web 服务器(如果是 Web 项目)
容器是 BeanFactory,但单例Bean的缓存是在 DefaultSingletonBeanRegistry 中管理的。
也就是说,BeanFactory 是接口,DefaultListableBeanFactory 是实现类,而它内部继承了 DefaultSingletonBeanRegistry,DefaultSingletonBeanRegistry 才真正维护了单例Bean的缓存。

graph
一个受 Spring 管理的 bean,生命周期主要阶段有
1. 创建:根据 bean 的构造方法或者工厂方法来创建 bean 实例对象
2. 依赖注入:根据 @Autowired,@Value 或其它一些手段,为 bean 的成员变量填充值、建立关系
3. 初始化:回调各种 Aware 接口,调用对象的各种初始化方法
4. 销毁:在容器关闭时,会销毁所有单例对象(即调用它们的销毁方法)
* prototype 对象也能够销毁,不过需要容器这边主动调用
我们要实现的就是对应的后置处理器的功能
ps:spring源码基本还回去了
自写探究
单一职责原则
前文

| 层级 | 组件 | 核心职责 | 设计模式 | 关键说明 |
|---|---|---|---|---|
| 容器底层 | Map 容器 | 存储 Bean | --- | Map<String, Object> / Map<String, BeanDefinition> |
| 工厂层 | BeanFactory | 创建和获取 Bean | 工厂模式 | getBean() 的真正执行者 |
| 上下文层 | ApplicationContext | 对外统一入口 | 门面模式 | 持有 BeanFactory 引用 |
| 解析层 | BeanDefinitionReader | 解析配置 | 策略模式 | 不关心 Bean 如何创建 |
| 元数据层 | BeanDefinition | 描述 Bean 信息 | 元信息模式 | 类名、作用域、依赖 |
| 配置来源 | xml / yml / annotation / properties | 定义 Bean | 配置策略 | 多配置形式统一为 BeanDefinition |
| 实例层 | Bean 实例 | 真实对象 | 反射 | Class.newInstance() |
| 代理层 | 原生 Bean / 代理 Bean | 增强功能 | 代理模式 | AOP、事务等 |
| 包装层 | BeanWrapper | 包装 Bean | 包装器模式 | 持有 Bean 引用 |
| 缓存层 | IOC 容器缓存 | 复用 Bean | 缓存思想 | 单例池 |

开写
java
public class GPDispatcherServlet extends HttpServlet {
private GPApplicationContext applicationContext;
@Override
public void init(ServletConfig config) throws ServletException {
//初始化Spring核心IoC容器
applicationContext = new GPApplicationContext(config.getInitParameter("contextConfigLocation"));
//==============MVC部分==============
//5、初始化HandlerMapping
doInitHandlerMapping();
System.out.println("GP Spring framework is init.");
}
}
GPApplicationContext {
}
GPApplicationContext 成员变量职责对应表
| 变量名 | 类型 | 在你这套框架中的作用 | Spring 中的对应角色 | 设计含义 |
|---|---|---|---|---|
reader |
GPBeanDefinitionReader |
读取并解析配置 | BeanDefinitionReader |
配置解析器 |
beanDefinitionMap |
Map<String, GPBeanDefinition> |
存储 Bean 元信息 | beanDefinitionMap |
IOC 的"设计图仓库" |
factoryBeanInstanceCache |
Map<String, GPBeanWrapper> |
缓存已创建的 BeanWrapper | singletonObjects / earlySingletonObjects |
一级 IOC 容器(实例缓存) |
factoryBeanObjectCache |
Map<String, Object> |
缓存原始 Bean 对象 | singletonFactories(简化版) |
原始对象缓存 |
三大核心 Map
| Map | 存的是什么 | 何时放 | 作用 |
|---|---|---|---|
beanDefinitionMap |
BeanDefinition |
容器启动时 | 描述 Bean,不创建 |
factoryBeanObjectCache |
原生 Bean | 实例化后 | 保存最原始对象 |
factoryBeanInstanceCache |
BeanWrapper |
DI 前 | 对外提供 Bean |
BeanDefinition管"是什么",Object管"创建",BeanWrapper管"使用"。
方法级变量
getBean(String beanName)
populateBean(...)
instantiateBean(...)
GPApplicationContext构造探究
思路
gpservlet传来配置,构造负责解析 GPBeanDefinitionReader,返回 GPBeanDefinition 描述对应的bean元信息
GPBeanDefinition 的存在让容器可以:
-
先收集所有 Bean 的描述
-
什么时候创建,由容器决定
package com.gupaoedu.vip.spring.framework.beans.config;
/**
-
Created by Tom.
*/
public class GPBeanDefinition {
private String factoryBeanName;
private String beanClassName; //Bean 对应的 Java 类全限定名public String getFactoryBeanName() {
return factoryBeanName;
}public void setFactoryBeanName(String factoryBeanName) {
this.factoryBeanName = factoryBeanName;
}public String getBeanClassName() {
return beanClassName;
}public void setBeanClassName(String beanClassName) {
this.beanClassName = beanClassName;
}
}
-
java
//1、加载配置文件
reader = new GPBeanDefinitionReader(configLocations);
//2、解析配置文件,封装成BeanDefinition
List<GPBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
GPBeanDefinitionReader 探究
初始化,扫描文件下的文件,找到需要加载的类。保存进regitryBeanClasses
java
public class GPBeanDefinitionReader {
//保存扫描的结果
private List<String> regitryBeanClasses = new ArrayList<String>();
private Properties contextConfig = new Properties();
}
public GPBeanDefinitionReader(String... configLocations) {
doLoadConfig(configLocations[0]);
//扫描配置文件中的配置的相关的类
doScanner(contextConfig.getProperty("scanPackage"));
}
java
doLoadConfig---
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation.replaceAll("classpath:",""));
try {
contextConfig.load(is);
}

loadBeanDefinitions 探究

| 变量 | 本质含义 | 用在什么时候 | 是否唯一 |
|---|---|---|---|
factoryBeanName |
Bean 在 IOC 中的名字 / Key | getBean()、@Autowired |
不一定 |
beanClassName |
Bean 的 真实类(全限定名) | 反射创建实例 | 唯一 |
BeanDefintion缓存
java
配置解析完成
↓
生成 BeanDefinition
↓
doRegistBeanDefinition ←(你现在这一步)
↓
getBean()
↓
实例化 + DI
一个 BeanDefinition,两个 key --一 个 BeanDefinition 可以被多个 key 指向"。
beanDefinitionMap
├── "userService" ----------------┐
├── "com.xxx.UserService" --------┼──> 同一个 BeanDefinition
├── "com.xxx.UserServiceImpl" ----┘
| 查的词 | 实际指向 |
|---|---|
"userService" |
UserServiceImpl 的配置 |
"com.xxx.UserService" |
UserServiceImpl 的配置 |
"com.xxx.UserServiceImpl" |
UserServiceImpl 的配置 |
因为 DI 查找 Bean 有多种方式:
@GPAutowired("userService")
@GPAutowired
private UserService userService;
field.getType().getName()
doAutowrited
java
private void doAutowrited() {
//调用getBean()
//这一步,所有的Bean并没有真正的实例化,还只是配置阶段
for (Map.Entry<String,GPBeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {
String beanName = beanDefinitionEntry.getKey();
getBean(beanName);
}
}
java
private void doAutowrited() {
Set<GPBeanDefinition> processed = new HashSet<>();
for (GPBeanDefinition bd : beanDefinitionMap.values()) {
if (processed.contains(bd)) continue;
getBean(bd.getFactoryBeanName());
processed.add(bd);
}
}
可以去重,可以不去
查看getbean-
getbean
判断容器内有没有wrapper. 没有的化 用classname 拿到配置信息,反射创建,wrapper包装。然后保存ioc容器
-再执行依赖注入
-这个类的创建需要依赖注入
-再返回对象


//4、保存到IoC容器
factoryBeanInstanceCache.put(beanName,beanWrapper);
执行依赖注入
执行依赖注入
java
private void populateBean(String beanName, GPBeanDefinition beanDefinition, GPBeanWrapper beanWrapper) {
//可能涉及到循环依赖?
//A{ B b}
//B{ A b}
//用两个缓存,循环两次
//1、把第一次读取结果为空的BeanDefinition存到第一个缓存
//2、等第一次循环之后,第二次循环再检查第一次的缓存,再进行赋值
Object instance = beanWrapper.getWrapperInstance();
Class<?> clazz = beanWrapper.getWrappedClass();
//在Spring中@Component
if(!(clazz.isAnnotationPresent(GPController.class) || clazz.isAnnotationPresent(GPService.class))){
return;
}
//把所有的包括private/protected/default/public 修饰字段都取出来
for (Field field : clazz.getDeclaredFields()) {
if(!field.isAnnotationPresent(GPAutowired.class)){ continue; }
GPAutowired autowired = field.getAnnotation(GPAutowired.class);
//如果用户没有自定义的beanName,就默认根据类型注入
String autowiredBeanName = autowired.value().trim();
if("".equals(autowiredBeanName)){
//field.getType().getName() 获取字段的类型
autowiredBeanName = field.getType().getName();
}
//暴力访问
field.setAccessible(true);
try {
if(this.factoryBeanInstanceCache.get(autowiredBeanName) == null){
continue;
}
//ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例
field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());
} catch (IllegalAccessException e) {
e.printStackTrace();
continue;
}
}
}
扫描是有顺序的,但 DI 不能依赖扫描顺序;
如果依赖的 Bean 还没创建好,注入会"悄悄失败",字段保持 null。