通过实现 mini 版本 spring 框架,成为一个更合格的spring工程师
@ComponentScanBeanDefinitionMapgetClass,annotation,field,constructor,methodAware 容器感知技术(set 方法 + 反射应用)InitializingBean 的方式来初始化 Bean 对象Java-jdk 动态代理方式实现 AOP 切面 BeanPostProcessor三级缓存依赖解决循环依赖问题
框架运行过程步骤描述包含两部分内容:默认为 spring 框架,使用[应用]标注的表示,这一步骤描述是属于应用程序
SpringApplicationContext:容器用于加载配置文件,并提供获取 Bean 的方法 getBean()AppConfig: 用于标识 spring 框架需要扫描的包路径@ComponentScan,@Component: 通过注解的方式来标记:哪些包需要被扫描,被扫描包目录下使用了 @Component 标记的类才就是 Bean 对象类加载器来获取注解标记的所有类对象,并准备将类对象转换为 Bean 对象的指定数据类型 BeanDefinition单例 singleton ,原型 prototype. 因此创建一个注解 @Scope 用来标记这个对象是哪一种 Bean 类型beanDefinitionMap 中管理。beanDefinitionMap中查找是否已创建,没有则创建,有则返回已创建的,scope 作用域为原型的 Bean 直接创建对应实例beanDefinitionMap中,并为所有的单例 Bean 创建了实例,所有的原型 Bean 仅仅是存储了其 Bean 数据结构beanDefinitioninitializingBeanBeanPostProcessor.需要注意 代理是对 某一个具体对象的某种行为进行代理,所以必须被代理的对象必须至少实现一个接口在书写源码的过程中,遇到不清晰的知识点,记录在文件夹 source-ex 中
public SpringApplicationContext(Class<?> primarySource, Class<?> configClass) {
this.configClass = configClass;
// 扫描
scanBeanDefinition(configClass);
// AOP
registerBeanPostProcessors();
// 获取Bean
preInstantiateSingletons();
}
/**
* 扫描.解析配置类
* 过程:通过传入的配置信息 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();
}
}
}
}
}
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);
}
}
}