目录
xml基础知识
BeanDefinition
ClassPathXmlApplicationContext
XmlBeanDefinitionReader
DefaultDocumentLoader
DefaultBeanDefinitionDocumentReader ->BeanDefinitionParserDelegate
NameSpaceHandler
BeanDefinition
Spring会先将各种途径加载的资源中定义的Bean属性统一转换为BeanDefinition。下面是我列出它的主要方法。
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
/**
* Bean的父类
*/
void setParentName(@Nullable String parentName);
/**
* Bean的类全路径名称
*/
void setBeanClassName(@Nullable String beanClassName);
/**
* Bean的作用域
*/
void setScope(@Nullable String scope);
/**
* 是否懒加载
*/
void setLazyInit(boolean lazyInit);
/**
* 如果是工厂模式加载的Bean,设置它的工厂
*/
void setFactoryBeanName(@Nullable String factoryBeanName);
/**
* 工厂方法模式加载的Bean 设置它的工厂方法
*/
void setFactoryMethodName(@Nullable String factoryMethodName);
/**
* 构造器的参数
*/
ConstructorArgumentValues getConstructorArgumentValues();
/**
* Bean的属性
*/
MutablePropertyValues getPropertyValues();
/**
* 是否是单例
*/
boolean isSingleton();
/**
* 是否是多例
*/
boolean isPrototype();
/**
* 是否抽象类
*/
boolean isAbstract();
}
ClassPathXmlApplicationContext
其构造方法,可以看到 会调用一次refresh方法。
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
// Spring容器的刷新,Spring的主要代码就是从这里开始的
refresh();
}
}
org.springframework.context.support.AbstractApplicationContext#refresh 查看类图
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// 主要看这里,获取一个新鲜的Bean工厂,这也是资源加载和XML解析成BeanDefinition的入口
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 。。。后面先不看
}
}
org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory 查看类图
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 刷新Bean工厂,这里面会调用其子类 AbstractRefreshableApplicationContext#refreshBeanFactory
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory 查看类图
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 创建一个DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 这里交给子类自定义
customizeBeanFactory(beanFactory);
// 主要看这里 这里开始加载Bean的定义,将刚刚创建的beanFactory传下去,用于后面的注册
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory) 查看类图
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// XmlBeanDefinitionReader 解析完xml之后需要注册Bean定义,将beanFactory作为注册表床给它
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// 设置环境,后面解析时会用到,根据不同环境(如开发或测试环境)选择是否加载该xml,
beanDefinitionReader.setEnvironment(this.getEnvironment());
// AbstractApplicationContext 在类图中我们能看到他是继承了ResourceLoader的, 它是由加载资源的能力的, 因此将this传入
beanDefinitionReader.setResourceLoader(this);
// 帮助reader在不联网的情况下能找到我们的本地schama文件
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 允许子类去初始化,在这里默认逻辑只会设置,是否对xml的名称空间进行校验
initBeanDefinitionReader(beanDefinitionReader);
// 配置完XmlBeanDefinitionReader 后开始其加载
loadBeanDefinitions(beanDefinitionReader);
}
ClassPathXmlApplicationContext 的Resource[] 的获取是在后面实现的,因此在这里会根据我们配置的 文件路径进行加载
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
// 我们当前案例在此事还未得到Resource,因此走这条链路,根据location进行load
String[] configLocations = getConfigLocations();
if (configLocations != null) {
// 委托reader进行加载
reader.loadBeanDefinitions(configLocations);
}
}
XmlBeanDefinitionReader
容易调用reader去加载文件,一开始是先调用其父类AbstractBeanDefinitionReader的loadBeanDefinitions(String... locations) --> loadBeanDefinitions(String location)
location可以配置多个,为每个加载每个location,并计数。
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;
}
加载单个location,先获得其Resource[]或 Resource,因为我们的location可能配置的是“classpath*/aa/bb/**.xml” 这种pattern形式,因此每个location会得到多个Resource,并且reader的resourceLoader 其实就是ClassPathXmlApplicationContext,而ClassPathXmlApplicationContext是实现ResourcePatternResolver的,因此会使用ResourcePatternResolver加载多个资源。
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 拿到ResourceLoader 其实就是容器
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
// 判断容器是否是实现了ResourcePatternResolver, 容器是肯定是实现它,需要通过模式匹配加载多个资源
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
// 使用ResourcePatternResolver进行模式匹配形式的加载资源
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 调用子类(xml,Properties 等不同文件形式的子类)进行具体的load
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 {
// 如果不是则直接用resourceLoader进行加载资源
Resource resource = resourceLoader.getResource(location);
// 调用子类(xml,Properties 等不同文件形式的子类)进行具体的load
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
从上面看出AbstractBeanDefinitionReader是一个骨架,它做的是 计数 + 加载资源。BeanDefinition的加载是通过传入Resource给方法BeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)交给子类去实现的。
下面我们介绍xml如何XmlBeanDefinitionReader如何解析xml文件。
下补一下xml的基本知识
上面都是调用AbstractBeanDefinitionReader的,接下来才是其具体文件形式的reader介绍:
XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)。
它需要传入一个EncodedResource ,其实就是Resource加了编码。
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
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);
}
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 {
// 根据Resource获得其文件流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 封装成InputSource
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();
}
}
}
下面代码可以看到,doLoadBeanDefinitions分为两步1、加载Document ,2、针对得到的Document 对解析出beanDefitnion并将其注册
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 加载Document
Document doc = doLoadDocument(inputSource, resource);
// 注册
return registerBeanDefinitions(doc, resource);
}
//后面不用看,不写了 。。。。。
}
先看加载Document, XmlBeanDefinitionReader 将Document 委托给documentLoader(其实现其实是DefaultDocumentLoader
)进行加载。
loadDocument接受几个参数:
- * @param inputSource 我们的资源,里面封装了InputStream的文件流。
- * @param entityResolver XSD的解析者
- * @param errorHandler 错误处理者
- * @param validationMode 校验模式
- * @param namespaceAware 是否感知xml的名称空间
后面四个参数在有简单案例进行解释
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
DefaultDocumentLoader
DefaultDocumentLoader#loadDocument
xml是使用dom工具对xml进行解析的。
/**
* Load a {@link Document document} from the supplied {@link InputSource source}.
* @param inputSource the source of the document that is to be loaded
* @param entityResolver XSD的解析者
* @param errorHandler 错误处理者
* @param validationMode 校验模式
* @param namespaceAware 是否感知xml的名称空间
*/
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
// 主要是factory.setValidating(true) 和 factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
// docBuilder.setEntityResolver(entityResolver)和docBuilder.setErrorHandler(errorHandler)
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
// 解析资源,并将Document返回
return builder.parse(inputSource);
}
DefaultBeanDefinitionDocumentReader
好拿到Document 后,针对Document进行BeanDefinition的注册,注册这个工作XmlBeanDefinitionReader又将其委托给了BeanDefinitionDocumentReader 。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 交给 BeanDefinitionDocumentReader 对 Document进行阅读
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
DefaultBeanDefinitionDocumentReader#registerBeanDefinitions:传入Document和reader上下文,其实就是XmlReaderContext,里面封装了一些上文需要用到的,比如注册表(其实就是beanFactory)。对Document 的解析是从其root开始的:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
// 拿到root进行解析
doRegisterBeanDefinitions(root);
}
拿到root后,
1、DefaultBeanDefinitionDocumentReader会判断这个root是不是beans的默认名称空间(空或者等于”http://www.springframework.org/schema/beans")如果是默认名称空间,会对名称空间的一个属性“profile”,进行判断,判断其是否为当前环境(dev,test,uat等),如果该文件不属于当前环境则停止解析。因此spring支持多套环境的不同配置
2、开始对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);
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;
}
DefaultBeanDefinitionDocumentReader#parseBeanDefinitions。这里再次判断root是否是默认名称空间的,如果是则解析,如果不是则调用自定义解析。并且解析的时候对每个node都会判断是否属于名称空间,如果是则解析,如果不是则调用自定义解析
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 如果root是默认名称空间的
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);
}
}
先看默认解析,这里判断Element 的名字是不是 import,alias,bean或者beans。调用其对应的解析方法
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是如何解析的。
- 先使用delegate解析得到一个BeanDefinitionHolder,这个BeanDefinitionHolder其实就是封装了(beanName + alias + beanDefinition)。
- 然后再对BeanDefinition进行一次修饰,这里用户可以自定义对得到的
- 调用工具对BeanDefinition进行注册
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 获得BeanDefinitionHolder(beanName + alias + beanDefinition)
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 这里会对bean的属性进行一次修饰,有些属性属于自定义的,这里会拿到它名称空间的HandlerResolver对其解析:this.readerContext.getNamespaceHandlerResolver()
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 最后对beanDefinition进行注册
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));
}
}
BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element)
从代码中看到,id,name的关系,如果我们定义了id,那么将id作为beanName,如果没有定义name,那么将name属性按照",; "进行分割,取第一个作为beanName,其他作为aliases。如果都为空会按照类名为它生成一个beanName
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
//检查beanName和别名的唯一性
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 对bean的其他属性如class, 父类 等进行赋值
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
// 如果没有beanName,readerContext会按照类名和计数的方式为它生成一个beanName
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 创建一个抽象的AbstractBeanDefinition, 为其设置它的Class(如果上下文有classLoader的话)或者className,设置它的parent
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 解析其属性, 是否是单例,是否懒加载等等
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 设置其构造函数参数
parseConstructorArgElements(ele, bd);
// 设置其bean的成本变量,bd.getPropertyValues().addPropertyValue(pv); RuntimeBeanReference 或者TypedStringValue
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 遍历直到解析到property
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}
public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// Should only have one child element: ref, value, list, etc.
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we're looking for.
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
subElement = (Element) node;
}
}
}
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
else if (hasValueAttribute) {
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
else if (subElement != null) {
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
NameSpaceHandler
用于自定义解析,自定义解析,先看一个案例
讲解:
前面代码讲到 DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法开始从root节点对Element进行解析
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 如果root是默认名称空间的
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);
}
}
BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element)的代码
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取自定义名称空间的URL
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
/**
* 这个readerContext 是前面registerBeanDefinitions中传入的: documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
* 这个readerContext 中有一个DefaultNamespaceHandlerResolver 它会根据namespaceUri获得一个我们自定义的NamespaceHandler
*/
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 使用自定义的NamespaceHandler解析 Element
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
看一下这个获取nameSpace的方法
public NamespaceHandler resolve(String namespaceUri) {
// 获取一个 key = namespace, value = NamespaceHandler的map
Map<String, Object> handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
// 如果它实现了NamespaceHandler直接返回如果不是则加载它的Class并实例化返回
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
"] for namespace [" + namespaceUri + "]", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
className + "] for namespace [" + namespaceUri + "]", err);
}
}
}
resolve方法一进来getHandlerMappings 这个handlerMappings放置了所有名称空间对应的handler。getHandlerMappings()是一个懒加载模式。它是阅读handlerMappingsLocation = "META-INF/spring.handlers"这个文件进行加载的。
private Map<String, Object> getHandlerMappings() {
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
synchronized (this) {
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
if (logger.isDebugEnabled()) {
logger.debug("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
}
try {
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
handlerMappings = new ConcurrentHashMap<>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return handlerMappings;
}
NamespaceHandler 有三个方法,可以自定义解析,解析完之后可以自定义修饰一波。
public interface NamespaceHandler {
void init();
@Nullable
BeanDefinition parse(Element element, ParserContext parserContext);
@Nullable
BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
}
他们分配在
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
被调用
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 获得BeanDefinitionHolder(beanName + alias + beanDefinition)
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 这里会对bean的属性进行一次修饰,有些属性属于自定义的,这里会拿到它名称空间的HandlerResolver对其解析:this.readerContext.getNamespaceHandlerResolver()
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 最后对beanDefinition进行注册
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));
}
}