简单容器BeanFactory
BeanFactory接口是Spring中的简单容器。它定义了一个常量和多个bean操作相关的方法。
其中,FACTORY_BEAN_PREFIX定义了访问FactoryBean的前缀。由于通过FactoryBean的beanName获取的对象是FactoryBean生产的对象,而通过&beanName能获取到FactoryBean本身。
- AutowireCapableBeanFactory:可以用来加载第三方的bean,由于无法在第三方提供的bean源代码中将其加入到Spring容器,所以需要这个接口。其中定义了装配bean的策略,比如byName、byType。
- HierarchicalBeanFactory:定义了层级关系, 接口中getParentBeanFactory方法能获取父级容器BeanFactory.
- ListableBeanFactory:能以列表的形式返回bean的信息。该接口的方法多数返回值为数组类型
- ConfigurableListableBeanFactory:定义了能配置容器的方法,比如ignoreDependencyType能忽略某些依赖的注入
- DefaultListableBeanFactory:抽象类,主要实现了容器中一些通用的方法。此外,它还实现了BeanDefinitionRegistry接口,是一个可独立运行的容器。其中定义了一个ConcurrentHashMap的成员变量beanDefinitionMap,这个map就是存放bean的容器。
在整个Spring框架中,只有2个地方定义了beanDefinitionMap,一个是DefaultListableBeanFactory类,一个是SimpleBeanDefinitionRegistry类,Spring默认是使用DefaultListableBeanFactory来存放bean
高级容器ApplicationContext
高级容器 ApplicationContext在BeanFactory的基础上实现了更多的接口,提供了更多的功能。相比于BeanFactory,ApplicationContext是面向Spring框架用户使用的,而BeanFactory更多的是Spring内部使用的容器。
1 | public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, |
- EnvironmentCapable:该接口中的getEnvironment能获取web.xml文件中的信息,比如在使用Spring MVC时,需要获取配置文件存放路径
- ResourcePatternResolver:该接口能解析多个Resource资源文件,支持classpath*的前缀
ApplictionContext接口提供的方法都是”可读”的方法,而ConfigurableApplicationContext接口提供了可以配置ApplicationContext的方法,比如refresh、close等方法。
AbstractApplicationContext类实现了ConfigurableApplicationContext中的通用方法,包括refresh方法:
refresh方法使用了模板方法的设计模式,模板方法设计模式主要包含以下四种方法:
- 模板方法:组合一些方法的执行流程
- 具体方法:具体实现的一些通用方法
- 钩子方法:视情况决定是否执行的方法,子类可以实现,也可以不实现
- 抽象方法:带有abstract关键字,子类必须实现的方法
refresh方法就是模板方法,定义了一系列容器初始化,配置解析的方法。
prepareRefresh方法是具体方法,AbstractApplicationContext提供了该方法的实现。
obtainFreshBeanFactory是抽象方法,AbstractApplicationContext并不做具体实现,该方法由子类实现。
postProcessBeanFactory是钩子方法,子类根据情况,决定实现与否。
1 | public void refresh() throws BeansException, IllegalStateException { |
refresh方法详解
prepareRefresh方法,容器刷新前的准备工作:
主要是记录容器启动的时间,设置容器的状态,存储事件监听器,创建事件集合等。
this指的是ApplicationContext
1 | rotected void prepareRefresh() { |
obtainFreshBeanFactory:
对于xml的方式,在此方法中创建DefaultListableBeanFactory工厂,将BeanDefinition注册到容器中,返回DefaultListableBeanFactory容器。
1 | protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { |
对于注解的方式,真正调用的是GenericApplicationContext方法。
注解方式在创建容器的构造方法中,就已经创建了DefaultListableBeanFactory容器,这里置需要将refreshed标志从false改为true(refreshed是AtomicBoolean类型),设置SerializationId后返回DefaultListableBeanFactory容器即可。
1 | protected final void refreshBeanFactory() throws IllegalStateException { |
prepareBeanFactory(beanFactory);
为DefaultListableBeanFactory设置一些属性,注册一些默认的Bean
1 | protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { |
postProcessBeanFactory(beanFactory);
这是一个钩子方法,允许容器的子类去注册后置处理器。
invokeBeanFactoryPostProcessors(beanFactory);
调用容器注册的容器级别的后置处理器。
registerBeanPostProcessors(beanFactory);
处理完BeanFactoryPostProcessors,接着注册BeanPostProcessors
initMessageSource();
国际化消息处理。
initApplicationEventMulticaster()
初始化ApplicationMulitCaster做为事件的发布者,可以存储所有事件监听者的信息,并根据不同的事件,通知不同的事件监听者
1 | protected void initApplicationEventMulticaster() { |
onRefresh();
预留给ApplicationContext子类用于初始化其他特殊的bean,该方法需要在所有单例bean初始化之前调用.
finishBeanFactoryInitialization()
初始化ConvertService,实例化bean
1 | protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { |
preInstantiateSingletons()
1 | public void preInstantiateSingletons() throws BeansException { |
finishRefresh
清除缓存,发布容器刷新完成的事件等
1 | protected void finishRefresh() { |
xml配置文件注册BeanDefinition流程
configLocation是配置文件的路径。
1 | public ClassPathXmlApplicationContext(String configLocation) throws BeansException { |
this(new String[] {configLocation}, true, null);
refresh默认值是true,也就是容器启动的时候,会调用refresh方法。refresh方法在AbstractApplicationContext类中实现。
1 | public ClassPathXmlApplicationContext( |
1 | public void refresh() throws BeansException, IllegalStateException { |
obtainFreshBeanFactory():
1 | protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { |
refreshBeanFactory():
1 | protected final void refreshBeanFactory() throws BeansException { |
AbstractXmlApplicationContext : loadBeanDefinitions(beanFactory);
创建XmlBeanDefinitionReader来解析xml配置文件。
1 | protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { |
AbstractXmlApplicationContext : loadBeanDefinitions(XmlBeanDefinitionReader reader);
根据reader读取的配置文件,去加载beanDefinition。
1 | protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { |
AbstractBeanDefinitionReader:loadBeanDefinitions(String… locations);
该方法可以加载多个配置文件,实际上就通过for循环调用加载一个配置文件的方法。
1 | public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { |
AbstractBeanDefinitionReader:loadBeanDefinitions(String location);
加载一个配置文件的方法
1 | public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { |
AbstractBeanDefinitionReader:loadBeanDefinitions(String location, @Nullable Set
调用本类的其他重载方法。在这个方法中,获取了resourceLoader。通过resourceLoader的实例类型,判断是加载单个资源还是多个资源,最终调用子类的loadBeanDefinitions(Resource… resources)或者loadBeanDefinitions(Resource resources)方法来加载一个或多个资源。此时已经将配置文件的location转换成了Spring抽象的Resource接口。
1 | public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { |
XmlBeanDefinitionReader:loadBeanDefinitions(Resource resource)
如果设置了编码,使用EncodeResource进行编码
1 | public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { |
1 | public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { |
XmlBeanDefinitionReader:ldoLoadBeanDefinitions(InputSource inputSource, Resource resource)
解析xml,获取document对象。根据document对象和配置文件的resource来注册beanDefinition
1 | protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) |
XmlBeanDefinitionReader:registerBeanDefinitions(Document doc, Resource resource)
创建document的解析对象,调用documentReader.registerBeanDefinitions来注册beanDefinition
1 | public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { |
registerBeanDefinitions由子类实现,因为创建的是DefaultBeanDefinitionDocumentReader对象来解析document,所以使用DefaultBeanDefinitionDocumentReader实现的registerBeanDefinitions方法
1 | public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { |
DefaultBeanDefinitionDocumentReader:doRegisterBeanDefinitions()
1 | protected void doRegisterBeanDefinitions(Element root) { |
DefaultBeanDefinitionDocumentReader:parseBeanDefinitions()
1 | protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { |
DefaultBeanDefinitionDocumentReader:parseDefaultElement
1 | private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { |
DefaultBeanDefinitionDocumentReader:processBeanDefinition
1 | protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { |
调用BeanDefinitionReaderUtils.registerBeanDefinition的方法将bean加入到容器当中:
参数BeanDefinitionRegistry是默认的DefaultListableBeanFactory。
1 | public static void registerBeanDefinition( |
DefaultListableBeanFactory.registerBeanDefinition
1 | public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) |
DefaultListableBeanFactory.updateManualSingletonNames
removeManualSingletonName实际上是调用 updateManualSingletonNames方法。
该方法会先判断是否已经创建bean了,如果已经创建了bean,对原来的容器加锁,重新创建新的单例。因为更新之后可能原来是单例的bean,现在已经不是单例了。
1 | private void updateManualSingletonNames(Consumer<Set<String>> action, Predicate<Set<String>> condition) { |
DefaultListableBeanFactory.resetBeanDefinition
1 | protected void resetBeanDefinition(String beanName) { |
分析到这里,Spring就将在xml配置中的bean标签加入到了容器beanDefinitionMap中,整个过程调用的方法非常多,这是因为Spring为了扩展性,将代码逻辑分成了多个方法。
小结:
- 加载配置文件
- 创建XmlBeanDefinitionReader读取配置文件,获取document对象
- 创建DefaultBeanDefinitionDocumentReader解析document
- import标签:importBeanDefinitionResource
- alias标签:processAliasRegistration
- bean标签:processBeanDefinition
- beans标签:递归处理,重复前3步的处理
- 拿到BeanDefinitionHolder,BeanDefinitionHolder封装了BeanDefinition和beanName。调用BeanDefinitionReaderUtils.registerBeanDefinition方法注册BeanDefinition
- BeanDefinitionReaderUtils调用BeanDefinitionRegistry的registerBeanDefinition方法
- validate(),
- 判断容器中是否已经存在beanName对应的BeanDefinition,如果有已存在的beanName,检查是否有权限,有角色覆盖,有的话就覆盖
- 判断是否已经创建bean,如果已经创建了bean,说明了容器已经在使用了,这次操作是一个增加操作。需要将原来的容器加锁,并更新原来的单例对象(因为现在的对象可能是多例,而不再是单例)
注解方式注册BeanDefinition流程
xml配置文件的BeanDefinition注册都是在refresh方法中的obtainFreshBeanFactory刷新容器时注册的。
1 | ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); |
而注解方式的BeanDefinition注册有3中情况:
- 内置BeanDefinition:在容器的构造函数中被调用的时候就注册到容器
- 用户自定义的标记@Configuration的BeanDefinition:在容器构造函数中调用registry方法注册到容器
- 常规的BeanDefinition(不标记@Configuration注解):在refresh方法中的后置处理器中注册到容器
1 | invokeBeanFactoryPostProcessors(beanFactory); |
1 | public AnnotationConfigApplicationContext(String... basePackages) { |
this();
初始化AnnotatedBeanDefinitionReader,ClassPathBeanDefinitionScanner路径扫描器。
1 | public AnnotationConfigApplicationContext() { |
这里的参数this是指AnnotationConfigApplicationContext本身,它的成员变量中还有一个属性beanFactory,默认是DefaultListableBeanFactory.
与xml的方式对比,基于注解的容器创建beanFactory的时机比较早,这是因为有一些内置BeanDefinition需要先注册到容器中。
在创建AnnotatedBeanDefiniftionReader的时候,就已经将内置BeanDefinition注册到容器当中了。
将AnnotationConfigApplicationContext作为BeanDefinitionRegistry传递给AnnotatedBeanDefinitionReader的构造函数。
1 | public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { |
在AnnotatedBeanDefinitionReader的构造函数中就已经调用registerAnnotationConfigProcessors来注册内部BeanDefiniton了。
1 | public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { |
1 | public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) { |
AnnotationConfigUtils.registerAnnotationConfigProcessors:
主要是使用DefaultListableBeanFactory,将内置BeanDefinition加入到容器当中。
1 | public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( |
在创建AnnotedBeanDefinitionReader时将内置BeanDefiniton加入到容器中后,创建路径扫描器ClassPathBeanDefinitionScanner。
1 | public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { |
useDefaultFilters = true
1 | public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) { |
判断BeanDefinitionRegistry是不是ResourceLoader的实例,如果是的话,就将它当成ResourceLoader
1 | public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, |
设置ClassPathBeanDefinitionScanner的一些属性。
1 | public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, |
在创建好AnnotedBeanDefinitionReader和ClassPathBeanDefinitonScanner之后,调用register方法开始注册标记有@Configuration的BeanDefiniton
1 | public AnnotationConfigApplicationContext(Class<?>... componentClasses) { |
1 | public void register(Class<?>... componentClasses) { |
进入AnnotedBeanDefinitionReader类中执行register方法:
1 | public void register(Class<?>... componentClasses) { |
for循环扫描所有标记@Configuration的类,准备注册到容器中:
1 | public void registerBean(Class<?> beanClass) { |
调用AnnotedBeanDefinitionReader类中的doRegisterBean方法,将Class包装成BeanDefinition,处理类上标记的注解,再调用BeanDefinitionReaderUtils.registerBeanDefinition将BeanDefinitonHolder包装类注册到容器当中,后面的流程与xml的方式是相同的。
1 | private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, |
在调用完register方法将标注@Confiuration的BeanDefinition加入到容器后,会调用refresh方法将普通的BeanDefinition(没有标记@Configuration)注册到容器当中。
当使用xml方式注册BeanDefiniton时,调用的是refresh方法中的obtainFreshBeanFactory将BeanDefinition注册到容器中。
1 | ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); |
而当使用注解方式注册普通的BeanDefiiton时,调用的是refresh方法中的invokeBeanFactoryPostProcessors来将BeanDefinition注册到容器中。
1 | invokeBeanFactoryPostProcessors(beanFactory); |
invokeBeanFactoryPostProcessors方法中调用PostProcessorRegistrationDelegate代理来调用invokeBeanFactoryPostProcessors方法。
1 | protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { |
1 |
如何注册委托给你AnnotationBeanDefinitionRead类的regist方法来实现
doRegisterBean(beanClass, null, null, null, null);
1 | private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, |
如果使用@Component,@Controller,@Service,@Repository注解标记的类,是在refresh方法中才注册到容器当中。
但是与XML的方式不同,XML是在下面一行代码的时候进行注册到容器中:
1 | ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); |
注解的方式是在容器调用其后置处理器的时候,触发对BeanDefinition的注册。
1 | invokeBeanFactoryPostProcessors(beanFactory); |
doGetBean
1 | Object sharedInstance = getSingleton(beanName); |
从三级缓存中获取单例对象,
二级缓存:在一级缓存中获取不到bean实例,且实例正在创建的时候会去二级缓存查找
三级缓存:在二级缓存中获取不到bean实例,且允许循环依赖,会去三级缓存中查找
1 | // allowEarlyReference:是否允许循环依赖,传参进来是true |
如果获取到单例对象,且参数为空,执行if逻辑
这里判断参数是否为空,如果参数不为空,需要赋值操作,就不是直接返回bean了
1 | if (sharedInstance != null && args == null) { |
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
判断bean是不是FactoryBean,一系列检查判断后,是的话调用其getObject方法创建bean实例。
1 | protected Object getObjectForBeanInstance( |
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
调用getObject创建bean对象,考虑是否需要单例…并执行后置处理器方法后,将创建后的bean加入factorybeanObject缓存,下次直接从缓存中获取单例对象。
这里保证单例模式,使用的是双重检查机制。
1 | protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) { |
若scope为prototype,或者是单例模式但是缓存中还不存在,就执行else分支
首先是对循环依赖的判断,如果存在循环依赖,Spring无法处理,直接抛出异常
如果不是循环依赖,且容器存在父容器,则递归去父容器中获取bean实例,递归的方式分几种
- 如果父容器是AbstractBeanFactory的实例,调用其doGetBean方法递归
- 如果父容器不是AbstractBeanFactory的实例
- 参数不为空,委托父容器根据beanName和args进行查找
- 如果参数为空,但getBean传入的Class不为空,委托父容器根据beanName和Class去查找bean实例
- 如果参数和Class都为空,委托父容器根据beanName查找
1 | else { |
如果能从父容器中递归查找出bean实例,就返回bean实例了。如果查找不到,就要开始创建bean实例了
typeCheckOnly是通过传参进来的,typeCheckOnly=true代表只是进行类型检查, 而不创建bean实例
1 | // typeCheckOnly是为了检查getBean是否仅仅是为了类型检查获取bean,而不是创建bean |
创建实例前,需要重新合并BeanDefinition,防止原来的数据改动,并且将已经创建或即将创建的beanName加入到alreadyCreated这个set集合中。而在markBeanAsCreated方法中,先设置好标记位
1 | protected void markBeanAsCreated(String beanName) { |
接下来,从当前容器中获取BeanDefinition实例,如果设置了depend-on属性,递归实例化显示依赖的depend-on实例
1 | // 将父类的BeanDefinition与子类的BeanDefinition进行合并覆盖 |
dependentBeanMap是一个ConcurrencyHashMap,它的key为beanName,values为dependentBeanName
1 | private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64); |
1 | protected boolean isDependent(String beanName, String dependentBeanName) { |
注册依赖关系,我依赖于谁,谁又依赖于我
1 | public void registerDependentBean(String beanName, String dependentBeanName) { |
在递归处理完显示指定的depend-on依赖后,开始创建bean实例,但是bean有不同的scope,有不同的逻辑处理。
我们来看下单例模式的bean实例创建:
调用子类实现的createBean方法创建bean实例
这里又遇到了老朋友getObjectForBeanInstance,它可以根据beanName是否已&开头,来决定使用哪种方法创建bean
- 如果是FactoryBean,则调用FactoryBean的getObject方法创建实例
- 如果是普通的bean,则直接返回
1 | if (mbd.isSingleton()) { |
在创建出bean实例后,对bean进行类型检查后返回。
小结:
- 尝试从缓存中获取bean
- 循环依赖的判断
- 递归去父容器获取bean实例
- 如果父容器中获取不到,从当前容器中获取BeanDefinition实例
- 递归实例化显示依赖的depend-on
- 根据不同的Scope采用不同的策略来创建bean实例
- 对bean进行类型检查