SpringFramework的核心:IOC容器的实现------BeanDefinition的载入

Dear 丶 2022-05-14 07:58 334阅读 0赞

上一次我已经讲述了如何定位BeanDefinition的Resource资源,那么现在我想继续讲述一下如何载入这些资源。

关于上节课的具体内容,我画了一个简图

70

在org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(XmlBeanDefinitionReader)

中是这样描述的,我在上一篇文章也已经说过,FileSystemXmlApplicationContext是通过获取xml文件的configLocations来获取BeanDefinition的。但是书上解析的是通过getConfigResources()方法进行获取,所以我决定按照书上讲解为主,以loadBeanDefinitions(resources)为主。

  1. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
  2. Resource[] configResources = getConfigResources();
  3. if (configResources != null) {
  4. reader.loadBeanDefinitions(configResources);
  5. }
  6. String[] configLocations = getConfigLocations();
  7. if (configLocations != null) {
  8. reader.loadBeanDefinitions(configLocations);
  9. }
  10. }

现在根据书上所说,我们来进入org.springframework.beans.factory.xml.XmlBeanDefinitionReader里面来看一下这个类是如何根据Xml来载入BeanDefinition的,我们可以看到,虽然XmlBeanDefinitionReader的父类AbstractBeanDefinitionReader有关于loadBeanDefinitions(Resource… resources)的实现。

但是在这个地方调用的是XmlBeanDefinitionReader里面的loadBeanDefinitions(Resource… resources)方法

我们来看关于这个方法的具体实现代码

  1. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  2. return loadBeanDefinitions(new EncodedResource(resource));
  3. }
  4. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  5. Assert.notNull(encodedResource, "EncodedResource must not be null");
  6. if (logger.isInfoEnabled()) {
  7. logger.info("Loading XML bean definitions from " + encodedResource.getResource());
  8. }
  9. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  10. if (currentResources == null) {
  11. currentResources = new HashSet<EncodedResource>(4);
  12. this.resourcesCurrentlyBeingLoaded.set(currentResources);
  13. }
  14. if (!currentResources.add(encodedResource)) {
  15. throw new BeanDefinitionStoreException(
  16. "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  17. }
  18. try {
  19. /**
  20. *这里得到Xml文件并得到IO的InputSource准备读取
  21. *
  22. */
  23. InputStream inputStream = encodedResource.getResource().getInputStream();
  24. try {
  25. InputSource inputSource = new InputSource(inputStream);
  26. if (encodedResource.getEncoding() != null) {
  27. inputSource.setEncoding(encodedResource.getEncoding());
  28. }
  29. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  30. }
  31. finally {
  32. inputStream.close();
  33. }
  34. }
  35. catch (IOException ex) {
  36. throw new BeanDefinitionStoreException(
  37. "IOException parsing XML document from " + encodedResource.getResource(), ex);
  38. }
  39. finally {
  40. currentResources.remove(encodedResource);
  41. if (currentResources.isEmpty()) {
  42. this.resourcesCurrentlyBeingLoaded.remove();
  43. }
  44. }
  45. }

在这个方法中主要调用了这样两个语句

  1. InputStream inputStream = encodedResource.getResource().getInputStream();
  2. InputSource inputSource = new InputSource(inputStream);

这里主要是获取resource的输入流,并创建inputSource对象获取这个流对resource进行读取。

下面这条语句是这个方法的核心

  1. doLoadBeanDefinitions(inputSource, encodedResource.getResource());

这个方法的实现同样也在XmlBeanDefinitionReader中。

  1. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
  2. throws BeanDefinitionStoreException {
  3. try {
  4. Document doc = doLoadDocument(inputSource, resource);
  5. return registerBeanDefinitions(doc, resource);
  6. }
  7. catch (BeanDefinitionStoreException ex) {
  8. throw ex;
  9. }
  10. catch (SAXParseException ex) {
  11. throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  12. "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
  13. }
  14. catch (SAXException ex) {
  15. throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  16. "XML document from " + resource + " is invalid", ex);
  17. }
  18. catch (ParserConfigurationException ex) {
  19. throw new BeanDefinitionStoreException(resource.getDescription(),
  20. "Parser configuration exception parsing XML from " + resource, ex);
  21. }
  22. catch (IOException ex) {
  23. throw new BeanDefinitionStoreException(resource.getDescription(),
  24. "IOException parsing XML document from " + resource, ex);
  25. }
  26. catch (Throwable ex) {
  27. throw new BeanDefinitionStoreException(resource.getDescription(),
  28. "Unexpected exception parsing XML document from " + resource, ex);
  29. }
  30. }

在这个方法中,我们可以看到我们首先获取了一个Document对象

关于这个方法,也就是如何启动对BeanDefinition解析的详细过程

  1. registerBeanDefinitions(doc, resource);

这个方法同样是在XmlBeanDefinitionReader类中

  1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  2. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  3. int countBefore = getRegistry().getBeanDefinitionCount();
  4. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  5. return getRegistry().getBeanDefinitionCount() - countBefore;
  6. }

BeanDefinition的载入包括两部分

  1. 首先是通过调用XML的解析器得到document对象,但这些document对象并没有按照SpringBean规则进行解析。
  2. 但这些document对象并没有按照Springbean规则进行解析,在完成通用的XML解析以后,才是按照SpringBean规则进行解析的地方。

按照Spring的Bean规则进行解析是在documentReader中进行的,这里使用的 是DefaultBeanDefinitionDocumentReader这个类

  1. private Class<?> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
  2. protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
  3. return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
  4. }

documentReader负责处理BeanDefinition,然后处理的结果交由BeanDefinitionHolder对象来持有。这个BeanDefinitionHolder对象除了持有BeanDefinition对象外,还持有了其他与BeanDefinition的使用相关的信息,比如Bean的名字,别名集合。这个BeanDefinitonHolder的生成是通过对Document文档树的内容进行解析来完成的,可以看到这个解析过程是由BeanDefinitionParserDelegate来实现的。

具体的解析语句是在这条代码中完成的

  1. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

同样是在DefaultBeanDefinitionDocumentReader类中

  1. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  2. this.readerContext = readerContext;
  3. logger.debug("Loading bean definitions");
  4. Element root = doc.getDocumentElement();
  5. doRegisterBeanDefinitions(root);
  6. }
  7. protected void doRegisterBeanDefinitions(Element root) {
  8. BeanDefinitionParserDelegate parent = this.delegate;
  9. this.delegate = createDelegate(getReaderContext(), root, parent);
  10. if (this.delegate.isDefaultNamespace(root)) {
  11. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  12. if (StringUtils.hasText(profileSpec)) {
  13. String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
  14. profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  15. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
  16. if (logger.isInfoEnabled()) {
  17. logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
  18. "] not matching: " + getReaderContext().getResource());
  19. }
  20. return;
  21. }
  22. }
  23. }
  24. preProcessXml(root);
  25. parseBeanDefinitions(root, this.delegate);
  26. postProcessXml(root);
  27. this.delegate = parent;
  28. }
  29. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  30. if (delegate.isDefaultNamespace(root)) {
  31. NodeList nl = root.getChildNodes();
  32. for (int i = 0; i < nl.getLength(); i++) {
  33. Node node = nl.item(i);
  34. if (node instanceof Element) {
  35. Element ele = (Element) node;
  36. if (delegate.isDefaultNamespace(ele)) {
  37. parseDefaultElement(ele, delegate);
  38. }
  39. else {
  40. delegate.parseCustomElement(ele);
  41. }
  42. }
  43. }
  44. }
  45. else {
  46. delegate.parseCustomElement(root);
  47. }
  48. }
  49. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  50. if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
  51. importBeanDefinitionResource(ele);
  52. }
  53. else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
  54. processAliasRegistration(ele);
  55. }
  56. else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
  57. processBeanDefinition(ele, delegate);
  58. }
  59. else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
  60. // recurse
  61. doRegisterBeanDefinitions(ele);
  62. }
  63. }

现在我们获取了一个这个DefaultBeanDefinitionDocumentReader———document读取器。我们来进入这个类查看它的

processBeanDefinition()方法.

  1. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  2. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  3. if (bdHolder != null) {
  4. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  5. try {
  6. // Register the final decorated instance.
  7. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  8. }
  9. catch (BeanDefinitionStoreException ex) {
  10. getReaderContext().error("Failed to register bean definition with name '" +
  11. bdHolder.getBeanName() + "'", ele, ex);
  12. }
  13. // Send registration event.
  14. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  15. }
  16. }

在这个里面我们看到了一个BeanDefintionHolder,这个类相当于是BeanDefinition对象的封装类,封装了BeanDefinition,Bean的名字和别名。用它来完成向IoC容器注册。得到这个BeanDefinitionHolder实际上就意味着BeanDefinition,是通过BeanDefinitionParserDelegate对XML元素的信息按照Spring的Bean规则进行解析得到的。

  1. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

下面是关于BeanDefinitionParserDelegate如何取得BeanDefinitionHolder对象的。

  1. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
  2. String id = ele.getAttribute(ID_ATTRIBUTE);
  3. String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
  4. List<String> aliases = new ArrayList<String>();
  5. if (StringUtils.hasLength(nameAttr)) {
  6. String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  7. aliases.addAll(Arrays.asList(nameArr));
  8. }
  9. String beanName = id;
  10. if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
  11. beanName = aliases.remove(0);
  12. if (logger.isDebugEnabled()) {
  13. logger.debug("No XML 'id' specified - using '" + beanName +
  14. "' as bean name and " + aliases + " as aliases");
  15. }
  16. }
  17. if (containingBean == null) {
  18. checkNameUniqueness(beanName, aliases, ele);
  19. }
  20. AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
  21. if (beanDefinition != null) {
  22. if (!StringUtils.hasText(beanName)) {
  23. try {
  24. if (containingBean != null) {
  25. beanName = BeanDefinitionReaderUtils.generateBeanName(
  26. beanDefinition, this.readerContext.getRegistry(), true);
  27. }
  28. else {
  29. beanName = this.readerContext.generateBeanName(beanDefinition);
  30. // Register an alias for the plain bean class name, if still possible,
  31. // if the generator returned the class name plus a suffix.
  32. // This is expected for Spring 1.2/2.0 backwards compatibility.
  33. String beanClassName = beanDefinition.getBeanClassName();
  34. if (beanClassName != null &&
  35. beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
  36. !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
  37. aliases.add(beanClassName);
  38. }
  39. }
  40. if (logger.isDebugEnabled()) {
  41. logger.debug("Neither XML 'id' nor 'name' specified - " +
  42. "using generated bean name [" + beanName + "]");
  43. }
  44. }
  45. catch (Exception ex) {
  46. error(ex.getMessage(), ele);
  47. return null;
  48. }
  49. }
  50. String[] aliasesArray = StringUtils.toStringArray(aliases);
  51. return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
  52. }
  53. return null;
  54. }

以上就是关于BeanDefinition的解析过程,具体到细节,比如property的注入可以参考原书。

发表评论

表情:
评论列表 (有 0 条评论,334人围观)

还没有评论,来说两句吧...

相关阅读