前篇:
先来说一下我们要使用的容器 MYApplicationContext 的继承关系,它实现了 BeanFactory 接口和 AbstractApplicationContext 抽象类
- BeanFactory 接口:提供了 getBean(beanName) 和 getBean(beanClass) 两种获取 bean 实例的方法
- AbstractApplicationContext 抽象类:核心是定义了 refresh() 方法,用于容器的初始化流程
注:这里只实现了 getBean(String beanName) 即通过 beanName 获取 bean,而 getBean(Class<?> beanClass) 通过 Class 获取 bean 还没有写,但逻辑是没差别的。
来看下面这行代码做了什么:
MYApplicationContext context = new MYApplicationContext("classpath:application.properties");
1)在 MYApplicationContext 的构造函数中,将配置文件路径保存到成员变量 configLocations ,然后调用 refresh() 创建并初始化一个 IOC 容器
2)refresh() 创建并初始 IOC 容器大致逻辑如下:
- 定位:加载配置文件,将文件中配置的类/扫描的包的全类名保存下来(PS:在Spring中这部分工作是Resource相关类)
- 加载:根据配置信息,将扫描到的所有类加载成 BeanDefinition(PS:关键信息 beanName、beanClass、isLazyInit、isSingon)
- 注册:将
List<beanDefinition>
根据注册到 beanDefinitionMap<String,BeanDefinition>,key 是 beanName(注:为了 getBean(Class) 这里本来还应该再按 beanClass 注册一次)
3)在 refresh() 的最后会调用 doAutowired() 去进行创建实例和依赖注入,而这些逻辑都是封装在 getBean() 方法中
注:所以,在初始化 IOC 容器时进行 bean 创建和依赖注入是有前提的,即该 bean 不是懒加载(默认非懒加载)
-
创建 bean 实例
- 根据 beanName 获取 BeanDefinition,如果没有 BeanDefinition,则代表该 bean 不是 IOC 容器管理的
- 根据 BeanDefinition 保存的 beanClass 反射创建实例对象
- 如果该 bean 是单例模式,则将该 bean 注册到 factoryBeanObjectCache<String,Object>,key 是 beanName
- 将实例对象包装成 BeanWrapper(包括 instance 和 class),然后注册到 factoryBeanInstanceCache<String,BeanWrapper>,key 是 beanName
-
执行依赖注入 populateBean()
- 获取当前 bean 的所有成员变量,并判断哪些字段标识了 @Autowired 注解
- 对标有 @Autowired 的注解的字段,从 factoryBeanInstanceCache 获取 bean 实例,然后通过反射进行注入(注:@Autowired 本来应该是按照 class 注入,但是这里没实现,所以使用的 byName 注入)
-
通过 factoryBeanInstanceCache#get(beanName) 返回目标对象
注:无论是单例还是多例,每次 getBean() 都会创建一个新的 BeanWrapper,然后放到 factoryBeanInstanceCache
- 对于单例,在创建 bean 实例时,之前创建过实例了,则会从 factoryBeanObjectCache 中取(已经完成依赖注入的);而多例则是每次都要重新创建一个 instance,再执行依赖注入(默认单例)
- 虽然都都会再放入 factoryBeanInstanceCache 这个 Map 中,但是 beanName 这个 key 没变,所以会覆盖之前的
再看这行代码又干了什么:
Object bean = context.getBean("myAction");
对于这里的 getBean()
- 由于该 bean 是非懒加载的,所以实例对象其实已经创建好,并且也完成了注入
- 由于该 bean 是单例的,所以会直接将已经创建好并完成注入的 bean 返回