Spring的IoC容器实现原理(一)#loadBeanDefinition

Spring有十几个组件,核心组件为bean(演员)-context(舞台)-core(道具)

bean包装的是object,而object中肯定要有数据,如何给这些数据提供生存环境就是context要解决的问题,对于context来说他就是要发现每个bean之间的关系,为他们建立起来并维护好这些关系。所以context就是一个bean关系的集合,这个关系集合就是我们常说的IOC容器。core组件就是发现、建立和维护每个bean之间的关系所需要的一些工具,把core叫做util更为贴切。 

 

IOC的基础 
下面我们从IOC/AOP开始,它们是Spring平台实现的核心部分;虽然,我们一开始大多只是在这个层面上,做一些配置和外部特性的使用工作,但对这两个核心模块工作原理和运作机制的理解,对深入理解Spring平台,却是至关重要的;因为,它们同时也是Spring其他模块实现的基础。从Spring要做到的目标,也就是从简化Java EE开发的出发点来看,简单的来说,它是通过对POJO开发的支持,来具体实现的;具体的说,Spring通过为应用开发提供基于POJO的开发模式,把应用开发和复杂的Java EE服务,实现解耦,并通过提高单元测试的覆盖率,从而有效的提高整个应用的开发质量。这样一来,实际上,就需要把为POJO提供支持的,各种Java EE服务支持抽象到应用平台中去,去封装起来;而这种封装功能的实现,在Spring中,就是由IOC容器以及AOP来具体提供的,这两个模块,在很大程度上,体现了Spring作为应用开发平台的核心价值。它们的实现,是Rod.Johnson在他的另一本著作《Expert One-on-One J2EE Development without EJB》 中,所提到Without EJB设计思想的体现;同时也深刻的体现了Spring背后的设计理念。 

从更深一点的技术层面上来看,因为Spring是一个基于Java语言的应用平台,如果我们能够对Java计算模型,比如像JVM虚拟机实现技术的基本原理有一些了解,会让我们对Spring实现的理解,更加的深入,这些JVM虚拟机的特性使用,包括像反射机制,代理类,字节码技术等等。它们都是在Spring实现中,涉及到的一些Java计算环境的底层技术;尽管对应用开发人员来说,可能不会直接去涉及这些JVM虚拟机底层实现的工作,但是了解这些背景知识,或多或少,对我们了解整个Spring平台的应用背景有很大的帮助;打个比方来说,就像我们在大学中,学习的那些关于计算机组织和系统方面的基本知识,比如像数字电路,计算机组成原理,汇编语言,操作系统等等这些基本课程的学习。虽然,坦率的来说,对我们这些大多数课程的学习者,在以后的工作中,可能并没有太多的机会,直接从事这么如此底层的技术开发工作;但具备这些知识背景,为我们深入理解基于这些基础技术构架起来的应用系统,毫无疑问,是不可缺少的。随着JVM虚拟机技术的发展,可以设想到的是,更多虚拟机级别的基本特性,将会持续的被应用平台开发者所关注和采用,这也是我们在学习平台实现的过程中,非常值得注意的一点,因为这些底层技术实现,毫无疑问,会对Spring应用平台的开发路线,产品策略产生重大的影响。同时,在使用Spring作为应用平台的时候,如果需要更深层次的开发和性能调优,这些底层的知识,也是我们知识库中不可缺少的部分。有了这些底层知识,理解整个系统,想来就应该障碍不大了。 

IOC的一点认识 
对Spring IOC的理解离不开对依赖反转模式的理解,我们知道,关于如何反转对依赖的控制,把控制权从具体业务对象手中转交到平台或者框架中,是解决面向对象系统设计复杂性和提高面向对象系统可测试性的一个有效的解决方案。这个问题触发了IoC设计模式的发展,是IoC容器要解决的核心问题。同时,也是产品化的IoC容器出现的推动力。而我觉得Spring的IoC容器,就是一个开源的实现依赖反转模式的产品。 

那具体什么是IoC容器呢?它在Spring框架中到底长什么样?说了这么多,其实对IoC容器的使用者来说,我们常常接触到的BeanFactory和ApplicationContext都可以看成是容器的具体表现形式。这些就是IoC容器,或者说在Spring中提IoC容器,从实现来说,指的是一个容器系列。这也就是说,我们通常所说的IoC容器,如果深入到Spring的实现去看,会发现IoC容器实际上代表着一系列功能各异的容器产品。只是容器的功能有大有小,有各自的特点。打个比方来说,就像是百货商店里出售的商品,我们举水桶为例子,在商店中出售的水桶有大有小;制作材料也各不相同,有金属的,有塑料的等等,总之是各式各样,但只要能装水,具备水桶的基本特性,那就可以作为水桶来出售来让用户使用。这在Spring中也是一样,它有各式各样的IoC容器的实现供用户选择和使用;使用什么样的容器完全取决于用户的需要,但在使用之前如果能够了解容器的基本情况,那会对容器的使用是非常有帮助的;就像我们在购买商品时进行的对商品的考察和挑选那样。 

我们从最基本的XmlBeanFactory看起,它是容器系列的最底层实现,这个容器的实现与我们在Spring应用中用到的那些上下文相比,有一个非常明显的特点,它只提供了最基本的IoC容器的功能。从它的名字中可以看出,这个IoC容器可以读取以XML形式定义的BeanDefinition。理解这一点有助于我们理解ApplicationContext与基本的BeanFactory之间的区别和联系。我们可以认为直接的BeanFactory实现是IoC容器的基本形式,而各种ApplicationContext的实现是IoC容器的高级表现形式。 

仔细阅读XmlBeanFactory的源码,在一开始的注释里面已经对 XmlBeanFactory的功能做了简要的说明,从代码的注释还可以看到,这是Rod Johnson在2001年就写下的代码,可见这个类应该是Spring的元老类了。它是继承DefaultListableBeanFactory这个类的,这个DefaultListableBeanFactory就是一个很值得注意的容器! 

Java代码  

  1. public class XmlBeanFactory extends DefaultListableBeanFactory {  
  2.     private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);  
  3.     public XmlBeanFactory(Resource resource) throws BeansException {  
  4.         this(resource, null);  
  5.     }  
  6.     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {  
  7.         super(parentBeanFactory);  
  8.         this.reader.loadBeanDefinitions(resource);  
  9.     }  
  10. }  

BeanFactory测试:

Java代码  

  1. public class BeanFactoryTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.         ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();  
  5.         Resource res = resolver.getResource("classpath:spring-test.xml");  
  6.         BeanFactory bf = new XmlBeanFactory(res);  
  7.         System.out.println("init BeanFactory");  
  8.   
  9.         Mars mars = bf.getBean("mars", Mars.class);  
  10.         System.out.println(mars.getCnName() + ":" + mars.getAge());  
  11.     }  
  12. }  

 
XmlBeanFactory的功能是建立在DefaultListableBeanFactory这个基本容器的基础上的,在这个基本容器的基础上实现了其他诸如XML读取的附加功能。对于这些功能的实现原理,看一看XmlBeanFactory的代码实现就能很容易地理解。在如下的代码中可以看到,在XmlBeanFactory构造方法中需要得到Resource对象。对XmlBeanDefinitionReader对象的初始化,以及使用这个这个对象来完成loadBeanDefinitions的调用,就是这个调用启动了从Resource中载入BeanDefinitions的过程,这个loadBeanDefinitions同时也是IoC容器初始化的重要组成部分。 

简单来说,IoC容器的初始化包括BeanDefinition的Resouce定位、载入和注册这三个基本的过程。我觉得重点是在载入和对BeanDefinition做解析的这个过程。可以从DefaultListableBeanFactory来入手看看IoC容器是怎样完成BeanDefinition载入的。在refresh调用完成以后,可以看到loadDefinition的调用: 

Java代码  

  1. public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {  
  2.     public AbstractXmlApplicationContext() {  
  3.     }  
  4.     public AbstractXmlApplicationContext(ApplicationContext parent) {  
  5.         super(parent);  
  6.     }  
  7.     //这里是实现loadBeanDefinitions的地方  
  8.     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {  
  9.         // Create a new XmlBeanDefinitionReader for the given BeanFactory.  
  10.         // 创建 XmlBeanDefinitionReader,并通过回调设置到 BeanFactory中去,创建BeanFactory的使用的也是 DefaultListableBeanFactory。  
  11.         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  
  12.   
  13.         // Configure the bean definition reader with this context's  
  14.         // resource loading environment.  
  15.         // 这里设置 XmlBeanDefinitionReader, 为XmlBeanDefinitionReader 配置ResourceLoader,因为DefaultResourceLoader是父类,所以this可以直接被使用  
  16.         beanDefinitionReader.setResourceLoader(this);  
  17.         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  
  18.   
  19.         // Allow a subclass to provide custom initialization of the reader,  
  20.         // then proceed with actually loading the bean definitions.  
  21.     // 这是启动Bean定义信息载入的过程  
  22.         initBeanDefinitionReader(beanDefinitionReader);  
  23.         loadBeanDefinitions(beanDefinitionReader);  
  24.     }  
  25.   
  26.     protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {  
  27.     }  

这里使用 XmlBeanDefinitionReader来载入BeanDefinition到容器中,如以下代码清单所示: 

Java代码  

  1.     //这里是调用的入口。  
  2.     public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {  
  3.         return loadBeanDefinitions(new EncodedResource(resource));  
  4.     }  
  5.     //这里是载入XML形式的BeanDefinition的地方。  
  6.     public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {  
  7.         Assert.notNull(encodedResource, "EncodedResource must not be null");  
  8.         if (logger.isInfoEnabled()) {  
  9.             logger.info("Loading XML bean definitions from " + encodedResource.getResource());  
  10.         }  
  11.   
  12.         Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();  
  13.         if (currentResources == null) {  
  14.             currentResources = new HashSet<EncodedResource>(4);  
  15.             this.resourcesCurrentlyBeingLoaded.set(currentResources);  
  16.         }  
  17.         if (!currentResources.add(encodedResource)) {  
  18.             throw new BeanDefinitionStoreException(  
  19.                     "Detected recursive loading of " + encodedResource + " - check your import definitions!");  
  20.         }  
  21.         //这里得到XML文件,并得到IO的InputSource准备进行读取。  
  22.         try {  
  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.set(null);  
  43.             }  
  44.         }  
  45.     }  
  46. //具体的读取过程可以在doLoadBeanDefinitions方法中找到:  
  47.     //这是从特定的XML文件中实际载入BeanDefinition的地方  
  48.     protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)  
  49.             throws BeanDefinitionStoreException {  
  50.         try {  
  51.             int validationMode = getValidationModeForResource(resource);  
  52.             //这里取得XML文件的Document对象,这个解析过程是由 documentLoader完成的,这个documentLoader是DefaultDocumentLoader,在定义documentLoader的地方创建  
  53.             Document doc = this.documentLoader.loadDocument(  
  54.                     inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());  
  55.             //这里启动的是对BeanDefinition解析的详细过程,这个解析会使用到Spring的Bean配置规则,是我们下面需要详细关注的地方。  
  56.             return registerBeanDefinitions(doc, resource);  
  57.         }  
  58.         catch (BeanDefinitionStoreException ex) {  
  59.             throw ex;  
  60.         }  
  61.         catch (SAXParseException ex) {  
  62.             throw new XmlBeanDefinitionStoreException(resource.getDescription(),  
  63.                     "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);  
  64.         }  
  65.         catch (SAXException ex) {  
  66.             throw new XmlBeanDefinitionStoreException(resource.getDescription(),  
  67.                     "XML document from " + resource + " is invalid", ex);  
  68.         }  
  69.         catch (ParserConfigurationException ex) {  
  70.             throw new BeanDefinitionStoreException(resource.getDescription(),  
  71.                     "Parser configuration exception parsing XML from " + resource, ex);  
  72.         }  
  73.         catch (IOException ex) {  
  74.             throw new BeanDefinitionStoreException(resource.getDescription(),  
  75.                     "IOException parsing XML document from " + resource, ex);  
  76.         }  
  77.         catch (Throwable ex) {  
  78.             throw new BeanDefinitionStoreException(resource.getDescription(),  
  79.                     "Unexpected exception parsing XML document from " + resource, ex);  
  80.         }  
  81.     }  

Java代码  

  1. protected EntityResolver getEntityResolver() {  
  2.         if (this.entityResolver == null) {  
  3.             // Determine default EntityResolver to use.  
  4.             ResourceLoader resourceLoader = getResourceLoader();  
  5.             if (resourceLoader != null) {  
  6.                 this.entityResolver = new ResourceEntityResolver(resourceLoader);  
  7.             }  
  8.             else {  
  9.                 this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());  
  10.             }  
  11.         }  
  12.         return this.entityResolver;  
  13.     }  

 

Java代码  

  1. public DelegatingEntityResolver(ClassLoader classLoader) {  
  2.         this.dtdResolver = new BeansDtdResolver();  
  3.         this.schemaResolver = new PluggableSchemaResolver(classLoader);  
  4.     }  

 

Java代码  

  1. public PluggableSchemaResolver(ClassLoader classLoader) {  
  2.         this.classLoader = classLoader;  
  3.         this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION; // "META-INF/spring.schemas"  
  4.     }  

 
关于具体的Spring BeanDefinition的解析,是在BeanDefinitionParserDelegate中完成的。这个类里包含了各种Spring Bean定义规则的处理,感兴趣的同学可以仔细研究。我们举一个例子来分析这个处理过程,比如我们最熟悉的对Bean元素的处理是怎样完成的,也就是我们在XML定义文件中出现的<bean></bean>这个最常见的元素信息是怎样被处理的。在这里,我们会看到那些熟悉的BeanDefinition定义的处理,比如id、name、aliase等属性元素。把这些元素的值从XML文件相应的元素的属性中读取出来以后,会被设置到生成的BeanDefinitionHolder中去。这些属性的解析还是比较简单的。对于其他元素配置的解析,比如各种Bean的属性配置,通过一个较为复杂的解析过程,这个过程是由parseBeanDefinitionElement来完成的。解析完成以后,会把解析结果放到BeanDefinition对象中并设置到BeanDefinitionHolder中去,如以下清单所示: 
#DefaultBeanDefinitionDocumentReader

Java代码  

  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.                   // 解析import, alias, beans等默认标签  
  15.                         parseDefaultElement(ele, delegate);  
  16.                     }  
  17.                     else {  
  18.                   // 解析aop, mvc, dubbo等自定义标签,这部分在文末解释  
  19.                         delegate.parseCustomElement(ele);  
  20.                     }  
  21.                 }  
  22.             }  
  23.         }  
  24.         else {  
  25.             delegate.parseCustomElement(root);  
  26.         }  
  27.     }  

Java代码  

  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.         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);  
  7.         if (bdHolder != null) {  
  8.             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);  
  9.             try {  
  10.                 // Register the final decorated instance.  
  11.                 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());  
  12.             }  
  13.             catch (BeanDefinitionStoreException ex) {  
  14.                 getReaderContext().error("Failed to register bean definition with name '" +  
  15.                         bdHolder.getBeanName() + "'", ele, ex);  
  16.             }  
  17.             // Send registration event.  
  18.             getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));  
  19.         }  
  20.     }  

  #BeanDefinitionReaderUtils

 

Java代码  

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

 上面的registry正是DefaultListableBeanFactory!

 

#BeanDefinitionParserDelegate

Java代码  

  1. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {  
  2.         //这里取得在<bean>元素中定义的id、name和aliase属性的值  
  3.         String id = ele.getAttribute(ID_ATTRIBUTE);  
  4.         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);  
  5.   
  6.         List<String> aliases = new ArrayList<String>();  
  7.         if (StringUtils.hasLength(nameAttr)) {  
  8.             String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);  
  9.             aliases.addAll(Arrays.asList(nameArr));  
  10.         }  
  11.   
  12.         String beanName = id;  
  13.         if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {  
  14.             beanName = aliases.remove(0);  
  15.             if (logger.isDebugEnabled()) {  
  16.                 logger.debug("No XML 'id' specified - using '" + beanName +  
  17.                         "' as bean name and " + aliases + " as aliases");  
  18.             }  
  19.         }  
  20.   
  21.         if (containingBean == null) {  
  22.             checkNameUniqueness(beanName, aliases, ele);  
  23.         }  
  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.                         // Register an alias for the plain bean class name, if still possible,  
  37.                         // if the generator returned the class name plus a suffix.  
  38.                         // This is expected for Spring 1.2/2.0 backwards compatibility.  
  39.                         String beanClassName = beanDefinition.getBeanClassName();  
  40.                         if (beanClassName != null &&  
  41.                                 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&  
  42.                                 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {  
  43.                             aliases.add(beanClassName);  
  44.                         }  
  45.                     }  
  46.                     if (logger.isDebugEnabled()) {  
  47.                         logger.debug("Neither XML 'id' nor 'name' specified - " +  
  48.                                 "using generated bean name [" + beanName + "]");  
  49.                     }  
  50.                 }  
  51.                 catch (Exception ex) {  
  52.                     error(ex.getMessage(), ele);  
  53.                     return null;  
  54.                 }  
  55.             }  
  56.             String[] aliasesArray = StringUtils.toStringArray(aliases);  
  57.             return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);  
  58.         }  
  59.   
  60.         return null;  
  61.     }  

在具体生成BeanDefinition以后。我们举一个对property进行解析的例子来完成对整个BeanDefinition载入过程的分析,还是在类BeanDefinitionParserDelegate的代码中,它对BeanDefinition中的定义一层一层地进行解析,比如从属性元素集合到具体的每一个属性元素,然后才是对具体的属性值的处理。根据解析结果,对这些属性值的处理会封装成PropertyValue对象并设置到BeanDefinition对象中去,如以下代码清单所示。 

Java代码  

  1. /** 
  2.  * 这里对指定bean元素的property子元素集合进行解析。 
  3.  */  
  4. public void parsePropertyElements(Element beanEle, BeanDefinition bd) {  
  5.     //遍历所有bean元素下定义的property元素  
  6.     NodeList nl = beanEle.getChildNodes();  
  7.     for (int i = 0; i < nl.getLength(); i++) {  
  8.         Node node = nl.item(i);  
  9.         if (node instanceof Element && DomUtils.nodeNameEquals(node, PROPERTY_ELEMENT)) {  
  10.             //在判断是property元素后对该property元素进行解析的过程  
  11.             parsePropertyElement((Element) node, bd);  
  12.         }  
  13.     }  
  14. }  
  15. public void parsePropertyElement(Element ele, BeanDefinition bd) {  
  16.     //这里取得property的名字  
  17.     String propertyName = ele.getAttribute(NAME_ATTRIBUTE);  
  18.     if (!StringUtils.hasLength(propertyName)) {  
  19.         error("Tag 'property' must have a 'name' attribute", ele);  
  20.         return;  
  21.     }  
  22.     this.parseState.push(new PropertyEntry(propertyName));  
  23.     try {  
  24.         //如果同一个bean中已经有同名的存在,则不进行解析,直接返回。也就是说,如果在同一个bean中有同名的property设置,那么起作用的只是第一个。  
  25.         if (bd.getPropertyValues().contains(propertyName)) {  
  26.             error("Multiple 'property' definitions for property '" + propertyName + "'", ele);  
  27.             return;  
  28.         }  
  29.         //这里是解析property值的地方,返回的对象对应对Bean定义的property属性设置的解析结果,这个解析结果会封装到PropertyValue对象中,然后设置到BeanDefinitionHolder中去。  
  30.         Object val = parsePropertyValue(ele, bd, propertyName);  
  31.         PropertyValue pv = new PropertyValue(propertyName, val);  
  32.         parseMetaElements(ele, pv);  
  33.         pv.setSource(extractSource(ele));  
  34.         bd.getPropertyValues().addPropertyValue(pv);  
  35.     }  
  36.     finally {  
  37.         this.parseState.pop();  
  38.     }  
  39. }  
  40. /** 
  41.  * 这里取得property元素的值,也许是一个list或其他。 
  42.  */  
  43. public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {  
  44.     String elementName = (propertyName != null) ?  
  45.                     "<property> element for property '" + propertyName + "'" :  
  46.                     "<constructor-arg> element";  
  47.   
  48.     // Should only have one child element: ref, value, list, etc.  
  49.     NodeList nl = ele.getChildNodes();  
  50.     Element subElement = null;  
  51.     for (int i = 0; i < nl.getLength(); i++) {  
  52.         Node node = nl.item(i);  
  53.         if (node instanceof Element && !DomUtils.nodeNameEquals(node, DESCRIPTION_ELEMENT) &&  
  54.                 !DomUtils.nodeNameEquals(node, META_ELEMENT)) {  
  55.             // Child element is what we're looking for.  
  56.             if (subElement != null) {  
  57.                 error(elementName + " must not contain more than one sub-element", ele);  
  58.             }  
  59.             else {  
  60.                 subElement = (Element) node;  
  61.             }  
  62.         }  
  63.     }  
  64.     //这里判断property的属性,是ref还是value,不允许同时是ref和value。  
  65.     boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);  
  66.     boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);  
  67.     if ((hasRefAttribute && hasValueAttribute) ||  
  68.             ((hasRefAttribute || hasValueAttribute) && subElement != null)) {  
  69.         error(elementName +  
  70.                 " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);  
  71.     }  
  72.     //如果是ref,创建一个ref的数据对象RuntimeBeanReference,这个对象封装了ref的信息。  
  73.     if (hasRefAttribute) {  
  74.         String refName = ele.getAttribute(REF_ATTRIBUTE);  
  75.         if (!StringUtils.hasText(refName)) {  
  76.             error(elementName + " contains empty 'ref' attribute", ele);  
  77.         }  
  78.         RuntimeBeanReference ref = new RuntimeBeanReference(refName);  
  79.         ref.setSource(extractSource(ele));  
  80.         return ref;  
  81.     } //如果是value,创建一个value的数据对象TypedStringValue ,这个对象封装了value的信息。  
  82.     else if (hasValueAttribute) {  
  83.         TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));  
  84.         valueHolder.setSource(extractSource(ele));  
  85.         return valueHolder;  
  86.     } //如果还有子元素,触发对子元素的解析  
  87.     else if (subElement != null) {  
  88.         return parsePropertySubElement(subElement, bd);  
  89.     }  
  90.     else {  
  91.         // Neither child element nor "ref" or "value" attribute found.  
  92.         error(elementName + " must specify a ref or value", ele);  
  93.         return null;  
  94.     }  
  95. }  

比如,再往下看,我们看到像List这样的属性配置是怎样被解析的,依然在BeanDefinitionParserDelegate中:返回的是一个List对象,这个List是Spring定义的ManagedList,作为封装List这类配置定义的数据封装,如以下代码清单所示。 

Java代码  

  1. public List parseListElement(Element collectionEle, BeanDefinition bd) {  
  2.     String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);  
  3.     NodeList nl = collectionEle.getChildNodes();  
  4.     ManagedList<Object> target = new ManagedList<Object>(nl.getLength());  
  5.     target.setSource(extractSource(collectionEle));  
  6.     target.setElementTypeName(defaultElementType);  
  7.     target.setMergeEnabled(parseMergeAttribute(collectionEle));  
  8.     //具体的List元素的解析过程。  
  9.     parseCollectionElements(nl, target, bd, defaultElementType);  
  10.     return target;  
  11. }  
  12. protected void parseCollectionElements(  
  13.         NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {  
  14.     //遍历所有的元素节点,并判断其类型是否为Element。  
  15.     for (int i = 0; i < elementNodes.getLength(); i++) {  
  16.         Node node = elementNodes.item(i);  
  17.         if (node instanceof Element && !DomUtils.nodeNameEquals(node, DESCRIPTION_ELEMENT)) {  
  18.     //加入到target中去,target是一个ManagedList,同时触发对下一层子元素的解析过程,这是一个递归的调用。  
  19.             target.add(parsePropertySubElement((Element) node, bd, defaultElementType));  
  20.         }  
  21.     }  
  22. }  

经过这样一层一层的解析,我们在XML文件中定义的BeanDefinition就被整个给载入到了IoC容器中,并在容器中建立了数据映射。在IoC容器中建立了对应的数据结构,或者说可以看成是POJO对象在IoC容器中的映像,这些数据结构可以以AbstractBeanDefinition为入口,让IoC容器执行索引、查询和操作。 

在我的感觉中,对核心数据结构的定义和处理应该可以看成是一个软件的核心部分了。所以,这里的BeanDefinition的载入可以说是IoC容器的核心,如果说IoC容器是Spring的核心,那么这些BeanDefinition就是Spring的核心的核心了! 

呵呵,这部分代码数量不小,但如果掌握这条主线,其他都可以举一反三吧,就像我们掌握了操作系统启动的过程,以及在操作系统设计中的核心数据结构像进程数据结构,文件系统数据结构,网络协议数据结构的设计和处理一样,对整个系统的设计原理,包括移植,驱动开发和应用开发,是非常有帮助的!

 

让我们回到牛逼的delegate:

 

Java代码  

  1. public BeanDefinition parseCustomElement(Element ele) {  
  2.         return parseCustomElement(ele, null);  
  3.     }  
  4.   
  5.     public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {  
  6.         String namespaceUri = getNamespaceURI(ele);  
  7.         NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);  
  8.         if (handler == null) {  
  9.             error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);  
  10.             return null;  
  11.         }  
  12.         return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));  
  13.     }  

 

Java代码  

  1. public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {  
  2.   
  3.     /** 
  4.      * The location to look for the mapping files. Can be present in multiple JAR files. 
  5.      */  
  6.     public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";  
  7.   
  8.         public DefaultNamespaceHandlerResolver() {  
  9.         this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);  
  10.     }  
  11.   
  12. /** 
  13.      * Locate the {@link NamespaceHandler} for the supplied namespace URI 
  14.      * from the configured mappings. 
  15.      * @param namespaceUri the relevant namespace URI 
  16.      * @return the located {@link NamespaceHandler}, or {@code null} if none found 
  17.      */  
  18.     public NamespaceHandler resolve(String namespaceUri) {  
  19.         Map<String, Object> handlerMappings = getHandlerMappings();  
  20.         Object handlerOrClassName = handlerMappings.get(namespaceUri);  
  21.         if (handlerOrClassName == null) {  
  22.             return null;  
  23.         }  
  24.         else if (handlerOrClassName instanceof NamespaceHandler) {  
  25.             return (NamespaceHandler) handlerOrClassName;  
  26.         }  
  27.         else {  
  28.             String className = (String) handlerOrClassName;  
  29.             try {  
  30.                 Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);  
  31.                 if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {  
  32.                     throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +  
  33.                             "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");  
  34.                 }  
  35.                 NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);  
  36.                 namespaceHandler.init();  
  37.                 handlerMappings.put(namespaceUri, namespaceHandler);  
  38.                 return namespaceHandler;  
  39.             }  
  40.             catch (ClassNotFoundException ex) {  
  41.                 throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +  
  42.                         namespaceUri + "] not found", ex);  
  43.             }  
  44.             catch (LinkageError err) {  
  45.                 throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +  
  46.                         namespaceUri + "]: problem with handler class file or dependent class", err);  
  47.             }  
  48.         }  
  49.     }  
  50. }  

 我们自定义标签时,需要做的是在META-INF下建两个文件spring.handlers, spring.schemas,然后实现我们自己的NamespaceHandler和BeanDefinitionParser.

 

Java代码  

  1. public class AopNamespaceHandler extends NamespaceHandlerSupport {  
  2.   
  3.     /** 
  4.      * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the 
  5.      * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}' 
  6.      * and '{@code scoped-proxy}' tags. 
  7.      */  
  8.     public void init() {  
  9.         // In 2.0 XSD as well as in 2.1 XSD.  
  10.         registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());  
  11.         registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());  
  12.         registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());  
  13.   
  14.         // Only in 2.0 XSD: moved to context namespace as of 2.1  
  15.         registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());  
  16.     }  
  17.   
  18. }  

 #AspectJAutoProxyBeanDefinitionParser

Java代码  

  1. public BeanDefinition parse(Element element, ParserContext parserContext) {  
  2.         AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);  
  3.         extendBeanDefinition(element, parserContext);  
  4.         return null;  
  5.     }  

# AopNamespaceUtils

Java代码  

  1. public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(  
  2.             ParserContext parserContext, Element sourceElement) {  
  3.   
  4.         BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(  
  5.                 parserContext.getRegistry(), parserContext.extractSource(sourceElement));  
  6.         useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);  
  7.         registerComponentIfNecessary(beanDefinition, parserContext);  
  8.     }  

 #AopConfigUtils

Java代码  

  1. public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {  
  2.         return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);  
  3.     }  
  4.   
  5. private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) {  
  6.         Assert.notNull(registry, "BeanDefinitionRegistry must not be null");  
  7.         if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {  
  8.             BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);  
  9.             if (!cls.getName().equals(apcDefinition.getBeanClassName())) {  
  10.                 int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());  
  11.                 int requiredPriority = findPriorityForClass(cls);  
  12.                 if (currentPriority < requiredPriority) {  
  13.                     apcDefinition.setBeanClassName(cls.getName());  
  14.                 }  
  15.             }  
  16.             return null;  
  17.         }  
  18.         RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);  
  19.         beanDefinition.setSource(source);  
  20.         beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);  
  21.         beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);  
  22.                 // 看这里!  
  23.         registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);  
  24.         return beanDefinition;  
  25.     }  

 另外,基于注解的配置,

Java代码  

  1. <context:component-scan base-package="com.itlong.whatsmars.spring"/>  

 处理过程参见ComponentScanBeanDefinitionParser,

Java代码  

  1. public BeanDefinition parse(Element element, ParserContext parserContext) {  
  2.         String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),  
  3.                 ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);  
  4.   
  5.         // Actually scan for bean definitions and register them.  
  6.         ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);  
  7.         Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);  
  8.         registerComponents(parserContext.getReaderContext(), beanDefinitions, element);  
  9.   
  10.         return null;  
  11.     }  

 

原文链接:[http://wely.iteye.com/blog/2296759]

时间: 2024-10-26 05:42:22

Spring的IoC容器实现原理(一)#loadBeanDefinition的相关文章

【spring源码学习】spring的IOC容器之自定义xml配置标签扩展namspaceHandler向IOC容器中注册bean

[spring以及第三方jar的案例]在spring中的aop相关配置的标签,线程池相关配置的标签,都是基于该种方式实现的.包括dubbo的配置标签都是基于该方式实现的.[一]原理 ===>spring在解析xml标签,一旦不是以<bean>开头的元素,就会走org.springframework.beans.factory.xml.BeanDefinitionParserDelegate的parseCustomElement(Element ele)方法解析自定义的标签 ===>

Spring在 IOC 容器中 Bean 之间的关系

一.在 Spring IOC 容器中 Bean 之间存在继承和依赖关系. 需要注意的是,这个继承和依赖指的是 bean 的配置之间的关系,而不是指实际意义上类与类之间的继承与依赖,它们不是一个概念. 二.Bean 之间的继承关系. 1.被继承的 ban 称为父 bean,继承这个父 bean 的 bean 称为子 bean.父 bean 可以通过指定 abstract 属性来声明为抽象的,子 bean 通过指定 parent 属性来指定父 bean 的引用. 2.子 bean 从父 bean 中

【spring源码学习】spring的IOC容器在初始化bean过程

[一]初始化IOC的bean的时候Spring会执行的一些回调方法 (1)spring bean创建的前置处理 =>ApplicationContextAwareProcessor 在创建bean的时候调用 bean.setEnvironment(this.applicationContext.getEnvironment()) bean.setEmbeddedValueResolver(new EmbeddedValueResolver(this.applicationContext.getB

重新学习Spring之核心IOC容器的底层原理

一:IOC容器的定义 控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心. 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找.依赖注入应用比较广泛.   二:Ioc容器相关含义 许多强大的功能都是由两个或是更多的类通过彼此的合作来实现业务逻辑,这使得每个对象和其他的对象产生依赖或者关联.(也就是对象持有其他对象的引用).如果这个获取过程

Spring源码学习之:模拟实现BeanFactory,从而说明IOC容器的大致原理

spring的IOC容器能够帮我们自动new对象,对象交给spring管之后我们不用自己手动去new对象了.那么它的原理是什么呢?是怎么实现的呢?下面我来简单的模拟一下spring的机制,相信看完之后就会对spring的原理有一定的了解. spring使用BeanFactory来实例化.配置和管理对象,但是它只是一个接口,里面有一个getBean()方法.我们一般都不直接用BeanFactory,而是用它的实现类ApplicationContext,这个类会自动解析我们配置的applicatio

Spring源代码解析(二):IOC容器在web容器中的启动

以下引用自博客:http://jiwenke-spring.blogspot.com/ 上面我们分析了IOC容器本身的实现,下面我们看看在典型的web环境中,Spring IOC 容器是怎样被载入和起作用的. 简单的说,在web容器中,通过ServletContext为Spring的IOC容器提供宿主环境,对 应的建立起一个IOC容器的体系.其中,首先需要建立的是根上下文,这个上下文持有的 对象可以有业务对象,数据存取对象,资源,事物管理器等各种中间层对象.在这个上下 文的基础上,和web MV

《Spring技术内幕》——2.2节IoC容器系列的设计与实现:BeanFactory和ApplicationContext

2.2 IoC容器系列的设计与实现:BeanFactory和ApplicationContext在Spring IoC容器的设计中,我们可以看到两个主要的容器系列,一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器的最基本功能:另一个是ApplicationContext应用上下文,它作为容器的高级形态而存在.应用上下文在简单容器的基础上,增加了许多面向框架的特性,同时对应用环境作了许多适配.有了这两种基本的容器系列,基本上可以满足用户对IoC容器使用的大部分需求了.下面

深入理解Java的Spring框架中的IOC容器_java

Spring IOC的原型spring框架的基础核心和起点毫无疑问就是IOC,IOC作为spring容器提供的核心技术,成功完成了依赖的反转:从主类的对依赖的主动管理反转为了spring容器对依赖的全局控制. 这样做的好处是什么呢? 当然就是所谓的"解耦"了,可以使得程序的各模块之间的关系更为独立,只需要spring控制这些模块之间的依赖关系并在容器启动和初始化的过程中将依据这些依赖关系创建.管理和维护这些模块就好,如果需要改变模块间的依赖关系的话,甚至都不需要改变程序代码,只需要将更

浅析Java的Spring框架中IOC容器容器的应用_java

Spring容器是Spring框架的核心.容器将创建对象,它们连接在一起,配置它们,并从创建到销毁管理他们的整个生命周期.在Spring容器使用依赖注入(DI)来管理组成应用程序的组件.这些对象被称为Spring Beans. 容器获得其上的哪些对象进行实例化,配置和组装通过阅读提供的配置元数据的说明.配置元数据可以通过XML,Java注释或Java代码来表示.下面的图是Spring如何工作的高层次图. Spring IoC容器是利用Java的POJO类和配置元数据的产生完全配置和可执行的系统或