IOC容器初始化的几个步骤
IOC容器的初始化由以下三个步骤构成:
1、BeanDefinition的Resource定位
由ResourceLoader通过统一的Resource接口来完成,Resource对不同形式的BeanDefinition有统一的接口。
比如文件系统中的BeanDefinition信息由FileSystemResourceLoader获取FileSystemResource来代表。
在类路径下的BeanDefinition信息由DefaultResourceLoader 获取的ClassPathResource来代表
2、BeanDefinition的载入解析
载入解析的过程就是把Resource中定义的Bean表示成IOC容器中的内部结构BeanDefinition。
3、BeanDefinition在IOC容器中的注册
将BeanDefinition通过BeanDefinitionRegisty注册到IOC容器中去.注册的过程,其实就是讲解析得到的BeanDefinition维护到IOC持有的一个HashMap上。
什么是BeanDefinition
BeanDefinition 就是Resource中定义的Bean在IOC容器中的抽象表示,通过这个数据结构,实现对IOC容器中Bean的管理。
为什么要拿ApplicationContext来讲IOC容器的初始化?
前面了解过编程式初始化IOC容器的方法,简单来说,需要创建一个默认IOC容器,一个Resource对象,一个持有IOC容器引用的BeanDefinitionReader对象,然后用这个BeanDefinitionReader对象来为IOC容器初始化Resource中的BeanDefinition对象。
使用DefaultListableBeanFactory的优势是可以灵活的使用自己的IOC容器,但是缺点就是比较麻烦。
而我们常用的ApplicationContext除了具备普通IOC容器存储Bean的功能,还通过继承和实现,持有ResourceLoader【资源定位加载】,MessageSource【国际化】,ApplicationEventPublisher【事件发布订阅】,具备一些高级的功能,同时简化IOC容器的初始化,因此今天的学习从ApplicationContext的FileSystemXmlApplicationContext来深入。
BeanDefinition的Resource定位
public FileSystemXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
//设置双亲容器
super(parent);
//多个资源定位
setConfigLocations(configLocations);
//是否要初始化
if (refresh) {
//实际的初始化操作
refresh();
}
}
如上,FileSystemXmlApplicationContext支持多个资源定位。
可以通过refresh指定是否要进行容器的初始化。
refresh 方法在AbstractApplicationContext【注意这个方法类继承自DefaultResourceLoader,因而具备资源定位加载的能力】中。
//关注这一行
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
obtainFreshBeanFactory最终调用到AbstractRefreshableApplicationContext的 refreshBeanFactory
protected final void refreshBeanFactory() throws BeansException {
//是否已经有Bean容器
if (hasBeanFactory()) {
//有就销毁并关闭容器
destroyBeans();
closeBeanFactory();
}
try {
//创建容器,可以看到,其实也是创建DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//加载BeanDefinition
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
loadBeandefinition最终调用到AbstractBeanDefinitionReader的loadBeandefinition
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
//通过DefaultResourceLoader的getResource方法来获取资源定位
Resource resource = resourceLoader.getResource(location);
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}
DefaultResourceLoader的getResource方法如下:
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
for (ProtocolResolver protocolResolver : this.protocolResolvers) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
if (location.startsWith("/")) {
return getResourceByPath(location);
}
//类路径下的资源
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
最终调用到getResourceByPath,
//饶了一大圈,确实掉了FileSystemXmlApplicationContext中的getResourceByPath,
得到FileSystemResource对象
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
简而言之:
BeanDefinition的资源定位通过DefaultResourceLoader的getResource实现,对于URI、Classpath下的资源有特殊处理,其他都会走到对应的getResourceByPath去实现个性化的资源定位。
BeanDefinition的载入解析
BeanDefinition的载入解析有XmlBeanDefinitionReader来实现
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
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 inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
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();
}
}
}
loadBeanDefinitions实际调用了doLoadBeanDefinitions
//将资源转成document对象
Document doc = doLoadDocument(inputSource, resource);
//解析并且注册BeanDefinition到容器,返回注册的数量
int count = registerBeanDefinitions(doc, resource);
registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//获取BeanDefinitionDocumentReader对象
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
//用DocumentReader去解析注册对象
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
documentReader.registerBeanDefinitions
最终调用到doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) {
//这个类用来解析document里的spring标签
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
//解析BeanDefinition
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
parseBeanDefinitions 调用到parseDefaultElement,根据不同的标签,走不同的处理方法
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//bean标签,实现Bean的载入
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
最终调用到processBeanDefinition
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//解析得到BeanDefinitionHolder持有BeanDefinition、BeanName以及别名
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
//注册到容器
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
简而言之:
BeanDefinition的载入解析过程可以概括如下:
1、先将Xml的Resource解析为Document对象
2、使用DocumentReader来实现Spring中Bean解析,其中DocumentReader把Bean解析的具体步骤委托BeanDefinitionParserDelegate来做
3、BeanDefinitionParserDelegate解析得到BeanDefinitionHolder对象,
BeanDefinitionHolder持有BeanDefinition、BeanName和别名,然后使用DefaultListableBeanFactory中实现的BeanDefinitionRegistry来进行BeanDefinition的注册。
BeanDefinition在IOC容器中的注册
从上面的BeanDefinition的载入和解析过程中,已经得知BeanDefinition载入的入口,来具体看下这边的注册怎么实现的?
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
//注册BenDefinition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
//注册别名
registry.registerAlias(beanName, alias);
}
}
}
BeanDefinitionRegistry其实就是DefaultListableBeanFactory本身,因为他实现这个接口。
registry.registerBeanDefinition
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
//如果这个bean已存在,是否允许覆盖
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
//把BeanDefinition存入map
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
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;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
如下可知BeanDefinition被维护在一个Map内。
同理别名维护在aliasMap。
简而言之:BeanDefinition的注册过程
1、把BeanDefinition维护进一个容器持有的Map
2、把Alias维护进另一个容器持有的Map