Bean的定义
开始介绍:BeanFactory是最顶层的接口,它定义了IOC容器的基本功能规范,可以看到xml配置中的某些属性了,比方说单例,多例,类型匹配,别名等。
BeanFactory有三个子类:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。
每个接口都有各自的定义,它主要是为了区分在Spring内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。
- ListableBeanFactory接口表示这些Bean是可列表的。
- HierarchicalBeanFactory表示的是这些Bean是有继承关系的,也就是每个Bean有可能有父Bean。
- AutowireCapableBeanFactory接口定义Bean的自动装配规则。
四个接口共同定义了Bean的集合、Bean之间的关系、以及Bean行为。
从最顶级的BeanFactory开始看起。
- DefaultListableBeanFactory
- XmlBeanDefinitionReader
容器的初始化包括BeanDefinition的Resource定位、载入和注册这三个基本的过程。
XmlBeanFactory来举例吧。
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
可以看出非常简单,两个构造方法,一个变量,资源加载的实现是this.reader.loadBeanDefinitions(resource)。
配置文件处理
XmlBeanDefinitionReader加载数据就是在这里,但是这里有一个super(parentBeanFactory);一直往上跟会发现跟到了父类AbstractAutowireCapableBeanFactory。
public AbstractAutowireCapableBeanFactory() {
super();
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}
ignoreDependencyInterface:他会自动忽略给定接口的自动装配功能。
Spring自动装配:A依赖B,那么当Spring获取A的bean时后如果属性B未初始化,那么他就会自动初始化B;
有些情况下不能初始化B,比方说B实现了BeanNameAware接口;
- Spring是这样描述的:自动装配时忽略给定的依赖接口,典型的应用是通过其他方式解析ApplicationContext注册依赖,类似于BeanFactory通过BeanFactoryAware进行注入或者ApplicationContext通过ApplicationContextAware进行注入。
读取Bean配置文件
继续描述this.reader.loadBeanDefinitions(resource)这个是资源载入的入口。
1.XmlBeanDefinitionReader首先对这个Resource对象用EncodedResource进行封装。
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
2.拿着Resource对象获取输入流,并构造inputSource
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();
}
3.通过构造的InputSource和Resource继续调用函数doLoadBeanDefinitions。继续往里面跟进我们可以发现
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();
}
这里的doLoadBeanDefinitions就是核心处理继续跟进可以发现。
try {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}...
可以看到注册Bean之前做了这些事情,
- 获取xml验证模式。
- 加载xml并获取一个Document对象,
- 然后用这两个对象去注册Bean。
获取XML验证模式:
- xml的验证模式有DTD(document type definition)和XSD(xml schema definition)。
获取Document:
- 采用SAX解析XML,如果是dtd类型的文件,解析器直接在当前路径,如果是xsd的默认到META_INF/Spring.schemas文件夹里找XSD文件加载。
解析注册BeanDifinitions:
通过解析配置获取到Bean的定义之后开始注册Bean。
验证xml模式先不看了,继续往下跟进,直接到注册bean的地方。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 实例化一个Reader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 统计当前BeanDefinition的个数
int countBefore = getRegistry().getBeanDefinitionCount();
// 加载注册Bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 返回本次注册Bean的个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
可以看到registerBeanDefinitions这里就是加载注册Bean的地方,继续跟进下去。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
doRegisterBeanDefinitions(root)这个就是重点了,继续跟进去
protected void doRegisterBeanDefinitions(Element root) {
// 预处理
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);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 解析前处理
preProcessXml(root);
// 处理
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
但是继续跟进去就发现很奇怪的现象。
protected void preProcessXml(Element root) {
}
protected void postProcessXml(Element root) {
}
这里用到了模板方法模式。他是为子类所设计的方法,在Bean解析前后做一些处理,只需要继承这个父类,子类自己去重写就好了。
我们继续往里面跟进。
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 默认命名空间
parseDefaultElement(ele, delegate);
}
else {
// 自定义命名空间
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}
}
到这里就发现一种是自定义命名空间的处理,一种是默认的处理。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
我们走倒数第二个就是最普通的Bean id这种方式装配
接下来进去发现最后容器是一个并发容器ConcurrentHashMap,这样就把Bean放到了Spring容器中了,这就完成了Spring的Bean的注册。