前两篇分析了Spring IoC容器初始化资源文件定位,接下来进入到第二步资源文件解析,该过程就是把用户所定义的bean转换为IoC容器的内部表示,这里又涉及到几个非常重要的接口和类:
- BeanDefinition–>bean的容器内部表示,例如各种bean标签通过解析之后都可以转换为BeanDefinition
- BeanDefinitionReader–>通过给定的Resource对象将资源文件解析为BeanDefinition
- BeanDefinitionParserDelegate–>解析资源文件的委托对象,BeanDefinitionReader并不会亲自解析资源文件,而是委托给BeanDefinitionParserDelegate解析
- BeanDefinitionRegistry–>维护解析到的BeanDefinition,相当于spring的内部一个数据库,通过hashMap来维护注册的BeanDefinition对象
该过程大致分为三步
- 将加载到的资源文件转换为Document对象
- 将资源文件解析委托BeanDefinitionParserDelegate对象进行解析
- 解析默认标签或解析自定义标签
1.资源文件转换为Document对象
public class XmlBeanFactory extends DefaultListableBeanFactory {
//实例化XmlBeanDefinitionReader对象
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
/**
* 通过指定Resource对象创建XmlBeanFactory实例
* Create a new XmlBeanFactory with the given resource,
* which must be parsable using DOM.
* @param resource the XML resource to load bean definitions from
* @throws BeansException in case of loading or parsing errors
*/
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
/**
* 通过指定Resource对象和父BeanFactory创建XmlBeanFactory实例
* Create a new XmlBeanFactory with the given input stream,
* which must be parsable using DOM.
* @param resource the XML resource to load bean definitions from
* @param parentBeanFactory parent bean factory
* @throws BeansException in case of loading or parsing errors
*/
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
//解析xml配置文件,将其转换为IoC容器的内部表示
this.reader.loadBeanDefinitions(resource);
}
}
以this.reader.loadBeanDefinitions(resource);
为入口,开始分析
XmlBeanDefinitionReader–>
//加载BeanDefinition
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
//将资源文件转换为Document对象
Document doc = doLoadDocument(inputSource, resource);
//加载BeanDefinition
return registerBeanDefinitions(doc, resource);
}
//省略catch代码....
}
Document doc = doLoadDocument(inputSource, resource);
的功能就是讲资源文件转换为Document,该过程与普通的xml文件解析原理大致相同,不再进行详细的分析
2.将资源文件解析委托BeanDefinitionParserDelegate对象进行解析
进入到registerBeanDefinitions(doc, resource)
方法继续看…
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建BeanDefinitionDocumentReader对象
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//获取BeanDefinitionRegistry中已经加载到的bean对象个数
int countBefore = getRegistry().getBeanDefinitionCount();
//注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//返回新注册的bean的个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
通过这段代码,可以分析到真正的注册过程,应该在documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
中,继续跟踪代码…
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
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 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);
//解析资源文件
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
再进去到parseBeanDefinitions(root, this.delegate);
方法中
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//spring默认命名空间
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);
}
}
代码跟踪到这里,终于可以看到spring对资源文件的解析了,解析的种类分为两种
- spring默认标签
- 自定义标签
2.1 spring默认标签解析
//默认标签解析
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标签
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//beans标签
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
默认标签解析也分为四种类型:
- import标签
- alias标签
- bean标签
- beans标签
这里边最常见也最重要的是bean标签,这里我们只分析这一种标签
2.2 spring bean标签解析
/**
* 解析bean标签将其转换为definition并注册到BeanDefinitionRegistry
* Process the given bean element, parsing the bean definition and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//解析Element节点,并将转换信息封装到BeanDefinitionHolder
//BeanDefinitionHolder封装了definition,beanName,以及aliases信息
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));
}
}
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
的作用就是解析Element节点,并将转换信息封装到BeanDefinitionHolder,BeanDefinitionHolder封装了definition,beanName,以及aliases信息,接下来的代码就是对bean标签的各种解析,先来看下BeanDefinitionParserDelegate的部分源码
public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";
public static final String TRUE_VALUE = "true";
public static final String FALSE_VALUE = "false";
public static final String DEFAULT_VALUE = "default";
public static final String DESCRIPTION_ELEMENT = "description";
public static final String AUTOWIRE_NO_VALUE = "no";
public static final String AUTOWIRE_BY_NAME_VALUE = "byName";
public static final String AUTOWIRE_BY_TYPE_VALUE = "byType";
public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor";
public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect";
public static final String NAME_ATTRIBUTE = "name";
public static final String BEAN_ELEMENT = "bean";
public static final String META_ELEMENT = "meta";
public static final String ID_ATTRIBUTE = "id";
public static final String PARENT_ATTRIBUTE = "parent";
public static final String CLASS_ATTRIBUTE = "class";
public static final String ABSTRACT_ATTRIBUTE = "abstract";
public static final String SCOPE_ATTRIBUTE = "scope";
private static final String SINGLETON_ATTRIBUTE = "singleton";
public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";
public static final String AUTOWIRE_ATTRIBUTE = "autowire";
public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate";
public static final String PRIMARY_ATTRIBUTE = "primary";
public static final String DEPENDS_ON_ATTRIBUTE = "depends-on";
public static final String INIT_METHOD_ATTRIBUTE = "init-method";
public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method";
public static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean";
public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";
public static final String INDEX_ATTRIBUTE = "index";
public static final String TYPE_ATTRIBUTE = "type";
public static final String VALUE_TYPE_ATTRIBUTE = "value-type";
public static final String KEY_TYPE_ATTRIBUTE = "key-type";
public static final String PROPERTY_ELEMENT = "property";
public static final String REF_ATTRIBUTE = "ref";
public static final String VALUE_ATTRIBUTE = "value";
public static final String LOOKUP_METHOD_ELEMENT = "lookup-method";
public static final String REPLACED_METHOD_ELEMENT = "replaced-method";
public static final String REPLACER_ATTRIBUTE = "replacer";
public static final String ARG_TYPE_ELEMENT = "arg-type";
public static final String ARG_TYPE_MATCH_ATTRIBUTE = "match";
public static final String REF_ELEMENT = "ref";
public static final String IDREF_ELEMENT = "idref";
public static final String BEAN_REF_ATTRIBUTE = "bean";
public static final String PARENT_REF_ATTRIBUTE = "parent";
public static final String VALUE_ELEMENT = "value";
public static final String NULL_ELEMENT = "null";
public static final String ARRAY_ELEMENT = "array";
public static final String LIST_ELEMENT = "list";
public static final String SET_ELEMENT = "set";
public static final String MAP_ELEMENT = "map";
public static final String ENTRY_ELEMENT = "entry";
public static final String KEY_ELEMENT = "key";
public static final String KEY_ATTRIBUTE = "key";
public static final String KEY_REF_ATTRIBUTE = "key-ref";
public static final String VALUE_REF_ATTRIBUTE = "value-ref";
public static final String PROPS_ELEMENT = "props";
public static final String PROP_ELEMENT = "prop";
public static final String MERGE_ATTRIBUTE = "merge";
public static final String QUALIFIER_ELEMENT = "qualifier";
public static final String QUALIFIER_ATTRIBUTE_ELEMENT = "attribute";
public static final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init";
public static final String DEFAULT_MERGE_ATTRIBUTE = "default-merge";
public static final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire";
public static final String DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE = "default-autowire-candidates";
public static final String DEFAULT_INIT_METHOD_ATTRIBUTE = "default-init-method";
public static final String DEFAULT_DESTROY_METHOD_ATTRIBUTE = "default-destroy-method";
从这些常量的定义可以发现,spring默认标签里的信息,都在这里有定义,接下来就是通过解析Element节点,把节点中的信息封装至BeanDefinition对象,再将BeanDefinition对象封装至BeanDefinitionHolder返回,然后循环所有节点并解析节点….
解析的代码比较简单,但是对应的标签太多,这里就不粘贴了,大家可以跟踪调试代码阅读
对BeanDefinition的加载就讲到这里了….