学习写简单Spring源码demo
最近在研究怎么实现简单的Spring的源码,通过注解的方式来实现对bean的加载管理。
首先先来看下我的工程结构:
(1)spring-common:定义了常用的枚举常量,工具类(如FileUtils提供了递归找到某个目录下所有文件的具体实现)
(2)spring-frame:是整个框架的具体实现,依赖spring-common
(3)spring-test:提供了一个简单的测试demo
我们先看测试类,这个应该更熟悉,简单得到bean实例的代码:
public static void main(String[] args) { // 1. 启动Spring MyApplicationContext context = new MyApplicationContext(AppConfig.class); // 2. getBean OrderService orderService = (OrderService) context.getBean("orderService"); orderService.getUserInfo(); }
其中我们引用了自己定义的MyApplicationContext来启动Spring去装载bean,并对bean进行初始化和实例化。
针对MyApplicationContext, 核心构造方法:
public MyApplicationContext(Class configClass) { // Spring启动要做什么事情? // 扫描类 --> 创建非懒加载的单例的bean --> 放入单例池 // 1. 单纯扫描包 List<Class> classList = scanSpecifiedPath(configClass); // 2. 解析出文件中的bean initialBeanDefinition(classList); // 3. 实例化单例的bean instantiateSingletonBean(); }
再上述看到的三个方法中,主要是使用了我们自己定义的@ComponentScan, @Component, @Scope, @Autowired注解来实现bean的扫描,识别,是否单例,依赖注入;
针对bean的初始化和后置处理,我们定义了同Spring原生的接口:InitializingBean,BeanPostProcessor来实现。
其中,我们特意构造了getBean的方法,核心代码如下:
public Object getBean(String beanName) { BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); if (Objects.equals(beanDefinition.getScope(), BaseConstant.Scope.PROTOTYPE)) { // 重新创建 return doCreateBean(beanName, beanDefinition); } else if (Objects.equals(beanDefinition.getScope(), BaseConstant.Scope.SINGLETON)) { Object obj = singletonObjectPool.get(beanName); if (Objects.isNull(obj)) { // 创建单例bean obj = doCreateBean(beanName, beanDefinition); singletonObjectPool.put(beanName, obj); } return obj; } return null; }
可以简单看到,针对Scope为原型的bean,直接创建了一个bean的实例;单例模式的bean会先从单例池中直接获取,否则才会创建之后再加入单例池。
简单描述到这里,更多实现细节和demo测试,见gitee源码: