IOC初始化之准备工作(定位、加载、注册)
文章目录
Spring基本认识
- 初衷:Spring框架一开始是为了解决开发的复杂性而存在的,所以归根解底他的目标就是四个字简化开发,这四个字几乎贯穿Spring,在学习Spring的过程中会慢慢领悟这四个字的意思。同时,Spring还保证了最小侵入式编程,其中有很多地方都可以使用配置文件来配置Spring,不需要对实际代码有过多侵入,或许这里Spring的注解有一些侵入性,所以我这里用最小侵入式来描述。
- IOC容器:如果将Spring比作舞台,那么Bean就是舞台上的演员,舞台就是为了演员而生,Spring的存在也就是为了Bean而存在,Spring编程思想是面向Bean的(BOP),俗称万物皆为Bean。而控制反转也是相对于IOC来说的,用户将万物的控制权交给了Spring去管理(也就是IOC容器),而用户只需要在配置文件中配置Bean与Bean之间的关系,将它们联系到一起,剩余工作只需要交给Spring去完成,所以Spring也被称为“粘合剂”,Spring是框架的框架这句话一点毛病都没有。
- 依赖注入:谈到依赖注入,首先需要介绍Bean的作用域。
- 单例:Spring中Bean默认作用域
Singleton
,如无设置Lazy-init = true的话,在IOC初始化完毕之后会初始化单例Bean,此时就会触发实例化Bean对象,以及依赖注入。此作用域为注册式单例模式,在Map中维护Bean实例做到单例效果。 - 原型:在IOC容器初始化时不会初始化,在真正getBean方法调用时才去实例化,而且每次getBean得到的原型Bean实例都不是同一个。
- 还有一些例如request、session之类的作用域,这里不多赘述。在取出Bean的过程中,少不了实例化Bean与依赖注入,依赖注入将Bean与Bean联系在一起,就像“注入”一样将Bean的属性(依赖项)设置到Bean实例中,依赖注入是初始化Bean属性和跟其他Bean联系起来的一个重要过程。而依赖注入有三种方式:@Autowired注解方式、Set方式、构造器方式 。
- 单例:Spring中Bean默认作用域
介绍几个重要的类
BeanFactory
Spring 对Bean的操作是典型的工厂模式,上面定义了许多不同的BeanFactory
,都是具有它各种各样不同的功能的,这些BeanFactory
们为操作Bean带来了极大的便利。最底层的DefaultListableBeanFactory
实现了以上所有接口,我们使用的默认的BeanFactory即为此类。看起来这个结构很复杂,但其实我们只需要看这些BeanFactory
接口究竟可以干些什么,就可以大概知道它的作用了。
-
BeanFactory:
public interface BeanFactory { //对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象, //如果需要得到工厂本身,需要转义 String FACTORY_BEAN_PREFIX = "&"; //根据bean的名字,获取在IOC容器中得到bean实例 Object getBean(String name) throws BeansException; //根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。 <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; //提供对bean的检索,看看是否在IOC容器有这个名字的bean boolean containsBean(String name); boolean isPrototype(String name) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException; //得到bean实例的Class类型 @Nullable Class<?> getType(String name) throws NoSuchBeanDefinitionException; //得到bean的别名,如果根据别名检索,那么其原名也会被检索出来 String[] getAliases(String name); }
- getBean:获取Bean实例的方法。
- containsBean:验证IOC容器是否有某个bean的方法。
- getType:获取bean的class类型的方法。
- getAliases:获取bean的别名的方法。
-
ListableBeanFactory:
public interface ListableBeanFactory extends BeanFactory { boolean containsBeanDefinition(String beanName); int getBeanDefinitionCount(); String[] getBeanDefinitionNames(); String[] getBeanNamesForType(ResolvableType type); String[] getBeanNamesForType(@Nullable Class<?> type); String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit); <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException; <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException; String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType); Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException; @Nullable <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType) throws NoSuchBeanDefinitionException; }
- containsBeanDefinition:在IOC容器存放Bean定义的地方是否存在此Bean定义类的方法。
- getBeanNamesForType:根据Class获取IOC容器中此类型的所有BeanName的方法。
-
AutowireCapableBeanFactory(这个BeanFactory定义了很多操作Bean的功能):
public interface AutowireCapableBeanFactory extends BeanFactory { int AUTOWIRE_NO = 0; int AUTOWIRE_BY_NAME = 1; int AUTOWIRE_BY_TYPE = 2; int AUTOWIRE_CONSTRUCTOR = 3; @Deprecated int AUTOWIRE_AUTODETECT = 4; <T> T createBean(Class<T> beanClass) throws BeansException; void autowireBean(Object existingBean) throws BeansException; Object configureBean(Object existingBean, String beanName) throws BeansException; Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException; Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException; void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException; void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException; Object initializeBean(Object existingBean, String beanName) throws BeansException; Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException; Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException; void destroyBean(Object existingBean); <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException; @Nullable Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException; @Nullable Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException; }
- createBean:定义了创建Bean的方法。
- initializeBean:初始化Bean的方法,是一个回调方法。
- applyBeanPostProcessorsBeforeInitialization:处理Bean-init方法初始化之前的方法,是一个回调方法。(此回调与下面的回调的时机都是在Bean实例化与依赖注入之后)
- applyBeanPostProcessorsAfterInitialization:Bean-init方法初始化之后的方法,是一个回调方法。
BeanDefinition
IOC容器中存放了一个描述Bean的类,这个类就是BeanDefinition
,它的最底层实现就是RootBeanDefinition
,在IOC容器初始化的过程中,会以各种不同方式定位资源例如读取配置文件,读取注解方式等,然后加载这些配置信息,将这些Bean信息注册进IOC容器中,而这些Bean信息Spring将其封装为一个类即为BeanDefinition
。
-
BeanDefinition:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; int ROLE_APPLICATION = 0; int ROLE_SUPPORT = 1; int ROLE_INFRASTRUCTURE = 2; void setParentName(@Nullable String parentName); @Nullable String getParentName(); void setBeanClassName(@Nullable String beanClassName); @Nullable String getBeanClassName(); void setScope(@Nullable String scope); @Nullable String getScope(); void setLazyInit(boolean lazyInit); boolean isLazyInit(); void setDependsOn(@Nullable String... dependsOn); @Nullable String[] getDependsOn(); void setAutowireCandidate(boolean autowireCandidate); boolean isAutowireCandidate(); void setPrimary(boolean primary); boolean isPrimary(); void setFactoryBeanName(@Nullable String factoryBeanName); @Nullable String getFactoryBeanName(); void setFactoryMethodName(@Nullable String factoryMethodName); @Nullable String getFactoryMethodName(); ConstructorArgumentValues getConstructorArgumentValues(); default boolean hasConstructorArgumentValues() { return !getConstructorArgumentValues().isEmpty(); } MutablePropertyValues getPropertyValues(); default boolean hasPropertyValues() { return !getPropertyValues().isEmpty(); } // Read-only attributes boolean isSingleton(); boolean isPrototype(); boolean isAbstract(); int getRole(); @Nullable String getDescription(); @Nullable String getResourceDescription(); @Nullable BeanDefinition getOriginatingBeanDefinition(); }
这个接口定义了大部分描述Bean信息需要用到的方法,例如:
- isSingleton/isPrototype:定义了判断Bean作用域是否单例或原型的方法。
- getPropertyValues:定义了获取Bean中属性值方法,在依赖注入时会用到这个方法。
- getDependsOn:获取依赖Bean的名称列表。
- isLazyInit:判断此Bean是否延迟加载。
- getScope:获取Bean的作用域。
ApplicationContext
其中,ApplicationContext
是Spring的上下文,定义了IOC容器初始化的过程,从类结构上可以看到,它也是一个BeanFactory
,还是一个ResourceLoader
资源加载器,同时它还是ApplicationContext
,扩展了IOC容器初始化的过程,在其注册Bean时还是使用上文提到的DefaultListableBeanFactory
这个BeanFactory,而ApplicationContext
存在的意义就是完善(增添)了IOC容器的功能,使获取Bean更加灵活,功能更多样。
可以看到最底层有四种不同的ApplicationContext
,它们各自有各自不同的方法加载Bean,从类名上也都可以看出来,AnnotationConfigWebApplicationContext
与XmlWebApplicationContext
主要用于Web,一种是根据注解的方式加载Bean信息,一种是根据xml文件加载Bean信息。而FileSystemXmlApplicationContext
与ClassPathXmlApplicationContext
一种是从文件系统资源来加载Bean定义,一种是从路径获取加载Bean定义,其实这些ApplicationContext
大同小异,他们都间接继承了AbstractApplicationContext
与其子类,这两个类实现了大部分初始化IOC容器的方法,而那四种ApplicationContext
的区别只在读取Bean资源的方式上,与应用场景不同上,本质的IOC容器初始化原理都是相同的,所以,我们这里随便拿一种ApplicationContext
做分析即可。
Spring容器(定位、加载、注册)
<beans>
<bean id="demo" class="com.test.demo" />
</beans>
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Demo demo = context.getBean("demo");
以上短短的两行代码和一个配置文件,其底层其实做了很多很多操作,这段代码就是我们分析源码的入口。
首先看一下它的构造器做了什么事情:
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
// 如果有父容器,先设置父容器
super(parent);
// 将配置路径存放在一个变量中,后面会解析这个路径的文件
setConfigLocations(configLocations);
if (refresh) {
// 整个IOC容器初始化的入口方法
refresh();
}
}
而主要的入口方法refresh是在父类AbstractApplicationContext
中实现的,在上面提到的四种ApplicationContext
其实最终都会调用父类的refresh的方法,这里也就可以看出来,在初始化IOC容器的过程中它们都是一样的,都是由父类去实现,它们只负责扩展加载Bean方式的不同,Spring把它们的共性初始化IOC容器的过程都提取出来放在AbstractApplicationContext
中,而子类只需负责扩展其他不同的地方,可见其面向对象的思想。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
// 子类的refreshBeanFactory()方法启动
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 为BeanFactory配置容器特性,例如类加载器、事件处理器等
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 为容器的某些子类指定特殊的BeanPost事件处理器
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 调用所有注册的BeanFactoryPostProcessor的Bean
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 为BeanFactory注册BeanPost事件处理器.
// BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 初始化信息源,和国际化相关.
initMessageSource();
// Initialize event multicaster for this context.
// 初始化容器事件传播器.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 调用子类的某些特殊Bean初始化方法
onRefresh();
// Check for listener beans and register them.
// 为事件传播器注册事件监听器.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 初始化所有剩余的单例Bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 初始化容器的生命周期事件处理器,并发布容器的生命周期事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
// 销毁已创建的Bean
destroyBeans();
// Reset 'active' flag.
// 取消refresh操作,重置容器的同步标识.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
这个方法中,定义了初始化ApplicationContext
的主要脉络(流程),在探求IOC初始化过程的定位、加载、注册只需要关注一行代码:
// 告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
// 子类的refreshBeanFactory()方法启动
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
此方法完成了所有Bean的加载,并且把BeanDefinition
注册进BeanFactory
中(此时Bean还未实例化,只是读取了Bean的信息封装成BeanDefinition类而已)
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
refreshBeanFactory方法为抽象方法,具体定义是在子类AbstractRefreshableApplicationContext
中完成,其会创建BeanFactory并且将其赋给父类变量beanFactory,所以下面一行可以将其get到并返回。
@Override
protected final void refreshBeanFactory() throws BeansException {
//如果已经有容器,销毁容器中的bean,关闭容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建IOC容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//对IOC容器进行定制化,如设置启动参数,开启注解的自动装配等
customizeBeanFactory(beanFactory);
//调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
Spring容器在大部分地方都考虑到了所有的情况,可见其严谨性与健壮性,由于情况很多,所以有时候一些方法会显得比较复杂,晦涩难懂这也是很正常的。此方法执行开始判断是否之前就以及初始化容器,如有需要销毁容器的Bean再关闭容器。接着就new出上面提到的DefaultListableBeanFactory
这个BeanFactory
,很多对Bean的操作都是由DefaultListableBeanFactory
完成的。这里loadBeanDefinitions 方法又是由不同子类定义的:
可以回过头回顾一下之前ApplicationContext
中的类图结构,这里将有三种方式去加载Bean,这里也可以看出Spring框架的扩展性了,其利用抽象类的特性将功能细分到不同的子类,实现多态的方法去达到自己想要的目的,增加了扩展性。这里分析AbstractXmlApplicationContext
读取Bean的方式:
//实现父类抽象的载入Bean定义方法
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
//创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean定义资源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
//为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的
//祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此,容器本身也是一个资源加载器
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
//为Bean读取器设置SAX xml解析器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
//当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
initBeanDefinitionReader(beanDefinitionReader);
//Bean读取器真正实现加载的方法
loadBeanDefinitions(beanDefinitionReader);
}
DefaultListableBeanFactory
是一个BeanDefinitionRegistry
,所以可以放入Reader
构造器中初始化,这里将beanFactory
放入Reader
中初始化的原因是接下来会使用Reader
这个类去读取Bean信息,然后Reader这个类会将信息注册进beanFactory
会用到它,所以先封装进Reader之后方便将Bean信息放入beanFactory
中。而正因为DefaultListableBeanFactory
是一个BeanDefinitionRegistry
所以拥有注册Bean的能力。
//Xml Bean读取器加载Bean定义资源
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//获取Bean定义资源的定位
Resource[] configResources = getConfigResources();
if (configResources != null) {
//Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
//的Bean定义资源
reader.loadBeanDefinitions(configResources);
}
//如果子类中获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
//Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
//的Bean定义资源
reader.loadBeanDefinitions(configLocations);
}
}
在这里会进入第二个方法reader.loadBeanDefinitions(configLocations),还记得在ApplicationContext
构造器中setConfigLocations方法吗?在这里就用到了此路径去解析Bean信息。
//重载方法,调用loadBeanDefinitions(String);
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
//获取在IoC容器初始化过程中设置的资源加载器
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
//将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
//加载多个指定位置的Bean定义资源文件
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
//将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
//加载单个指定位置的Bean定义资源文件
Resource resource = resourceLoader.getResource(location);
//委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
在这里,使用资源加载器去加载路径定位资源信息,使其封装为Resource对象,然后委派调用其子类XmlBeanDefinitionReader
的方法loadBeanDefinitions(resource) 加载此资源。
//XmlBeanDefinitionReader加载资源的入口方法
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//将读入的XML资源进行特殊编码处理
return loadBeanDefinitions(new EncodedResource(resource));
}
//这里是载入XML形式Bean定义资源文件方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
//将资源文件转为InputStream的IO流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//从InputStream中得到XML的解析源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//这里是具体的读取过程
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
//关闭从Resource中得到的IO流
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
可以看到,这里会将资源对象先进行编码封装,然后将其转换成IO流对象(Resource中定义的方法),经过一系列封装,最终执行doLoadBeanDefinitions(inputSource, encodedResource.getResource()) 方法,真正的去加载Bean信息。在Spring中,真正做事的方法都是由do开头,Spring将方法封装的很深,有时候很容易迷路。
//从特定XML文件中实际载入Bean定义资源的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//将XML文件转换为DOM对象,解析过程由documentLoader实现
Document doc = doLoadDocument(inputSource, resource);
//这里是启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则
return registerBeanDefinitions(doc, resource);
}
//略...
}
这里将资源转换为一个DOM对象,方便之后Spring根据一些规则去解析Bean信息。
//按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//获得容器中注册的Bean数量
int countBefore = getRegistry().getBeanDefinitionCount();
//解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader只是个接口,
//具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//统计解析的Bean数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
//根据Spring DTD对Bean的定义规则解析Bean定义Document对象
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
//获得XML描述符
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
//获得Document的根元素
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
//具体的解析过程由BeanDefinitionParserDelegate实现,
//BeanDefinitionParserDelegate中定义了Spring Bean定义XML文件的各种元素
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
//略...
//在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性
preProcessXml(root);
//从Document的根元素开始进行Bean定义的Document对象
parseBeanDefinitions(root, this.delegate);
//在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性
postProcessXml(root);
this.delegate = parent;
}
在这里会执行一些自定义解析,我将在之后的关于介绍Spring的扩展的一篇文章中介绍自定义标签的使用,这里只分析Spring自己的标签解析 -> parseBeanDefinitions(root, this.delegate);
//使用Spring的Bean规则从Document的根元素开始进行Bean定义的Document对象
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//Bean定义的Document对象使用了Spring默认的XML命名空间
if (delegate.isDefaultNamespace(root)) {
//获取Bean定义的Document对象根元素的所有子节点
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//获得Document节点是XML元素节点
if (node instanceof Element) {
Element ele = (Element) node;
//Bean定义的Document的元素节点使用的是Spring默认的XML命名空间
if (delegate.isDefaultNamespace(ele)) {
//使用Spring的Bean规则解析元素节点
parseDefaultElement(ele, delegate);
}
else {
//没有使用Spring默认的XML命名空间,则使用用户自定义的解//析规则解析元素节点
delegate.parseCustomElement(ele);
}
}
}
}
else {
//Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的
//解析规则解析Document根节点
delegate.parseCustomElement(root);
}
}
这里进入Spring自定规则解析方法 -> parseDefaultElement(ele, delegate);
//使用Spring的Bean规则解析Document元素节点
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//如果元素节点是<Import>导入元素,进行导入解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//如果元素节点是<Alias>别名元素,进行别名解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
//按照Spring的Bean规则解析元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
//如果是<Beans>标签则递归处理
doRegisterBeanDefinitions(ele);
}
}
可以看到,这里将解析<import>
、<Alias>
、<Bean>
或<Beans>
标签,这里看看是如何解析<Bean>
标签的。
//解析Bean定义资源Document对象的普通元素
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
// BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类
//对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
// BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
//向Spring IOC容器注册解析得到的Bean定义,这是Bean定义向IOC容器注册的入口
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
//在完成向Spring IOC容器注册解析得到的Bean定义之后,发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
这里就是主要封装Bean信息的方法了,这个方法做了两件事:
-
委派模式,委派
delegate
去解析标签,并且它将其结果封装为一个BeanDefinitionHolder
//解析<Bean>元素的入口 @Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return parseBeanDefinitionElement(ele, null); }
感兴趣可以去看看此方法,具体就是将标签中id、name、以及中信息,设置依赖对象与所需资源、配置作用域(默认Singleton单例)、构造方法设置等等等等信息,封装成
Holder
对象返回。 -
使用工具类
BeanDefinitionReaderUtils
的静态方法去注册Bean定义的信息,注意这里将holder注册进了之前提到的Registry中,此Registry即为一开始传入构造器的beanFactory。//将解析的BeanDefinitionHold注册到容器中 public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. //获取解析的BeanDefinition的名称 String beanName = definitionHolder.getBeanName(); //向IOC容器注册BeanDefinition registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. //如果解析的BeanDefinition有别名,向容器为其注册别名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
这里还是回到了
DefaultListableBeanFactory
中的注册方法registerBeanDefinition ://向IOC容器注册解析的BeanDefiniton @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { //略... if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) //注册的过程中需要线程同步,以保证数据的一致性 synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } //检查是否有同名的BeanDefinition已经在IOC容器中注册 if (oldBeanDefinition != null || containsSingleton(beanName)) { //重置所有已经注册过的BeanDefinition的缓存 resetBeanDefinition(beanName); } }
这里省略了一大段判断的代码,只看主要注册的过程。到这里我们可以知道,真正注册其实是在一个
ConcurrentHashMap
中存放Bean信息,Key为BeanName ,Value为BeanDefinition
。/** Map of bean definition objects, keyed by bean name */ //存储注册信息的BeanDefinition private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
并且会更新BeanName列表,在此会以List的形式存放所有Bean的名称:
/** List of bean definition names, in registration order */ private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
总结
到此为止,我们介绍完了IOC容器对资源的定位(根据路径定位资源)、加载(根据一定规则加载资源成为一个可解析的对象)、注册(将封装好了的Bean信息对象存放进BeanFactory中的Map里,并且使用List存放BeanName)。然而,这个过程只是一个开始,此时的Bean还未初始化,BeanFactory中只是存放了Bean的描述定义对象而已,真正初始化是在getBean()方法被调用。
读者是否感觉到很晕,经常不知道自己在哪里,我这里建议在了解大部分流程之后自己去画一张时序图,我以前研究Spring的时候真是看到哪里晕到哪里,都不知道我在哪我是谁了,但是只要根据流程自己去动手画一张时序图,再配合资料我相信你会清晰很多很多。同时还需要读者不断回顾主线脉络,一定要知道常用的几个类究竟是什么,干什么用的,这样才能跟着Spring的思路更容易看懂Spring在这里究竟想做什么。