XmlBeanFactory

类结构图

image.png

代码

  1. import org.springframework.beans.factory.BeanFactory;
  2. import org.springframework.beans.factory.xml.XmlBeanFactory;
  3. import org.springframework.core.io.ClassPathResource;
  4. public class XmlBeanFactoryTest {
  5. public static void main(String[] args) {
  6. // 资源加载
  7. ClassPathResource classPathResource = new ClassPathResource("spring-bean.xml");
  8. // XmlBeanFactory 加载资源并解析注册bean
  9. BeanFactory beanFactory = new XmlBeanFactory(classPathResource);
  10. // BeanFactory.getBean();
  11. UserBean userBean = (UserBean) beanFactory.getBean("userBean");
  12. System.out.println(userBean.getName());
  13. }
  14. }

源码解析

XmlBeanFactory解析Xml是使用了XmlBeanDefinitionReader.loadBeanDefinition()方法

  1. @Deprecated
  2. @SuppressWarnings({"serial", "all"})
  3. public class XmlBeanFactory extends DefaultListableBeanFactory {
  4. private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
  5. /**
  6. * Create a new XmlBeanFactory with the given resource,
  7. * which must be parsable using DOM.
  8. * @param resource XML resource to load bean definitions from
  9. * @throws BeansException in case of loading or parsing errors
  10. */
  11. public XmlBeanFactory(Resource resource) throws BeansException {
  12. this(resource, null);
  13. }
  14. /**
  15. * Create a new XmlBeanFactory with the given input stream,
  16. * which must be parsable using DOM.
  17. * @param resource XML resource to load bean definitions from
  18. * @param parentBeanFactory parent bean factory
  19. * @throws BeansException in case of loading or parsing errors
  20. */
  21. public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
  22. super(parentBeanFactory);
  23. this.reader.loadBeanDefinitions(resource);
  24. }
  25. }

this.reader.loadBeanDefinitions(resource);

  1. /**
  2. * Load bean definitions from the specified XML file.
  3. * @param resource the resource descriptor for the XML file
  4. * @return the number of bean definitions found
  5. * @throws BeanDefinitionStoreException in case of loading or parsing errors
  6. */
  7. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  8. return loadBeanDefinitions(new EncodedResource(resource));
  9. }
  10. /**
  11. * Load bean definitions from the specified XML file.
  12. * @param encodedResource the resource descriptor for the XML file,
  13. * allowing to specify an encoding to use for parsing the file
  14. * @return the number of bean definitions found
  15. * @throws BeanDefinitionStoreException in case of loading or parsing errors
  16. */
  17. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  18. Assert.notNull(encodedResource, "EncodedResource must not be null");
  19. if (logger.isInfoEnabled()) {
  20. logger.info("Loading XML bean definitions from " + encodedResource.getResource());
  21. }
  22. // 通过属性来记录已经加载的资源
  23. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  24. if (currentResources == null) {
  25. currentResources = new HashSet<EncodedResource>(4);
  26. this.resourcesCurrentlyBeingLoaded.set(currentResources);
  27. }
  28. if (!currentResources.add(encodedResource)) {
  29. throw new BeanDefinitionStoreException(
  30. "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  31. }
  32. try {
  33. // 从encodedResource已经封装的Resource获取InputStream
  34. InputStream inputStream = encodedResource.getResource().getInputStream();
  35. try {
  36. //InputSource 并不是spring的,而是 org.xml.sax
  37. InputSource inputSource = new InputSource(inputStream);
  38. //如果encodedResource 中的 Encoding 不是 null 则同步设置 InputSource的 Encoding
  39. if (encodedResource.getEncoding() != null) {
  40. inputSource.setEncoding(encodedResource.getEncoding());
  41. }
  42. //加载bean的Definitions 将xml中的信息加载到Definition中,并且在内存中注册的也是key+definitions
  43. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  44. }
  45. finally {
  46. inputStream.close();
  47. }
  48. }
  49. catch (IOException ex) {
  50. throw new BeanDefinitionStoreException(
  51. "IOException parsing XML document from " + encodedResource.getResource(), ex);
  52. }
  53. finally {
  54. currentResources.remove(encodedResource);
  55. if (currentResources.isEmpty()) {
  56. this.resourcesCurrentlyBeingLoaded.remove();
  57. }
  58. }
  59. }

上述源码可能看着比较长,但实际上这里并不是真正解析的地方,在这里做了如下: 1:从encodedResource已经封装的Resource获取InputStream; 2:如果encodedResource 中的 Encoding 不是 null 则同步设置 InputSource的 Encoding; 3:将解析动作委托给doLoadBeanDefinitions实现;

doLoadBeanDefinitions(inputSource, encodedResource.getResource())

  1. /**
  2. * Actually load bean definitions from the specified XML file.
  3. * @param inputSource the SAX InputSource to read from
  4. * @param resource the resource descriptor for the XML file
  5. * @return the number of bean definitions found
  6. * @throws BeanDefinitionStoreException in case of loading or parsing errors
  7. */
  8. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
  9. throws BeanDefinitionStoreException {
  10. try {
  11. int validationMode = getValidationModeForResource(resource);
  12. //加载 Document
  13. Document doc = this.documentLoader.loadDocument(
  14. inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
  15. //注册 bean
  16. return registerBeanDefinitions(doc, resource);
  17. }
  18. catch (BeanDefinitionStoreException ex) {
  19. throw ex;
  20. }
  21. catch (SAXParseException ex) {
  22. throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  23. "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
  24. }
  25. catch (SAXException ex) {
  26. throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  27. "XML document from " + resource + " is invalid", ex);
  28. }
  29. catch (ParserConfigurationException ex) {
  30. throw new BeanDefinitionStoreException(resource.getDescription(),
  31. "Parser configuration exception parsing XML from " + resource, ex);
  32. }
  33. catch (IOException ex) {
  34. throw new BeanDefinitionStoreException(resource.getDescription(),
  35. "IOException parsing XML document from " + resource, ex);
  36. }
  37. catch (Throwable ex) {
  38. throw new BeanDefinitionStoreException(resource.getDescription(),
  39. "Unexpected exception parsing XML document from " + resource, ex);
  40. }
  41. }

当我们看着这个方法的时候,依旧不是真正的解析或注册的方法,在这里只是做了Document的加载,并将后续工作委托给了registerBeanDefinitions

registerBeanDefinitions(doc, resource)

  1. /**
  2. * Register the bean definitions contained in the given DOM document.
  3. * Called by {@code loadBeanDefinitions}.
  4. * <p>Creates a new instance of the parser class and invokes
  5. * {@code registerBeanDefinitions} on it.
  6. * @param doc the DOM document
  7. * @param resource the resource descriptor (for context information)
  8. * @return the number of bean definitions found
  9. * @throws BeanDefinitionStoreException in case of parsing errors
  10. * @see #loadBeanDefinitions
  11. * @see #setDocumentReaderClass
  12. * @see BeanDefinitionDocumentReader#registerBeanDefinitions
  13. */
  14. @SuppressWarnings("deprecation")
  15. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  16. //实例化 BeanDefinitionDocumentReader
  17. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  18. documentReader.setEnvironment(getEnvironment());
  19. //获取之前的beanDefinition加载个数
  20. int countBefore = getRegistry().getBeanDefinitionCount();
  21. //加载xml及注册bean
  22. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  23. //记录本次加载个数
  24. return getRegistry().getBeanDefinitionCount() - countBefore;
  25. }

documentReader.registerBeanDefinitions(doc, createReaderContext(resource))

  1. /**
  2. * This implementation parses bean definitions according to the "spring-beans" XSD
  3. * (or DTD, historically).
  4. * <p>Opens a DOM Document; then initializes the default settings
  5. * specified at the {@code <beans/>} level; then parses the contained bean definitions.
  6. */
  7. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  8. this.readerContext = readerContext;
  9. logger.debug("Loading bean definitions");
  10. //实例化 ReaderContext
  11. Element root = doc.getDocumentElement();
  12. //注册
  13. doRegisterBeanDefinitions(root);
  14. }

registerBeanDefinitions并没有做什么,我们继续看doRegisterBeanDefinitions方法

doRegisterBeanDefinitions(root)

  1. protected void doRegisterBeanDefinitions(Element root) {
  2. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  3. if (StringUtils.hasText(profileSpec)) {
  4. String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
  5. profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  6. if (!getEnvironment().acceptsProfiles(specifiedProfiles)) {
  7. return;
  8. }
  9. }
  10. // Any nested <beans> elements will cause recursion in this method. In
  11. // order to propagate and preserve <beans> default-* attributes correctly,
  12. // keep track of the current (parent) delegate, which may be null. Create
  13. // the new (child) delegate with a reference to the parent for fallback purposes,
  14. // then ultimately reset this.delegate back to its original (parent) reference.
  15. // this behavior emulates a stack of delegates without actually necessitating one.
  16. BeanDefinitionParserDelegate parent = this.delegate;
  17. this.delegate = createDelegate(this.readerContext, root, parent);
  18. //解析前处理, 内容null 留个子类实现
  19. preProcessXml(root);
  20. //解析
  21. parseBeanDefinitions(root, this.delegate);
  22. //解析后处理, 内容null 留个子类实现
  23. postProcessXml(root);
  24. this.delegate = parent;
  25. }

在doRegisterBeanDefinitions方法中验证xml的namespace,最重要的方法是parseBeanDefinitions,parseBeanDefinitions方法进行了解析操作;

parseBeanDefinitions(root, this.delegate);

  1. /**
  2. * Parse the elements at the root level in the document:
  3. * "import", "alias", "bean".
  4. * @param root the DOM root element of the document
  5. */
  6. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  7. if (delegate.isDefaultNamespace(root)) {
  8. NodeList nl = root.getChildNodes();
  9. for (int i = 0; i < nl.getLength(); i++) {
  10. Node node = nl.item(i);
  11. if (node instanceof Element) {
  12. Element ele = (Element) node;
  13. if (delegate.isDefaultNamespace(ele)) {
  14. //对默认标签处理
  15. parseDefaultElement(ele, delegate);
  16. }
  17. else {
  18. //对自定义标签处理
  19. delegate.parseCustomElement(ele);
  20. }
  21. }
  22. }
  23. }
  24. else {
  25. //对自定义标签处理
  26. delegate.parseCustomElement(root);
  27. }
  28. }

parseBeanDefinitions方法中已经开始对标签进行解析,区分默认标签和自定义标签,我们本次只对默认标签的源码进行解析,自定义标签自行DeBug

parseDefaultElement(ele, delegate);

  1. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  2. //解析import标签
  3. if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
  4. importBeanDefinitionResource(ele);
  5. }
  6. //解析alias标签并注册
  7. else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
  8. processAliasRegistration(ele);
  9. }
  10. //解析bean标签并注册
  11. else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
  12. processBeanDefinition(ele, delegate);
  13. }
  14. //解析beans标签
  15. else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
  16. // recurse
  17. doRegisterBeanDefinitions(ele);
  18. }
  19. }

到这里我们可以看到,spring对import/bean/alias/beans的解析过程,对于beans的解析无非就是解析beans中的bean标签,spring直接又重新调用了doRegisterBeanDefinitions方法,我们接下来进行对bean标签的解析

processBeanDefinition(ele, delegate);

  1. /**
  2. * Process the given bean element, parsing the bean definition
  3. * and registering it with the registry.
  4. */
  5. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  6. //委托BeanDefinitionParserDelegate的parseBeanDefinitionElement方法进行元素解析并返回
  7. //BeanDefinitionHolder实例,BeanDefinitionHolder已经包含了配置文件中的各种属性
  8. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  9. //当BeanDefinitionHolder返回不null的情况,弱存在默认标签下的子标签再有自定义的属性,还需要再次解析
  10. if (bdHolder != null) {
  11. //解析默认标签中的自定义标签
  12. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  13. try {
  14. // Register the final decorated instance.
  15. // 进行实例注册注册操作是BeanDefinitionReaderUtisl.registerBeanDefinition进行处理
  16. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  17. }
  18. catch (BeanDefinitionStoreException ex) {
  19. getReaderContext().error("Failed to register bean definition with name '" +
  20. bdHolder.getBeanName() + "'", ele, ex);
  21. }
  22. // Send registration event.
  23. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  24. }
  25. }

在processBeanDefinition方法中,spring做了两件事情: 1:委托BeanDefinitionParserDelegate的parseBeanDefinitionElement方法进行元素解析并返回BeanDefinitionHolder实例,BeanDefinitionHolder已经包含了配置文件中的各种属性 2:通过上获得的BeanDefinitionHolder进行bean的注册操作,通BeanDefinitionReaderUtils.registerBeanDefinition方法;

delegate.parseBeanDefinitionElement(ele);

  1. /**
  2. * Parses the supplied {@code &lt;bean&gt;} element. May return {@code null}
  3. * if there were errors during parse. Errors are reported to the
  4. * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
  5. */
  6. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
  7. //解析id属性
  8. String id = ele.getAttribute(ID_ATTRIBUTE);
  9. //解析name属性
  10. String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
  11. //分割name属性
  12. List<String> aliases = new ArrayList<String>();
  13. if (StringUtils.hasLength(nameAttr)) {
  14. String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  15. aliases.addAll(Arrays.asList(nameArr));
  16. }
  17. String beanName = id;
  18. if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
  19. beanName = aliases.remove(0);
  20. if (logger.isDebugEnabled()) {
  21. logger.debug("No XML 'id' specified - using '" + beanName +
  22. "' as bean name and " + aliases + " as aliases");
  23. }
  24. }
  25. if (containingBean == null) {
  26. checkNameUniqueness(beanName, aliases, ele);
  27. }
  28. //将信息封装到 beanDefinition中
  29. AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
  30. if (beanDefinition != null) {
  31. if (!StringUtils.hasText(beanName)) {
  32. try {
  33. //beanname不存在则使用默认规则创建
  34. if (containingBean != null) {
  35. beanName = BeanDefinitionReaderUtils.generateBeanName(
  36. beanDefinition, this.readerContext.getRegistry(), true);
  37. }
  38. else {
  39. beanName = this.readerContext.generateBeanName(beanDefinition);
  40. // Register an alias for the plain bean class name, if still possible,
  41. // if the generator returned the class name plus a suffix.
  42. // This is expected for Spring 1.2/2.0 backwards compatibility.
  43. String beanClassName = beanDefinition.getBeanClassName();
  44. if (beanClassName != null &&
  45. beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
  46. !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
  47. aliases.add(beanClassName);
  48. }
  49. }
  50. if (logger.isDebugEnabled()) {
  51. logger.debug("Neither XML 'id' nor 'name' specified - " +
  52. "using generated bean name [" + beanName + "]");
  53. }
  54. }
  55. catch (Exception ex) {
  56. error(ex.getMessage(), ele);
  57. return null;
  58. }
  59. }
  60. String[] aliasesArray = StringUtils.toStringArray(aliases);
  61. return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
  62. }
  63. return null;
  64. }

在parseBeanDefinitionElement方法中做了三件事: 1:解析id/name; 2:检查name的唯一性; 3:将信息封装到 beanDefinition中,接下来直接看parseBeanDefinitionElement方法;

parseBeanDefinitionElement(ele, beanName, containingBean);

  1. /**
  2. * Parse the bean definition itself, without regard to name or aliases. May return
  3. * {@code null} if problems occurred during the parsing of the bean definition.
  4. */
  5. public AbstractBeanDefinition parseBeanDefinitionElement(
  6. Element ele, String beanName, BeanDefinition containingBean) {
  7. this.parseState.push(new BeanEntry(beanName));
  8. String className = null;
  9. //解析classname属性
  10. if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
  11. className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
  12. }
  13. try {
  14. String parent = null;
  15. //解析parent属性
  16. if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
  17. parent = ele.getAttribute(PARENT_ATTRIBUTE);
  18. }
  19. //创建用于承载属性的AbstractBeanDefinition类型的
  20. AbstractBeanDefinition bd = createBeanDefinition(className, parent);
  21. //解析bean的各种属性
  22. parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
  23. //提取description
  24. bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
  25. //解析meta (元数据)
  26. parseMetaElements(ele, bd);
  27. //解析Lookup-method
  28. parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
  29. //解析replaced-method
  30. parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
  31. //构造函数 参数
  32. //解析constructor-arg
  33. parseConstructorArgElements(ele, bd);
  34. //解析Property
  35. parsePropertyElements(ele, bd);
  36. //解析Qualifier
  37. parseQualifierElements(ele, bd);
  38. bd.setResource(this.readerContext.getResource());
  39. bd.setSource(extractSource(ele));
  40. return bd;
  41. }
  42. catch (ClassNotFoundException ex) {
  43. error("Bean class [" + className + "] not found", ele, ex);
  44. }
  45. catch (NoClassDefFoundError err) {
  46. error("Class that bean class [" + className + "] depends on not found", ele, err);
  47. }
  48. catch (Throwable ex) {
  49. error("Unexpected failure during bean definition parsing", ele, ex);
  50. }
  51. finally {
  52. this.parseState.pop();
  53. }
  54. return null;
  55. }

通过上述代码我们可以看到这里首先是实例化了一个AbstractBeanDefinition来承载各种xml属性,接下来通过parseBeanDefinitionAttributes方法解析了xml中的各种你属性值,然后在解析lookUp-method(方法注入),replaced-method(替换方法或方法返回值),构造函数参数constructor-arg,property属性,Qualifier属性等;上述方法的源码就不一一展示了,无非都是通过Element进行解析;

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

下面代码是上述代码块调用了registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()),真正具体实现如下(使用了DefaultListableBeanFactory类实现的方法)

  1. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  2. throws BeanDefinitionStoreException {
  3. Assert.hasText(beanName, "Bean name must not be empty");
  4. Assert.notNull(beanDefinition, "BeanDefinition must not be null");
  5. if (beanDefinition instanceof AbstractBeanDefinition) {
  6. try {
  7. //校验 MethodOverrides
  8. ((AbstractBeanDefinition) beanDefinition).validate();
  9. }
  10. catch (BeanDefinitionValidationException ex) {
  11. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
  12. "Validation of bean definition failed", ex);
  13. }
  14. }
  15. BeanDefinition oldBeanDefinition;
  16. synchronized (this.beanDefinitionMap) {
  17. oldBeanDefinition = this.beanDefinitionMap.get(beanName);
  18. if (oldBeanDefinition != null) {
  19. if (!this.allowBeanDefinitionOverriding) {
  20. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
  21. "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
  22. "': There is already [" + oldBeanDefinition + "] bound.");
  23. }
  24. else {
  25. if (this.logger.isInfoEnabled()) {
  26. this.logger.info("Overriding bean definition for bean '" + beanName +
  27. "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
  28. }
  29. }
  30. }
  31. else {
  32. // 增加beanDefinitionNames
  33. this.beanDefinitionNames.add(beanName);
  34. // 清除缓存
  35. this.frozenBeanDefinitionNames = null;
  36. }
  37. // 仍处于启动注册阶段
  38. // 注册 beanDefinitionMap 新实例 beanName + beanDefinitio
  39. this.beanDefinitionMap.put(beanName, beanDefinition);
  40. }
  41. if (oldBeanDefinition != null || containsSingleton(beanName)) {
  42. resetBeanDefinition(beanName);
  43. }
  44. }

上述代码中首先验证了beanName和BeannDefinition不可为空,然后继续校验了MethodOverridesMethodOverrides在解析并组装beanDefinition时lookup-method和recpse-method的源码中有提到,继续判断beanDefinitionMap是否存在该bean,如果bean已经存在,通过allowBeanDefinitionOverriding属性判断是否可覆盖,反之则抛出异常;如果不存在则需要判断本次是否是第一次注册bean,如果是则初始化beanDefinitionMap后进行put操作,反之直接put beanDefinitionMap完成注册;

https://blog.csdn.net/qq_30257149/article/details/87972291