spring 源码实现
通过实现 mini 版本 spring 框架,成为一个更合格的spring工程师
- 实现 spring 框架整个扫描过程,
@ComponentScan
- 实现 解析全匹配类路径,并使用类加载器加载(双亲委派原理)扫秒类,生成
BeanDefinitionMap
- 贯穿熟悉 Reflection 反射调用,包括:
getClass,annotation,field,constructor,method
- 实现
Aware
容器感知技术(set 方法 + 反射应用) - 实现 使用实现
InitializingBean
的方式来初始化 Bean 对象 - 实现 使用
Java-jdk 动态代理
方式实现 AOP 切面BeanPostProcessor
- 熟悉 Bean 命名定义,创建过程,声明周期
- 实现
三级缓存依赖
解决循环依赖
问题
spring 框架运行过程描述
- com.source.content 应用程序代码
- com.source.spring 框架源码
框架运行过程步骤描述包含两部分内容:默认为 spring 框架,使用[应用]标注的表示,这一步骤描述是属于应用程序
- 创建管理容器
SpringApplicationContext
:容器用于加载配置文件,并提供获取 Bean 的方法 getBean() - (应用)创建一个应用配置类
AppConfig
: 用于标识 spring 框架需要扫描的包路径 - (应用)创建两个类 UserService,XxUtils:用于稍后交给 spring 框架来扫描,一个是 Bean 对象,一个普通的类,只有注解标记的类才会被解析
- 创建注解
@ComponentScan
,@Component
: 通过注解的方式来标记:哪些包需要被扫描,被扫描包目录下使用了@Component
标记的类才就是 Bean 对象 - 通过
类加载器
来获取注解标记的所有类对象
,并准备将类对象转换为 Bean 对象的指定数据类型BeanDefinition
- 此时,我们将 Bean 设计为两种类型:
单例 singleton
,原型 prototype
. 因此创建一个注解@Scope
用来标记这个对象是哪一种 Bean 类型 - 在扫描过程中,将所有的 Bean 都以 <beanName,beanDefinition>的键值对存入
beanDefinitionMap
中管理。 - 扫描完成后,为所有 bean 对象创建对应实例。这里:scope 作用域为单例的 Bean 首先在
beanDefinitionMap
中查找是否已创建,没有则创建,有则返回已创建的,scope 作用域为原型的 Bean 直接创建对应实例
- 依赖注入实现。所有的 Bean 已被扫描并存储在
beanDefinitionMap
中,并为所有的单例 Bean 创建了实例,所有的原型 Bean 仅仅是存储了其 Bean 数据结构beanDefinition
- 容器感知 beanNameAware: 对于某一个 Bean 本身(userService)而言,使用者是不知道关于容器的信息。实现 beanNameAware 容器感知
- Bean 初始化方式: 在 spring 源码中提供了两种 Bean 初始化方式,这里实现其中之一
initializingBean
- AOP 实现:通过 JAVA-JDK 动态代理实现,即实现 AOP 切面,实现位置位于
BeanPostProcessor
.需要注意 代理是对 某一个具体对象的某种行为进行代理,所以必须被代理的对象必须至少实现一个接口
困难与挑战
在书写源码的过程中,遇到不清晰的知识点,记录在文件夹 source-ex
中
源码大赏
- spring 容器
public SpringApplicationContext(Class<?> primarySource, Class<?> configClass) {
this.configClass = configClass;
// 扫描
scanBeanDefinition(configClass);
// AOP
registerBeanPostProcessors();
// 获取Bean
preInstantiateSingletons();
}
- 扫描 scanBeanDefinition
/**
* 扫描.解析配置类
* 过程:通过传入的配置信息 ComponentScan 获取扫描路径 --> 执行扫描 @Component
*
* @param configClass 配置类
*/
private void scanBeanDefinition(Class<?> configClass) {
// 1. 传递来的类,是都被扫描注解标记
ComponentScan componentScanAnnotation = configClass.getAnnotation(ComponentScan.class);
String scanPath = componentScanAnnotation.value();
// 2. 获取扫扫描路径后,准备扫描。一个包下有许多的类,我们框架关心的是被指定注解标记的类(@Component),才会被扫描
// 如何获取一个包下面的java类?使用类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL resource = classLoader.getResource("com/sourcecode/content/service");
assert resource != null;
File file = new File(resource.getFile());
if (file.isDirectory()) {
for (File listFile : Objects.requireNonNull(file.listFiles())) {
// 2.1 由于 classLoader.loadClass(quotePath) 需要获取的是 com.xxx.xxx这样的引用地址,所以需要转换一下
String absolutePath = listFile.getAbsolutePath();
if (absolutePath.endsWith(".class")) {
String quotePath = resolveClassAbsolutePath(absolutePath);
try {
Class<?> aClass = classLoader.loadClass(quotePath);
if (aClass.isAnnotationPresent(Component.class)) {
// 2.2 使用 @Component 注解装饰类:就表示希望将它交给Spring容器托管,它是一个bean对象
// class ---??---> Bean
// 2.3 在将class转换为我们制定的Bean类型时,由于Bean有两种类型:单例和原型。需要使用单例模式来确保Bean对象的唯一性
// 因此,如何实现单例呢?
// 可以创建一个@Scope注解来标识它是单例还是原型Bean类型,同时创建一个Map来保存所有的Bean。
// Map {BeanName,BeanObject} 也就是常说的单例池
// 2.4 判断是单例Bean还是原型Bean
Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value();
if ("".equals(beanName)) {
beanName = Introspector.decapitalize(aClass.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(aClass);
if (aClass.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = aClass.getDeclaredAnnotation((Scope.class));
String scope = scopeAnnotation.value();
beanDefinition.setScope(scope);
} else {
beanDefinition.setScope("singleton");
}
// 存储Bean
beanDefinitionMap.put(beanName, beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
- 获取 Bean:
preInstantiateSingletons
/**
* 初始化:实例化所有的单例Bean
*/
private void preInstantiateSingletons() {
beanDefinitionMap.forEach((beanName, beanDefinition) -> {
if (beanDefinition.isSingleton()) {
getBean(beanName);
}
});
}
public Object getBean(String beanName) {
Asset.notNull(beanName);
if (!beanDefinitionMap.containsKey(beanName)) {
throw new NullPointerException("没有找到bean:" + beanName);
} else {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition.isSingleton()) {
// 实现3级缓存或者使用最简单的一级缓存,只需在方法中 getSingleton 决定
Object singletonObject = getSingleton(beanName, true);
// 三级缓存中都没有,那么就只能 create
if (singletonObject == null) {
singletonObject = createBean(beanName, beanDefinition);
singletonObjects.put(beanName, singletonObject);
earlySingletonObjects.remove(beanName);
singletonFactories.remove(beanName);
}
return singletonObject;
} else {
// prototype.每次创建新对象
return createBean(beanName, beanDefinition);
}
}
}