概述
项目的初衷是独立作出一个成熟的有特色的IOC容器,但由于过程参考Spring太多,而且也无法作出太多改进,于是目的变为以此项目作为理解Spring的一个跳板,与网上的一些模仿Spring的框架不同,本项目主要是针对注解形式
地址是Thales
流程
在Spring中,一个bean的形成分三个大的阶段,
- bean的定义阶段(包含BeanDefinition的加载,解析,与注册)
- bean的实例化阶段(包含对象的创建,属性的注入)
- bean的初始化阶段(包含一些资源的初始化,譬如打开文件建立连接等等)
这只是大概的划分,关于BeanPostProcessor等后置处理并没有显式的提及.
类的设计
如果只想了解一个bean是怎么从生到死的,只需要一步步debug就好了,如果看不懂,就多debug几遍.可是如果想实现一个类似的容器,类的设计,职责的分配,接口的实现继承必然是要了解的(除非你想几个类做完所有的事)
以下是
DefaultListableBeanFactory的类图
是不是顶不住
我们再来看一张图
第一张是Spring5.0的,第二张图是Spring0.9的,所以并没有必要在一开始就引入过多的设计复杂度
我们再来看一套对比图
哪一个是0.9的,哪一个是5.0的一目了然.
说这么多的目的,是说明我们没必要一开始就奔着最完善的目标去写,可以一步步来,一步步加入功能
实现简易IOC
众所周知,SpringIoC中最基本的就是BeanFactory
我们先定义一个BeanFactory接口
//暂时就给这一个方法
public interface BeanFactory {
/**
* 根据名字获取Bean实例
* @param name
* @return
*/
Object getBean(String name);
}
beanDefinition
由于是注解形式,我们不能再像xml那样给定一个资源文件再去解析了,而应该去扫描classPath下所有带有@Component的类,
这时候我们需要给定的参数就从文件路径变成了包路径,我们只需要扫描这个包及其子包内符合条件的类,并且将其转化为BeanDefinition再注册就好.执行这个功能的是
ClassPathBeanDefinitionScanner这个类.在这一步,就已经和传统的流程有所区别了,我们会传入一个ResourceLoader去实现具体的扫描功能(即定位),但不会再有专门的类去处理解析这一步
public interface Resource {
File getFile();
String getFilename();
String getFilePath();
}
//在最初设计的时候这个抽象类似乎没有用,但考虑到以后的扩展,还是先放在这
public abstract class AbstractResource implements Resource {
@Override
public String getFilename() {
return getFile().getName();
}
@Override
public String getFilePath() {
return getFile().getPath();
}
}
//这就是最终我们实例化bean时用到的Resource类,在Spring中并没有直接用,而是通过外观模式集成了一下成为RootBeanDefinition
public class ClassPathResource extends AbstractResource {
private final String path;
private ClassLoader classLoader;
private Class<?> clazz;
public ClassPathResource(String path, ClassLoader classLoader, Class<?> clazz) {
this.path = path;
this.classLoader = classLoader;
this.clazz = clazz;
}
}
public interface ResourceLoader {
Resource getResource(String location);
}
//此类能够实现加载多个资源
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
List<? extends Resource> getResources(String location);
}
//这个类就是正式用于扫描的类了
public class PathMatchingResourcePatternResolver implements ResourcePatternResolver {
private final ResourceLoader resourceLoader;
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader){
this.resourceLoader = resourceLoader;
}
@Override
public Resource getResource(String location) {
return resourceLoader.getResource(location);
}
//在Spring中,是通过一层层方法的包装完成包名到路径的转换再到每个文件的扫描再转换为Resource,这里暂时就先一步到位,把具体实现放在工具类里
@Override
public List<? extends Resource> getResources(String location) {
Set<Class<?>> classes = ClassUtils.getClasses(location);
List<ClassPathResource> classPathResources = new ArrayList<>();
for (Class<?> clazz:classes) {
classPathResources.add(new ClassPathResource("",clazz.getClassLoader(),clazz));
}
return classPathResources;
}
}
但最后直接使用的并不是
PathMatchingResourcePatternResolver
而是把他作为
ClassPathBeanDefinitionScanner的一个属性,在这个类里调用.
我们得到了Resource,如何获得对应的BeanDefinition?
先考虑