[关闭]
@huangyichun 2017-07-25T09:29:50.000000Z 字数 16495 阅读 939

Spring 基本Ioc容器的使用与源码分析

Spring


注意该源码基于Spring 3.2版本分析的

Ioc容器的使用

  1. public class MyTestBean {
  2. private String testStr = "testStr";
  3. public String getTestStr() {
  4. return testStr;
  5. }
  6. public void setTestStr(String testStr) {
  7. this.testStr = testStr;
  8. }
  9. }
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean id="myTestBean" class="chapter2.MyTestBean"/>
  7. </beans>
  1. public class BeanFactoryTest {
  2. @Test
  3. public void testSimpleLoad() {
  4. ClassPathResource resource = new ClassPathResource("MyTestBean.xml");
  5. DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
  6. XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
  7. reader.loadBeanDefinitions(resource);
  8. MyTestBean bean = (MyTestBean) beanFactory.getBean("myTestBean");
  9. Assert.assertEquals("testStr", bean.getTestStr());
  10. }
  11. }
  1. 创建Ioc配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息。
  2. 创建一个BeanFactory,这里使用了DefaultListableBeanFactory。
  3. 创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition,通过一个回调配置给BeanFactory。
  4. 从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成。完成整个载入和注册Bean定义之后,需要的Ioc容器就建立起来了。这个时候我们就可以直接使用Ioc容器了。

总体时序图:

Created with Raphaël 2.1.2MyTestBeanMyTestBeanClassPathResourceClassPathResourceDefaultListableBeanFactoryDefaultListableBeanFactoryXmlBeanDefinitionReaderXmlBeanDefinitionReadernew ClassPathResource("MyTestBean.xml")Resourcenew DefaultListableBeanFactory()new XmlBeanDefinitionReader(beanFactory)reader.loadBeanDefinitions(resource)beanFactory

下面对于Bean解析注册进行源码分析

Created with Raphaël 2.1.2XmlBeanDefinitionReaderXmlBeanDefinitionReaderInputSourceInputSourceDocumentLoaderDocumentLoaderBeanDefinitionDocumentReaderBeanDefinitionDocumentReaderBeanDefinitionParserDelegateBeanDefinitionParserDelegateBeanDefinitionReaderUtilBeanDefinitionReaderUtilDefaultListableBeanFactoryDefaultListableBeanFactorynew InputSource()inputSourcedocumentLoader.loadDocument()documentcreateBeanDefinitionDocumentReader()documentReaderparseBeanDefinitionElement()BeanDefinitionHolder()registerBeanDefinition()registerBeanDefinition()

上面是Bean加载,解析,注册的时序图,下面先认识一些类的作用:


XmlBeanDefinitionReader.java

  1. //loadBeanDefinitions的具体实现,而EncodedResource主要用于对资源文件的处理
  2. public int loadBeanDefinitions(EncodedResource encodedResource)
  3. throws BeanDefinitionStoreException {
  4. Assert.notNull(encodedResource, "EncodedResource must not be null");
  5. if (logger.isInfoEnabled()) {
  6. logger.info("Loading XML bean definitions from " + encodedResource.getResource());
  7. }
  8. //检查是否重复加载xml配置
  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. // 调用DefaultResourceLoader的getResources方法完成具体的Resource定位
  19. try {
  20. //从EncodedResource中获取已经封装的Resource对象并再次从Resource中获取inputStream
  21. InputStream inputStream = encodedResource.getResource().getInputStream();
  22. try {
  23. InputSource inputSource = new InputSource(inputStream);
  24. if (encodedResource.getEncoding() != null) {
  25. inputSource.setEncoding(encodedResource.getEncoding());
  26. }
  27. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());//真正的逻辑
  28. }
  29. finally {
  30. inputStream.close();//关闭流
  31. }
  32. }
  33. catch (IOException ex) {
  34. throw new BeanDefinitionStoreException(
  35. "IOException parsing XML document from " + encodedResource.getResource(), ex);
  36. }
  37. finally {
  38. currentResources.remove(encodedResource);
  39. if (currentResources.isEmpty()) {
  40. this.resourcesCurrentlyBeingLoaded.remove();
  41. }
  42. }
  43. }
  44. /**
  45. * 真正的核心处理部分
  46. * 如果不考虑冗余的代码,其实只做三件事:
  47. * 1.获取XML文件的验证模式
  48. * 2.加载XML,并获取Document.
  49. * 3.返回的Document,注册Bean
  50. */
  51. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
  52. throws BeanDefinitionStoreException {
  53. try {
  54. //获取XML验证模式,默认为XSD
  55. int validationMode = getValidationModeForResource(resource);
  56. // 取得XML文件的Document对象, 这个解析过程由DefaultDocumentLoader完成
  57. Document doc = this.documentLoader.loadDocument(
  58. inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
  59. // 启动对BeanDefinition解析的详细过程, 解析过程中会使用到Spring的Bean配置规则
  60. return registerBeanDefinitions(doc, resource);
  61. }
  62. catch (BeanDefinitionStoreException ex) {
  63. throw ex;
  64. }
  65. catch (SAXParseException ex) {
  66. throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  67. "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
  68. }
  69. catch (SAXException ex) {
  70. throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  71. "XML document from " + resource + " is invalid", ex);
  72. }
  73. catch (ParserConfigurationException ex) {
  74. throw new BeanDefinitionStoreException(resource.getDescription(),
  75. "Parser configuration exception parsing XML from " + resource, ex);
  76. }
  77. catch (IOException ex) {
  78. throw new BeanDefinitionStoreException(resource.getDescription(),
  79. "IOException parsing XML document from " + resource, ex);
  80. }
  81. catch (Throwable ex) {
  82. throw new BeanDefinitionStoreException(resource.getDescription(),
  83. "Unexpected exception parsing XML document from " + resource, ex);
  84. }
  85. }

DefaultDocumentLoader.java

  1. public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
  2. ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
  3. DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
  4. if (logger.isDebugEnabled()) {
  5. logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
  6. }
  7. DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
  8. //解析Xml文件,返回Document
  9. return builder.parse(inputSource);
  10. }

通过上面的过程已经完成了XML文件的加载,并且转换成document,接下来是提取和注册Bean。

XmlBeanDefinitonReader.java

  1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  2. //获取DefaultBeanDefinitionDocumentReader对Xml的BeanDefiniton进行解析
  3. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  4. //设置环境变量
  5. documentReader.setEnvironment(this.getEnvironment());
  6. //记录统计前BeanDefinition的加载个数
  7. int countBefore = getRegistry().getBeanDefinitionCount();
  8. //核心代码,加载及注册bean
  9. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  10. //返回本次加载的BeanDefinition个数
  11. return getRegistry().getBeanDefinitionCount() - countBefore;
  12. }

DefaultBeanDefinitionDocumentReader.java

  1. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  2. this.readerContext = readerContext;
  3. logger.debug("Loading bean definitions");
  4. //获取document的root
  5. Element root = doc.getDocumentElement();
  6. doRegisterBeanDefinitions(root);//核心代码
  7. }
  8. //--------------------------------------------------------
  9. protected void doRegisterBeanDefinitions(Element root) {
  10. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  11. if (StringUtils.hasText(profileSpec)) {
  12. Assert.state(this.environment != null, "environment property must not be null");
  13. //处理root的profile属性,Spring存在两套配置方案,开发时和生产时,分别使用<beans profile="dev">...</beans>和<beans profile="product">..</beans>来进行配置
  14. String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  15. if (!this.environment.acceptsProfiles(specifiedProfiles)) {
  16. return;
  17. }
  18. }
  19. //该类主要负责定义解析Element的各种方法
  20. BeanDefinitionParserDelegate parent = this.delegate;
  21. this.delegate = createHelper(readerContext, root, parent);
  22. // 解析Bean定义之前, 增强解析过程的可扩展性
  23. preProcessXml(root);
  24. parseBeanDefinitions(root, this.delegate);//核心代码
  25. // 解析Bean定义之后, 增强解析过程的可扩展性
  26. postProcessXml(root);
  27. this.delegate = parent;
  28. }
  29. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  30. if (delegate.isDefaultNamespace(root)) {
  31. // 获取Document对象根元素的所有子节点并循环解析
  32. NodeList nl = root.getChildNodes();
  33. for (int i = 0; i < nl.getLength(); i++) {
  34. Node node = nl.item(i);
  35. if (node instanceof Element) {
  36. Element ele = (Element) node;
  37. if (delegate.isDefaultNamespace(ele)) {
  38. // 解析Spring的Bean规则默认元素节点
  39. parseDefaultElement(ele, delegate);
  40. }
  41. else {
  42. //解析自定义节点元素
  43. delegate.parseCustomElement(ele);
  44. }
  45. }
  46. }
  47. }
  48. else {
  49. // 解析自定义元素根节点
  50. delegate.parseCustomElement(root);
  51. }
  52. }
  53. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  54. //解析import元素
  55. if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
  56. importBeanDefinitionResource(ele);
  57. }
  58. // 解析alias元素
  59. else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
  60. processAliasRegistration(ele);
  61. }
  62. // 解析bean元素
  63. else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
  64. processBeanDefinition(ele, delegate);//Bean处理的核心代码
  65. }
  66. // 解析内嵌beans元素, 作为根节点递归解析
  67. else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
  68. // 递归解析
  69. doRegisterBeanDefinitions(ele);
  70. }
  71. }

      从代码可直接看出,Spring首先获取Document的根元素,然后取得根元素所有的子节点并循环解析这些子节点;如果子节点在Spring默认的命名空间内,则按照Spring Bean定义规则来解析,否则按照自定义的节点解析。在按照Spring Bean定义规则进行解析的parseDefaultElement方法中,完成了对< import/>、< alias/>、< bean/>、< beans/>等元素的解析。

BeanDefinitionParserDelegate类,主要定义了Spring默认的标签并且提供了Element的解析方法

这里只关注Spring对< bean >元素的解析过程

DefaultBeanDefinitionDocumentReader.java

  1. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  2. // 具体的解析委托给BeanDefinitionParserDelegate来完成
  3. // BeanDefinitionHolder是BeanDefinition的封装类, 封装了BeanDefinition、Bean的名字和别名, 用它来完成向IoC容器注册.
  4. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  5. if (bdHolder != null) {
  6. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  7. try {
  8. // Register the final decorated instance.
  9. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  10. }
  11. catch (BeanDefinitionStoreException ex) {
  12. getReaderContext().error("Failed to register bean definition with name '" +
  13. bdHolder.getBeanName() + "'", ele, ex);
  14. }
  15. // Send registration event.
  16. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  17. }
  18. }

BeanDefinitionParserDeleger.java

  1. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
  2. return parseBeanDefinitionElement(ele, null);
  3. }
  4. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
  5. // 解析Bean定义资源文件中的<Bean>元素,主要处理<Bean>元素的id,name和aliase属性
  6. String id = ele.getAttribute(ID_ATTRIBUTE);
  7. String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
  8. List<String> aliases = new ArrayList<String>();
  9. if (StringUtils.hasLength(nameAttr)) {
  10. String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  11. aliases.addAll(Arrays.asList(nameArr));
  12. }
  13. String beanName = id;
  14. // 如果<Bean>元素中没有配置id属性时, 将别名中的第一个值赋值给beanName
  15. if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
  16. beanName = aliases.remove(0);
  17. if (logger.isDebugEnabled()) {
  18. logger.debug("No XML 'id' specified - using '" + beanName +
  19. "' as bean name and " + aliases + " as aliases");
  20. }
  21. }
  22. if (containingBean == null) {
  23. checkNameUniqueness(beanName, aliases, ele);
  24. }
  25. // 对<bean>元素进行详细解析
  26. AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
  27. if (beanDefinition != null) {
  28. if (!StringUtils.hasText(beanName)) {
  29. try {
  30. if (containingBean != null) {
  31. beanName = BeanDefinitionReaderUtils.generateBeanName(
  32. beanDefinition, this.readerContext.getRegistry(), true);
  33. }
  34. else {
  35. beanName = this.readerContext.generateBeanName(beanDefinition);
  36. //为解析的Bean使用别名注册时, 为了向后兼容(Spring1.2/2.0给别名添加类名后缀)
  37. String beanClassName = beanDefinition.getBeanClassName();
  38. if (beanClassName != null &&
  39. beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
  40. !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
  41. aliases.add(beanClassName);
  42. }
  43. }
  44. if (logger.isDebugEnabled()) {
  45. logger.debug("Neither XML 'id' nor 'name' specified - " +
  46. "using generated bean name [" + beanName + "]");
  47. }
  48. }
  49. catch (Exception ex) {
  50. error(ex.getMessage(), ele);
  51. return null;
  52. }
  53. }
  54. String[] aliasesArray = StringUtils.toStringArray(aliases);
  55. return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
  56. }
  57. return null;
  58. }
  59. //该方法解析Bean生成一个BeanDefinition
  60. public AbstractBeanDefinition parseBeanDefinitionElement(
  61. Element ele, String beanName, BeanDefinition containingBean) {
  62. this.parseState.push(new BeanEntry(beanName));
  63. // 这里只读取<Bean>元素中配置的class名字, 然后载入到BeanDefinition中去
  64. // 只是记录配置的class名字, 并不实例化, 对象的实例化在依赖注入时完成
  65. String className = null;
  66. if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
  67. className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
  68. }
  69. try {
  70. String parent = null;
  71. if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
  72. parent = ele.getAttribute(PARENT_ATTRIBUTE);
  73. }
  74. // 根据<Bean>元素配置的class名称和parent属性值创建BeanDefinition, 为载入Bean定义信息做准备
  75. AbstractBeanDefinition bd = createBeanDefinition(className, parent);
  76. // 对当前<Bean>元素中配置的一些属性进行解析, 如singleton、abstract等
  77. parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
  78. bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
  79. // 对<Bean>元素的meta(元数据)、lookup-method、replaced-method等子元素进行解析
  80. parseMetaElements(ele, bd);
  81. parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
  82. parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
  83. parseConstructorArgElements(ele, bd);// 解析<Bean>元素的构造方法参数
  84. parsePropertyElements(ele, bd);// 解析<Bean>元素的<property>设置
  85. parseQualifierElements(ele, bd);
  86. bd.setResource(this.readerContext.getResource());
  87. bd.setSource(extractSource(ele));
  88. return bd;
  89. }
  90. catch (ClassNotFoundException ex) {
  91. error("Bean class [" + className + "] not found", ele, ex);
  92. }
  93. catch (NoClassDefFoundError err) {
  94. error("Class that bean class [" + className + "] depends on not found", ele, err);
  95. }
  96. catch (Throwable ex) {
  97. error("Unexpected failure during bean definition parsing", ele, ex);
  98. }
  99. finally {
  100. this.parseState.pop();
  101. }
  102. return null;
  103. }

从上面分析可以看出,BeanDefinition就被载入到IoC容器后,就可以在容器中建立了数据映射了,剩下的就是BeanDefinition注册了。

BeanDefinition注册

DefaultBeanDefinitionDocumentReader.java

该方法在上面已经分析过了,主要是将bean元素解析为BeanDefinition,并且将返回的BeanDefinition封装为BeanDefinitionHolder交给Ioc容器进行处理

  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());//注册BeanDefinition
  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. }

BeanDefinitionReaderUtil.java

  1. public static void registerBeanDefinition(
  2. BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
  3. throws BeanDefinitionStoreException {
  4. // Register bean definition under primary name.
  5. String beanName = definitionHolder.getBeanName();
  6. // 向IoC容器注册BeanDefinition
  7. registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
  8. // Register aliases for bean name, if any.
  9. String[] aliases = definitionHolder.getAliases();
  10. if (aliases != null) {
  11. for (String aliase : aliases) {
  12. registry.registerAlias(beanName, aliase);
  13. }
  14. }
  15. }

这里的registry是DefaultListableBeanFactory,我们可以从下面的调用看出
image.png-12.9kB

DefaultListableBeanFactory.java

  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. // 对解析得到的BeanDefinition校验
  6. if (beanDefinition instanceof AbstractBeanDefinition) {
  7. try {
  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. // 注册的过程中需要线程同步, 以保证数据的一致性
  16. synchronized (this.beanDefinitionMap) {
  17. Object 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. this.beanDefinitionNames.add(beanName);
  33. this.frozenBeanDefinitionNames = null;
  34. }
  35. this.beanDefinitionMap.put(beanName, beanDefinition);//将beanDefinition放入map容器中
  36. }
  37. resetBeanDefinition(beanName);
  38. }
  39. //这个是Ioc容器中Bean的存储位置
  40. private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);

以上是简单的Ioc容器bean解析,注册的所有过程。


总结下以上步骤:

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注