IOC
容器的初始化过程分为三步骤:Resource
定位、BeanDefinition
的载入和解析,BeanDefinition
注册。
1、Resource 定位。我们一般用外部资源(如xml
文件)来描述 Bean
对象,所以在初始化IOC
容器的第一步就是需要定位这个外部资源。
2、BeanDefinition 的载入和解析。装载就是 BeanDefinition
的载入。BeanDefinitionReader
读取、解析 Resource
资源,也就是将用户定义的 Bean
表示成IOC
容器的内部数据结构:BeanDefinition
。在IOC
容器内部维护着一个 BeanDefinition Map
的数据结构,在配置文件中每一个
都对应着一个BeanDefinition
对象。
3、BeanDefinition 注册。向IOC
容器注册在第二步解析好的 BeanDefinition
,这个过程是通过 BeanDefinitionRegistery
接口来实现的。在IOC
容器内部其实是将第二个过程解析得到的BeanDefinition
注入到一个 HashMap
容器中,IOC
容器就是通过这个HashMap
来维护这些 BeanDefinition
的。在这里需要注意的一点是这个过程并没有完成依赖注入,依赖注入是发生在应用第一次调用 getBean()
向容器索要 Bean
时。当然我们可以通过设置预处理,即对某个Bean
设置 lazyinit
属性,那么这个 Bean
的依赖注入就会在容器初始化的时候完成。
ClassPathResource resource = new ClassPathResource("bean.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
1、ClassPathResource resource = new ClassPathResource("bean.xml");
: 根据 Xml
配置文件创建 Resource
资源对象。ClassPathResource
是 Resource
接口的子类,bean.xml
文件中的内容是我们定义的Bean
信息。
2、DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
创建一个 BeanFactory
。DefaultListableBeanFactory
是 BeanFactory
的一个子类,BeanFactory
作为一个接口,其实它本身是不具有独立使用的功能的,而DefaultListableBeanFactory
则是真正可以独立使用的IOC
容器,它是整个 Spring IOC
的始祖,。
3、XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
:创建 XmlBeanDefinitionReader
读取器,用于载入BeanDefinition
。
4、reader.loadBeanDefinitions(resource);
:开启Bean
的载入和注册进程,完成后的 Bean
放置在 IOC
容器中。
Spring
为了解决资源定位的问题,提供了两个接口:Resource
、ResourceLoader
,其中 Resource
接口是Spring
统一资源的抽象接口,ResourceLoader
则是 Spring
资源加载的统一抽象。
Resource
资源的定位需要Resource
和 ResourceLoader
两个接口互相配合,在上面那段代码中 new ClassPathResource("bean.xml")
为我们定义了资源,那么 ResourceLoader
则是在什么时候初始化的呢?看 XmlBeanDefinitionReader
构造方法:
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
直接调用其父类 AbstractBeanDefinitionReader
构造方法 :
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
// Determine ResourceLoader to use.
if (this.registry instanceof ResourceLoader) {
this.resourceLoader = (ResourceLoader) this.registry;
}
else {
this.resourceLoader = new PathMatchingResourcePatternResolver();
}
// Inherit Environment if possible
if (this.registry instanceof EnvironmentCapable) {
this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
}
else {
this.environment = new StandardEnvironment();
}
}
核心在于 resourceLoader
字段,如果设置了 ResourceLoader
则用设置的,否则使用 PathMatchingResourcePatternResolver
,该类是一个集大成者的 ResourceLoader
。
reader.loadBeanDefinitions(resource);
开启 BeanDefinition
的解析过程。如下:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
在这个方法会将资源 resource
包装成一个 EncodedResource
实例对象,然后调用 loadBeanDefinitions()
方法,而将 Resource
封装成 EncodedResource
主要是为了对 Resource
进行编码,保证内容读取的正确性。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
String filename = encodedResource.getResource().getFilename();
if (StringUtils.endsWithIgnoreCase(filename, ".xml")) {
// 如果是以 xml 结尾,则以标准的xml解析方式去加载 resource 资源
return this.standardXmlBeanDefinitionReader.loadBeanDefinitions(encodedResource);
} else {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Loading Groovy bean definitions from " + encodedResource);
}
Closure<Object> beans = new Closure<Object>(this) {
public Object call(Object... args) {
GroovyBeanDefinitionReader.this.invokeBeanDefiningClosure((Closure)args[0]);
return null;
}
};
Binding binding = new Binding() {
public void setVariable(String name, Object value) {
if (GroovyBeanDefinitionReader.this.currentBeanDefinition != null) {
GroovyBeanDefinitionReader.this.applyPropertyToBeanDefinition(name, value);
} else {
super.setVariable(name, value);
}
}
};
binding.setVariable("beans", beans);
int countBefore = this.getRegistry().getBeanDefinitionCount();
try {
GroovyShell shell = new GroovyShell(this.getBeanClassLoader(), binding);
shell.evaluate(encodedResource.getReader(), "beans");
} catch (Throwable var7) {
throw new BeanDefinitionParsingException(new Problem("Error evaluating Groovy script: " + var7.getMessage(), new Location(encodedResource.getResource()), (ParseState)null, var7));
}
int count = this.getRegistry().getBeanDefinitionCount() - countBefore;
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + count + " bean definitions from " + encodedResource);
}
return count;
}
}
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);
}
// 获取正在被加载的 resources 资源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
// 如果当前加载的 resource 资源之前没有被加载过,则进行保存
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 {
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();
}
}
}
从 encodedResource
源中获取 xml
的解析源,调用 doLoadBeanDefinitions()
执行具体的解析过程。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
// 省略很多catch代码
}
在该方法中主要做两件事:
1、根据 xml
解析源封装成相应的 Document
对象
2、调用 registerBeanDefinitions()
开启 BeanDefinition
的解析注册过程。
调用 doLoadDocument()
会将 Bean
的定义资源转换为 Document
对象。
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
loadDocument()
方法接受五个参数:
Document
的 Resource
源Document
对象的过程的错误XML
名称空间的支持,则为true
。 loadDocument()
在类 DefaultDocumentLoader
中提供了实现,如下:
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
// 创建文件解析工厂
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
// 创建文档解析器
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
// 解析 Spring 的 Bean 定义资源
return builder.parse(inputSource);
}
到这里,就已经将定义的 Bean
资源文件,载入并转换为 Document
对象了,那么下一步就是如何将其解析为 Spring IOC
管理的 Bean
对象并将其注册到容器中。这个过程有方法 registerBeanDefinitions()
实现。如下:
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));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
首先创建BeanDefinition
的解析器 BeanDefinitionDocumentReader
,然后调用 documentReader.registerBeanDefinitions()
开启解析过程,这里使用的是委派模式,具体的实现由子类 DefaultBeanDefinitionDocumentReader
完成。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
// 获得XML描述符
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
// 获得Document的根元素
Element root = doc.getDocumentElement();
// 解析根元素
doRegisterBeanDefinitions(root);
}
从 Document
对象中获取根元素 root
,然后调用 doRegisterBeanDefinitions()
开启真正的解析过程。
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
// 省略部分代码
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
preProcessXml()
、postProcessXml()
为前置、后置增强处理,目前 Spring
中都是空实现, parseBeanDefinitions()
是对根元素 root 的解析注册过程。
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默认的命名空间,则使用用户自定义的解析规则解析
delegate.parseCustomElement(root);
}
}
迭代 root
元素的所有子节点,对其进行判断,若节点为默认命名空间,则调用 parseDefaultElement()
开启默认标签的解析注册过程,否则调用 parseCustomElement()
开启自定义标签的解析注册过程。
若定义的元素节点使用的是Spring
默认命名空间,则调用 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解析注册
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// // 如果元素节点元素,则进行Beans解析
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
对于自定义标签则由 parseCustomElement()
负责解析。
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
获取节点的 namespaceUri
,然后根据该 namespaceuri
获取相对应的 Handler
,调用 Handler
的 parse()
方法即完成自定义标签的解析和注入。
经过上面的解析,则将 Document
对象里面的Bean
标签解析成了一个个的 BeanDefinition
,下一步则是将这些BeanDefinition
注册到IOC
容器中。动作的触发是在解析 Bean
标签完成后,如下:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
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));
}
}
调用 BeanDefinitionReaderUtils.registerBeanDefinition()
注册,其实这里面也是调用 BeanDefinitionRegistry
的 registerBeanDefinition()
来注册 BeanDefinition ,不过最终的实现是在DefaultListableBeanFactory
中实现,如下:
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 省略一堆校验
BeanDefinition oldBeanDefinition;
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 省略一堆 if
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 (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
这段代码最核心的部分是这句 this.beanDefinitionMap.put(beanName, beanDefinition)
,所以注册过程也不是那么的高大上,就是利用一个 ConcurrentHashMap的集合对象来存放,key 是 beanName,value 是 BeanDefinition。
至此,整个IOC
的初始化过程就已经完成了,从 Bean
资源的定位,转换为 Document
对象,接着对其进行解析,最后注册到IOC
容器中,都已经完美地完成了。现在IOC
容器中已经建立了整个 Bean
的配置信息,这些 Bean
可以被检索、使用、维护,他们是控制反转的基础,是后面注入 Bean
的依赖。