BeanDefinition是Spring中很重要的一个部分,通过加载bean的配置的,把bean的配置信息转换为BeanDefinition对象,这个对象中就是一个对bean的描述相关信息。beanFactory就可以按照beanDefinition的描述对bean进行创建,beanDefinition 存在于beanFactory工厂中。
XmlBeanFactory是读取Spring配置文件的入口类,我们通过这个类作为入口,一层一层的扒开如何加载bean的配置信息
public class XmlBeanFactory extends DefaultListableBeanFactory {
// XmlBeanDefinitionReaer 作为bean的
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);
// 加载配置核心代码
// 传入xml配置文件路径
this.reader.loadBeanDefinitions(resource);
}
}
1. 加载Xml文件
加载指定的xml文件,通过构造函数指定配置文件
XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
1.1. 配置文件封装
Spring 的配置文件读取是通过ClassPathResource 进行封装的,
2. 资源加载
资源文件的加载是通过XmlBeanDefinitionReader类来实现的,在这个类中有一个loadBeanDefinition()方法。
首先对资源文件使用EncodeResource进行封装
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 读取配置文件
// 在读取配置文件前,将配置文件对象封装成EncodedResource对象,目的是当设置了编码时,Spring会使用相应的编码作为配置文件输入流的编码
return loadBeanDefinitions(new EncodedResource(resource));
}
使用EncodeResource封装资源文件的目的是对资源文件进行编码处理,当设置了编码属性的时候Spring会使用相应的编码作为输入流的编码。当获取输入流时,会根据编码返回输入流。
public Reader getReader() throws IOException {
if (this.charset != null) {
return new InputStreamReader(this.resource.getInputStream(), this.charset);
}
else if (this.encoding != null) {
return new InputStreamReader(this.resource.getInputStream(), this.encoding);
}
else {
return new InputStreamReader(this.resource.getInputStream());
}
}
对资源文件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 {
// 从EncodedResource对象中获取已经封装的Resource对象并再次从Resource中获取InputStream资源文件输入流
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();
}
}
}
加载xml文件
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 加载XML文件,得到对应的Document
Document doc = doLoadDocument(inputSource, resource);
// 根据Document注册Bean信息
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
3. 获取XML的验证模式
在了解XML文件的验证模式之前,先介绍下什么是XML的验证模式。XML的验证模式保证了XML的正确性,比较常见的验证模式有两种:DTD和XSD
3.1. DTD验证模式
DTD(Document Type Definition)文档类型定义,是一种XML约束模式语言,是XML文件的验证机制,属于XML文件组成的一部分。DTD是一种能保证XML文档格式正确的有效方法,可以对比XML文档和DTD文件来看文档是否规范,元素和标签使用是否正确。一个DTD文档包含:元素的定义规则,元素间的关系定义规则,元素可使用的属性,可使用的实体或符号规则。
3.2. XSD验证模式
XML Schema 语言就是XSD(XML Schemas Definition)。XSD 描述了XML文档的结构,可以用一个XSD来验证某个XML文档,以检查XML文档是否符合要求。文档设计者可以根据XSD来指定XML文档所允许的结构和内容,并可检查XML文档是否有效。XSD 本身就是XML文档,它符合XML语法结构,可以用通用的XML解析器来解析。
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
3.3. 验证模式获取
Spring 在加载xml文档之前会先获取xml文档的验证模式,这个验证模式就是通过getValidationModeForResource()方法来获取对应资源的验证模式
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
分析getValidationModeForResource()方法是如何来获取xml的验证模式的。
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = getValidationMode();
// 手动指定验证模式
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
// 未手动指定则自动检测验证模式
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
// Hmm, we didn't get a clear indication... Let's assume XSD,
// since apparently no DTD declaration has been found up until
// detection stopped (before finding the document's root tag).
return VALIDATION_XSD;
}
getValidationModeForResource()方法很简单,里面的逻辑如果指定了验证模式,就使用指定的验证模式。如果没有指定验证模式,就自动是被验证模式。手动指定验证模式可以通过XmlBeanDefinitionReader类中的setValidationMode 来设置。
3.3.1. 手动设置验证模式
// 伪代码
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader();
reader.setValidationMode(XmlValidationModeDetector.VALIDATION_NONE);
3.3.2. 自动识别验证模式
自动识别验证模式的逻辑由detectValidationMode()方法完成,然后交由XmlValidationModeDetector类的detectValidationMode()方法识别验证模式
protected int detectValidationMode(Resource resource) {
if (resource.isOpen()) {
throw new BeanDefinitionStoreException(
"Passed-in Resource [" + resource + "] contains an open stream: " +
"cannot determine validation mode automatically. Either pass in a Resource " +
"that is able to create fresh streams, or explicitly specify the validationMode " +
"on your XmlBeanDefinitionReader instance.");
}
InputStream inputStream;
try {
inputStream = resource.getInputStream();
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
"Did you attempt to load directly from a SAX InputSource without specifying the " +
"validationMode on your XmlBeanDefinitionReader instance?", ex);
}
try {
// 由XmlValidationModeDetector 识别验证模式
return this.validationModeDetector.detectValidationMode(inputStream);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
resource + "]: an error occurred whilst reading from the InputStream.", ex);
}
}
XmlValidationModeDetector类的detectValidationMode()方法
public int detectValidationMode(InputStream inputStream) throws IOException {
// Peek into the file to look for DOCTYPE.
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
boolean isDtdValidated = false;
String content;
while ((content = reader.readLine()) != null) {
// 读取行
content = consumeCommentTokens(content);
// 如果读取的是空行或者注释行略过
if (this.inComment || !StringUtils.hasText(content)) {
continue;
}
if (hasDoctype(content)) {
isDtdValidated = true;
break;
}
// 读取到<开始符号,验证模式一定会在开始符号之前
if (hasOpeningTag(content)) {
// End of meaningful data...
break;
}
}
// 返回验证模式
return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
}
catch (CharConversionException ex) {
// 由调用方决定用哪种验证模式
// 如果获取验证模式异常
return VALIDATION_AUTO;
}
finally {
reader.close();
}
}
4. 获取Document
经过xml文档验证模式的获取工作后,接下来就是加载Document了。读取文档XmlBeanFactoryReader类会委托给DocumentLoader接口去执行,默认实现类是DefaultDocumentLoader
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
// 创建DocumentBuilderFactory
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
// 创建DocumentBuilder
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
// 获取Document
return builder.parse(inputSource);
}
4.1. 创建DocumentBuilderFactory
创建DocumentBuilderFactory 在Spring中没有什么特殊的地方,仅创建一个工厂对象
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
throws ParserConfigurationException {
// 实例化DocumentBulderFactory 对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);
if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
factory.setValidating(true);
// xml文档校验类型是XSD
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
factory.setNamespaceAware(true);
try {
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
}
catch (IllegalArgumentException ex) {
ParserConfigurationException pcex = new ParserConfigurationException(
"Unable to validate using XSD: Your JAXP provider [" + factory +
"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
pcex.initCause(ex);
throw pcex;
}
}
}
return factory;
}
4.2. 创建DocumentBuilder
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
@Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
throws ParserConfigurationException {
// 实例化DocumentBuilder 对象
DocumentBuilder docBuilder = factory.newDocumentBuilder();
// 设置EntityResolver
if (entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);
}
// 异常处理Hander
if (errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);
}
return docBuilder;
}
4.3. 获取EntityResolver
在4.2中创建DocumentBuilder时,会给它赋值EntityResolver。它的获取方式很简单,是在XmlBeanDefinitionReader类中调用DefaultDocumentLoader类时传入的。
EntityResolver的作用是项目本身就可以提供一个如何寻找DTD声明的方法,即由程序来实现如何寻找DTD声明的过程,比如我们将DTD放在项目的某个位置,在实现时直接将此文档读取并返回给SAX即可,这样避免了通过网络来寻找相应的声明。
// XmlBeanDefinitionReader类中获取EntityResolver的方法
protected EntityResolver getEntityResolver() {
if (this.entityResolver == null) {
ResourceLoader resourceLoader = getResourceLoader();
// 指定EntityResolver
if (resourceLoader != null) {
this.entityResolver = new ResourceEntityResolver(resourceLoader);
}
else {
// 默认EntityResolver实现类
this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
}
}
return this.entityResolver;
}
4.3.1. 默认实现DelegatingEntityResolver
DelegatingEntityResolver 作为EntityResolver接口的默认实现,在它的构造方法中创建了两个EntityResolver对象
public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {
// DTD 解析对象
this.dtdResolver = new BeansDtdResolver();
// 从/META-INF/spring.schemas解析
this.schemaResolver = new PluggableSchemaResolver(classLoader);
}
DelegatingEntityResolver 类中解析方法,逻辑很简单就是决定采用哪中解析方式来解析
public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId)
throws SAXException, IOException {
if (systemId != null) {
// DTD解析
if (systemId.endsWith(DTD_SUFFIX)) {
return this.dtdResolver.resolveEntity(publicId, systemId);
}
// META-INF/spring.schemas解析
else if (systemId.endsWith(XSD_SUFFIX)) {
return this.schemaResolver.resolveEntity(publicId, systemId);
}
}
// Fall back to the parser's default behavior.
return null;
}
5. 解析及注册BeanDefinition
在获取了Document后,接下来就是解析Document提取及注册bean。回到第2节中,在doLoadBeanDefinitions()方法中调用完获取Document方法后,仅接着就是调用registerBeanDefinitions()方法来注册bean
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 实例化DefaultBeanDefinitionDocumentReader 对象
// DefaultBeanDefinitionDocumentReader 是BeanDefinitionDocumentReader 接口的实现
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 记录统计前的BeanDefinition 加载个数
int countBefore = getRegistry().getBeanDefinitionCount();
// 加载及注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 本次加载的BeanDefinition 加载个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
registerBeanDefinitions()方法本身没有加载及注册bean方法中,只提取Document的root,具体的注册交给doRegisterBeanDefinitions()方法
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
// 提取root
// 将注册工作交给doRegisterBeanDefinitions()方法
doRegisterBeanDefinitions(doc.getDocumentElement());
}
接着,我们看doRegisterBeanDefinitions()方法是如何真正的解析XML文件的
protected void doRegisterBeanDefinitions(Element root) {
// xml文件解析器
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
// 处理profile属性
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;
}
5.1. profile 属性的使用
在注册bean时,最开始解析的是对PROFILE_ATTRIBUTE解析,关于profile属性的作用是用来区分环境的,比如:dev、sit、sandbox等环境
有了profile这个属性,我们就可以同时在配置文件中部署多套配置,来适应不同的环境,如开发环境和生产环境。方便我们在开发、部署时的切换
5.2. 解析并注册BeanDefinition
处理完profile属性后,就可以进行XML文件的解析,parseBeanDefinitions()是我们解析XML文件的方法
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 判断是否有默认命名空间
// 判断是否默认命名空间还是自定义命名空间的办法,如果和http://www.springframework.org/schema/beans相等就表示默认命名空间,否则是自定义命名空间
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);
}
}
6. 默认标签的解析
在Spring中有默认标签和自定义标签两种,两种标签的解析存在这很大的不同。接下来我们先看默认标签是如何解析的。在5.2中默认标签的解析会交给parseDefaultElement()方法来处理,可以发现会解析4种标签,分别有不同的处理方式
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);
}
}
6.1. import标签的解析
import标签的作用是在xml文件中引入其他的xml文件,这在我们分模块的开发当中是很常见的,将多个模块的bean按模块配置在不同的xml文件中,然后通过import引入这些xml文件。import 标签的解析由importBeanDefinitionResource()方法来完成
protected void importBeanDefinitionResource(Element ele) {
// 获取resource属性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
// 如果resource属性不存在,不做任何处理
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
// 解析系统属性,如:"${user.dir}"
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<>(4);
// 校验location是绝对URI还是相对URI
boolean absoluteLocation = false;
try {
absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
}
catch (URISyntaxException ex) {
// cannot convert to an URI, considering the location relative
// unless it is the well-known Spring prefix "classpath*:"
}
// 如果是绝对URI就直接根据地址加载对应的配置文件
if (absoluteLocation) {
try {
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isTraceEnabled()) {
logger.trace("Imported " + importCount + " bean definitions from URL location [" + location + "]");
}
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
}
}
else {
// 相对路径根据相对地址计算出相对地址
try {
int importCount;
//Resource存在多个子实现时,如VfsResource、FileSystemResource等
// 而每个resource的createRelative 的实现方式是不一样的,这里先使用子类的方法尝试解析
Resource relativeResource = getReaderContext().getResource().createRelative(location);
if (relativeResource.exists()) {
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
else {
// 如果解析不成功,使用默认解析器DefaultNamespaceHandlerResolver进行解析
String baseLocation = getReaderContext().getResource().getURL().toString();
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
if (logger.isTraceEnabled()) {
logger.trace("Imported " + importCount + " bean definitions from relative location [" + location + "]");
}
}
catch (IOException ex) {
getReaderContext().error("Failed to resolve current resource location", ele, ex);
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to import bean definitions from relative location [" + location + "]", ele, ex);
}
}
// 解析后进行监听器激活处理
Resource[] actResArray = actualResources.toArray(new Resource[0]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
6.2. alias标签解析
我们在xml文档中对bean的定义除了用id外,还可以使用alias。alias的作用是为bean提供别名,这样就相当于我们为一个bean定义了多个名称,在Spring容器创建完这个bean后,我们使用其中的任何一个名称就可以获取到该bean
protected void processAliasRegistration(Element ele) {
// 获取bean标签中的beanName
String name = ele.getAttribute(NAME_ATTRIBUTE);
// 获取bean标签中的alias
String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
boolean valid = true;
if (!StringUtils.hasText(name)) {
getReaderContext().error("Name must not be empty", ele);
valid = false;
}
if (!StringUtils.hasText(alias)) {
getReaderContext().error("Alias must not be empty", ele);
valid = false;
}
if (valid) {
try {
// 为bean注册alias
getReaderContext().getRegistry().registerAlias(name, alias);
}
catch (Exception ex) {
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name + "'", ele, ex);
}
// 别名注册后,通知监听器做相应处理
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
6.3. bean标签的解析及注册
processBeanDefinition()方法只干了4件事情
- 解析默认标签,交给BeanDefinitionParserDelegate类的parseBeanDefinitionElement()方法去解析
- 解析默认标签子节点下的自定义标签,交给BeanDefinitionParserDelegate类的decorateBeanDefinitionIfRequired()方法去解析
- 注册BeanDefinition,交给BeanDefinitionReaderUtils类的registerBeanDefinition()方法完成
- 发送注册事件,通知相关监听器
这几件事情都不是由它自己本身完成,而是都交给其他类来完成
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 委托BeanDefinitionParserDelegate类进行元素解析,返回BeanDefinitionHolder
// BeanDefinitionHolder中已经包含了我们在bean标签中定义的id、class、name、alias之类的属性
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 解析默认标签的子节点下的自定义属性
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 注册BeanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 发送注册事件,通知相关监听器
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
6.3.1. 解析BeanDefinition
我们接着看BeanDefinitionParserDelegate类的parseBeanDefinitionElement()方法,是如何解析bean标签
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 解析id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 解析name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 分割name属性
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.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 对其他标签解析,返回GenerBeanDefinition对象
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
// 如果beanName不存在,那么根据Spring中提供的命名规则为当前bean生成beanName
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
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.isTraceEnabled()) {
logger.trace("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);
// 将解析后的bean信息封装到BeanDefinitionHolder中
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
对其他标签解析,统一封装成GenericBeanDefinition对象
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
// 解析class属性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
// 解析parent属性
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 创建用于用于承载属性AbstractBeanDefinition类型的GenericBeanDefinition对象
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 硬编码解析默认bean的各种属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 提取description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析元数据
parseMetaElements(ele, bd);
// 解析lookup-method属性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析replaced-method属性
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析构造函数参数
parseConstructorArgElements(ele, bd);
// 解析property子元素
parsePropertyElements(ele, bd);
// 解析qualifier 子元素
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;
}
6.3.2. AbstractBeanDefinition属性
对其他标签解析完后,我们就已经完成了XML文档到GenericBeanDefinition的转换,也就是说到这里xml中的所有配置都可以在GenericBeanDefinition 实例中找到对应的属性
GenericBeanDefinition 只是子类实现,大部分的通用属性都保存在了AbstractBeanDefinition中。我们可以通过查看xml中bean的配置属性和AbstractBeanDefinition类中的属性做对比知道属性的含义。
6.3.3. 解析默认标签中的自定义标签元素
回到6.3中,在调用解析默认标签方法后,仅接着就是解析默认标签中的自定义标签。默认标签中的自定义标签是由BeanDefinitionParserDelegate类的decorateBeanDefinitionIfRequired()方法来完成
// 解析标签方法
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 解析默认标签,也就是6.3.1中分析的
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 解析默认标签中的自定义标签
// 是我们这里要介绍的
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));
}
}
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = originalDef;
// 遍历所有属性,看看是否由适用于修饰的属性
NamedNodeMap attributes = ele.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
// Decorate based on custom nested elements.
// 遍历所有子节点,看看是否有适用于修饰的子元素
NodeList children = ele.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}
上面的代码中对元素的所有属性以及子节点进行分别调用,都通过decorateIfRequired()方法解析。首先获取属性或者元素的命名空间,以此来判断该元素或者属性是否适用于自定义标签的解析条件,找出自定义类型所对应的NamespaceHandler进行进一步解析
public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
// 自定义标签的命名空间
String namespaceUri = getNamespaceURI(node);
// isDefaultNamespace(namespaceUri) 对非默认标签进行修饰
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
// 根据命名空间找到对应的处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
// 修饰
BeanDefinitionHolder decorated =
handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}
}
else if (namespaceUri.startsWith("http://www.springframework.org/schema/")) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
}
else {
// A custom namespace, not to be handled by Spring - maybe "xml:...".
if (logger.isDebugEnabled()) {
logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
}
}
}
return originalDef;
}
6.3.4. 注册解析的BeanDefinition
对bean解析后得到BeanDefinition后,我们需要将它注册到BeanFactory中,通过processBeanDefinition()方法中调用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())方法进行注册。我们看registerBeanDefinition()方法的代码,对beanDefinition的注册分成根据beanName和alias注册
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 使用beanName作为唯一key,把BeanDefinition注册到beanFactory中
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 如果存在别名,注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
6.3.4.1. 通过beanName注册BeanDefinition
注册bean的本质其实就是将bean的BeanDefinition信息加入到map缓存中,beanName作为map的key,BeanDefinition作为map的value
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");
// 注册前的最后一次校验,这里的校验不同于之前的XML文件校验
// 主要是对于AbstractBeanDefinition属性中的methodOverrides校验
// 校验methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 获取bean的定义信息BeanDefinition
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
// beanName已经被注册
if (existingDefinition != null) {
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 + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else { // beanName 没被注册
if (hasBeanCreationStarted()) {
// 加锁,防止beanDefinitionMap同一时间被多个线程修改
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;
removeManualSingletonName(beanName);
}
}
else {
// bean的定义信息加入map缓存
this.beanDefinitionMap.put(beanName, beanDefinition);
// 记录已经被注册的bean
this.beanDefinitionNames.add(beanName);
// 从手动注册的单实例map缓存中移除bean定义信息
// 如果是直接通过beanFactory工厂对象将对象注册进单实例缓存中,是属于手动注册
// bean的配置中如果有beanName和手动注册的重名,那么删除手动注册
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// 重置所有beanName对应的缓存
// 清楚解析之前留下的对应的beanName 的缓存
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
6.3.4.2. 通过别名注册BeanDefinition
别名注册方法的代码逻辑很清晰
- alias 和 beanName 相同时,删除alias
- alias覆盖处理,若aliasName已经使用并已经指向了另一beanName则需要用户设置进行处理
- alias循环检查。当A->B存在时,若再次出现A->C->B时抛出异常
注册alias
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
// 锁定存放bean别名的Map
// 防止统一时间只允许一个线程操作别名Map
synchronized (this.aliasMap) {
// 如果beanName和别名相同的话,不记录别名,并从map缓存中删除
if (alias.equals(name)) {
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
else {
// 从别名map缓存中获取beanName信息
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// 别名已经存在,不需要进行处理
return;
}
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
if (logger.isInfoEnabled()) {
logger.info("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
// 别名循环检查
// 当A->B存在时,若再次出现A->B->C时候者会抛出异常
checkForAliasCircle(name, alias);
// 将别名加入aliasMap缓存
this.aliasMap.put(alias, name);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
6.4. beans标签的解析及注册
beans标签是一个定义一组bean的标签,可以可以理解为一个beans标签中可以包含多个bean标签。beans标签解析很简单,它是解析代码是复用doRegisterBeanDefinitions()方法。将Element 元素交给该方法执行,递归解析标签。
6.5. ReaderEventListener 监听器
在前面我们对import、alias、bean、beans标签解析完后,会发送一个事件。Spring没有对这个事件有具体的事件,只留了一个空的实现。
// 解析完标签后,发送事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
如果我们在标签解析完后,通过这个事件来做一些业务处理。我们只需要实现ReaderEventListener接口,然后把这个实现类注册进XmlBeanDefinitionReader中。
public class ReaderEventListenerImpl implements ReaderEventListener {
/**
* 解析标签前事件
* @param defaultsDefinition
*/
@Override
public void defaultsRegistered(DefaultsDefinition defaultsDefinition) {
}
/**
* bean 解析完成后事件
* @param componentDefinition
*/
@Override
public void componentRegistered(ComponentDefinition componentDefinition) {
}
/**
* alias 标签解析完成后事件
* @param aliasDefinition
*/
@Override
public void aliasRegistered(AliasDefinition aliasDefinition) {
}
/**
* import 标签解析完成后事件
* @param importDefinition
*/
@Override
public void importProcessed(ImportDefinition importDefinition) {
}
}
把实现类注册进XmlBeanDefinitionRead中,就可以实现在import、alias、bean标签解析完成之后监听消息,做出符合业务逻辑的开发
public static void main(String[] args) {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(new XmlBeanFactory(new ClassPathResource("beans.xml")));
reader.setEventListener(new ReaderEventListenerImpl());
}
}
7. 自定义标签的解析
前面已经介绍了Spring的默认标签是如何解析的,现在我们分下下对于自定义标签Spring是如何解析的,我们先回顾一下自定义标签的入口是从哪里开始的,在DefaultBeanDefinitionDocumentReader类中的parseBeanDefinition()方法
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);
}
}
接着看parseCustomElement()方法是如何解析自定义标签的
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
// containingBd 为父类的bean,对顶层元素的解析应设置为null
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取对应的命名空间
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 根据命名空间找到对应的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;
}
// 调用自定义的NamespaceHaner 进行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
7.1. 获取标签的命名空间
无论是Spring中默认标签还是自定义标签的解析,都是从命名空间开始的。至于如何提取对应元素的命名空间其实并不需要我们亲自去实现,在org.w3c.dom.Node中已经提供了方法供我们直接调用
public String getNamespaceURI(Node node) {
return node.getNamespaceURI();
}
7.2. 提取自定义标签处理器
有了命名空间,我们就可以进行NamespaceHandler的提取了,继续之前parseCustomElement()方法的跟踪,分析NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri),在readerContext初始化的时候其属性namespaceHandlerResolver已经被初始化为DefaultNamespaceHandlerResolver的实例,所以,这里调用resolve方法其实调用的是DefaultNamespaceHandlerResolver类中的方法,我们进入DefaultNamespaceHandlrResolver的resolve()方法进行查看
public NamespaceHandler resolve(String namespaceUri) {
// 获取所有已经配置的handler映射
Map<String, Object> handlerMappings = getHandlerMappings();
// 根据命名空间找到对应的信息
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
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的初始化方法
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);
}
}
}
7.3. 标签解析
通过handler.parse(ele, new ParserContext(this.readerContext, this, containingBd))解析标签,进入到NamespaceHandlerSupport类的parse()方法
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 寻找解析器
BeanDefinitionParser parser = findParserForElement(element, parserContext);
// 标签解析
return (parser != null ? parser.parse(element, parserContext) : null);
}
先寻找元素对应的解析器,然后在调用解析器中的parse()方法
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
// 获取元素名称
// 如:自定义标签:<myname:user> 那么元素名称就是user
String localName = parserContext.getDelegate().getLocalName(element);
// 根据元素名称找到解析器
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
解析方法parse()
public final BeanDefinition parse(Element element, ParserContext parserContext) {
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
String id = resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
parserContext.getReaderContext().error(
"Id is required for element '" + parserContext.getDelegate().getLocalName(element)
+ "' when used as a top-level tag", element);
}
String[] aliases = null;
if (shouldParseNameAsAliases()) {
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
registerBeanDefinition(holder, parserContext.getRegistry());
if (shouldFireEvents()) {
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
}
catch (BeanDefinitionStoreException ex) {
String msg = ex.getMessage();
parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
return null;
}
}
return definition;
}
具体的解析方法交给parseInternal()
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
// 获取自定义标签中的class
// 此时会调用自定义解析器中的getBeanClass方法
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
else {
// 若解析器中没有重写getBeanClass()方法,则尝试检查子类是否重写getBeanClassName()方法
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
if (containingBd != null) {
// 若存在父类,使用父类的scope属性
builder.setScope(containingBd.getScope());
}
if (parserContext.isDefaultLazyInit()) {
// 配置延迟加载
builder.setLazyInit(true);
}
// 调用子类重写的doParse方法进行解析
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
对于自定义标签的解析,需要注意的几个点。
- 定义XSD文件来规范xml文档的格式
- 继承AbstractSingleBeanDefinitionParser 抽象类实现doParse()方法解析自定义标签
- 创建自定义Handler继承至NamespaceHandlerSupport将解析器注册到Spring容器