通过读取xml注册bean

  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 http://www.springframework.org/schema/beans/spring-beans.xsd">
  5. <bean id="user" class="com.jame.pojo.User"/>
  6. </beans>
  1. public static void test1(){
  2. XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
  3. Object o = factory.getBean("user");
  4. System.out.println(o);
  5. }

首先看XmlBeanFactory结构

diagram

  1. XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));

在创建XmlBeanFactory时传入了一个ClassPathResource对象,先来看这个对象

ClassPathResource

​ 在java中将不同的资源抽象成URL,通过注册不同的handler(URLStreamHandler)来处理不同的资源的读取逻辑,而URL没有提供一些基本的方法,这些都是Spring对内部使用到的资源实现了自己的抽象结构:Resource接口来封装底层资源

​ InputStreamSource封装任何能返回InputStream的类,比如File,ClassPath下的资源等等,它只有定义一个方法:getInputStream(); 该方法返回一个新的InputStream对象

​ Resource接口抽象了所有的Spring内部使用到的底层资源,首先它定义了3个判断当前资源状态的方法,存在性(exists),可读性(isReadable),是否处于打开状态(isOpen),另外Resource接口还提供不同资源到URL URI File类型的转换,以及获取lastModified属性,文件名(不带路径信息的文件名,getFilename())的方法,创建基于当前路径创建相对资源的方法 createRelative()

有了Resource接口后便可以对资源文件进行统一处理

ClassPathResource

  1. public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
  2. Assert.notNull(path, "Path must not be null");
  3. String pathToUse = StringUtils.cleanPath(path);
  4. if (pathToUse.startsWith("/")) {
  5. pathToUse = pathToUse.substring(1);
  6. }
  7. this.path = pathToUse;
  8. this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
  9. }

当通过Resource相关类完成了对配置文件进行封装后,配置文件的读取工作就交给XmlBeanDefinitionReader来处理

接下来就开始进入构造方法

XmlBeanFactory

  1. public XmlBeanFactory(Resource resource) throws BeansException {
  2. //调用XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)构造方法
  3. this(resource, null);
  4. }
  1. public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
  2. super(parentBeanFactory);
  3. //加载资源的切入点
  4. this.reader.loadBeanDefinitions(resource);
  5. }

在进行解析xml文件之前,先看一下super调用的方法

AbstractAutowireCapableBeanFactory

  1. public AbstractAutowireCapableBeanFactory() {
  2. super();
  3. //忽略给定接口的自动装配功能
  4. //默认情况下获取A,而A的Bean属性还有B,而B还没有初始化,那么Spring会将B初始化,
  5. //但是某些情况下不会初始化B,例如B继承了BeanFactoryAware接口
  6. ignoreDependencyInterface(BeanNameAware.class);
  7. ignoreDependencyInterface(BeanFactoryAware.class);
  8. ignoreDependencyInterface(BeanClassLoaderAware.class);
  9. }

现在可以看this.reader.loadBeanDefinitions(resource);方法了

主要流程如下

  1. 封装资源文件 进入XmlBeanDefinitionReader后先对参数Resource使用EncodedResource类进行封装
  2. 获取输入流,从Resource中获取对应的InputStream并构造InputSource
  3. 通过构造的InputSource实例和Resource实例继续调用函数doLoadBeanDefinitions

XmlBeanDefinitionReader

  1. @Override
  2. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  3. return loadBeanDefinitions(new EncodedResource(resource));
  4. }

EncodedResource的作用是对资源文件的编码进行处理,主要逻辑体现在getReader()方法中,当设置了编码Spring就会使用相应的编码作为输入的编码

EncodedResource

  1. public Reader getReader() throws IOException {
  2. if (this.charset != null) {
  3. return new InputStreamReader(this.resource.getInputStream(), this.charset);
  4. }
  5. else if (this.encoding != null) {
  6. return new InputStreamReader(this.resource.getInputStream(), this.encoding);
  7. }
  8. else {
  9. return new InputStreamReader(this.resource.getInputStream());
  10. }
  11. }

当构造好encodedResource对象后,再次传入了loadBeanDefinitions(new EncodedResource(resource))

这个方法内部才是真正的数据准备阶段

XmlBeanDefinitionReader

  1. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  2. Assert.notNull(encodedResource, "EncodedResource must not be null");
  3. if (logger.isTraceEnabled()) {
  4. logger.trace("Loading XML bean definitions from " + encodedResource);
  5. }
  6. //通过属性来记录已经加载的资源
  7. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  8. if (currentResources == null) {
  9. currentResources = new HashSet<>(4);
  10. this.resourcesCurrentlyBeingLoaded.set(currentResources);
  11. }
  12. if (!currentResources.add(encodedResource)) {
  13. throw new BeanDefinitionStoreException(
  14. "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  15. }
  16. try {
  17. //从encodedResource中获取Resource对象,然后再从Resource中获取InputStream对象
  18. InputStream inputStream = encodedResource.getResource().getInputStream();
  19. try {
  20. //这个类并不来自Spring,全路径为org.xml.sax
  21. InputSource inputSource = new InputSource(inputStream);
  22. if (encodedResource.getEncoding() != null) {
  23. inputSource.setEncoding(encodedResource.getEncoding());
  24. }
  25. //真正核心部分=======================----
  26. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  27. //=========================================
  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. }
  1. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
  2. throws BeanDefinitionStoreException {
  3. try {
  4. //通过inputSource获取Document对象
  5. Document doc = doLoadDocument(inputSource, resource);
  6. int count = registerBeanDefinitions(doc, resource);
  7. if (logger.isDebugEnabled()) {
  8. logger.debug("Loaded " + count + " bean definitions from " + resource);
  9. }
  10. return count;
  11. }
  12. //剩下全部都是异常处理

上面的代码其实就做了3件事

  1. 获取对XML文件的验证模式
  2. 加载XML,并得到对应的Document
  3. 根据返回的Document注册Bean信息

首先来看第一件事:获取xml文件验证模式

简单说下DTD和XSD区别

DTD是一种xml约束模式语言,一个DTD文档包含:元素的定义规则,元素间关系的定义规则,元素可使用规则,可使用的实体或符号规则

要是用DTD验证模式只需要在XML文件头部声明即可

XML Schema 语言就是XSD 它描述了xml文档的结构,可以指定一个xml schema来验证某个xml,xml schema本身就是一个xml文档,可以用通用xml解析器解析它

在使用xml schema文档对xml实例文档进行检验,除了要声明名称空间外,还必须指定该名称空间所对应的xml schema文档储存位置,通过schemaLocation属性来指定xml schema文件位置或URL地址

验证模式的读取

XmlBeanDefinitionReader

  1. protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
  2. return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
  3. getValidationModeForResource(resource), isNamespaceAware());
  4. }
  1. protected int getValidationModeForResource(Resource resource) {
  2. int validationModeToUse = getValidationMode();
  3. //如果手动指定验证模式则使用指定的验证模式
  4. if (validationModeToUse != VALIDATION_AUTO) {
  5. return validationModeToUse;
  6. }
  7. int detectedMode = detectValidationMode(resource);
  8. //如果未指定则使用自动检测
  9. if (detectedMode != VALIDATION_AUTO) {
  10. return detectedMode;
  11. }
  12. return VALIDATION_XSD;
  13. }

detectValidationMode(resource);自动检测验证的模式

  1. protected int detectValidationMode(Resource resource) {
  2. if (resource.isOpen()) {
  3. throw new BeanDefinitionStoreException(
  4. "Passed-in Resource [" + resource + "] contains an open stream: " +
  5. "cannot determine validation mode automatically. Either pass in a Resource " +
  6. "that is able to create fresh streams, or explicitly specify the validationMode " +
  7. "on your XmlBeanDefinitionReader instance.");
  8. }
  9. InputStream inputStream;
  10. try {
  11. inputStream = resource.getInputStream();
  12. }
  13. catch (IOException ex) {
  14. throw new BeanDefinitionStoreException(
  15. "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
  16. "Did you attempt to load directly from a SAX InputSource without specifying the " +
  17. "validationMode on your XmlBeanDefinitionReader instance?", ex);
  18. }
  19. try {
  20. //又交给detectValidationMode去完成验证
  21. return this.validationModeDetector.detectValidationMode(inputStream);
  22. }
  23. catch (IOException ex) {
  24. throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
  25. resource + "]: an error occurred whilst reading from the InputStream.", ex);
  26. }
  27. }

XmlValidationModeDetector

  1. //判断方法就是是否包含DOCTYPE,如果包含就是DTD,否则就是XSD
  2. public int detectValidationMode(InputStream inputStream) throws IOException {
  3. // Peek into the file to look for DOCTYPE.
  4. BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
  5. try {
  6. boolean isDtdValidated = false;
  7. String content;
  8. while ((content = reader.readLine()) != null) {
  9. content = consumeCommentTokens(content);
  10. //如果读取的是空行或者是注释略过
  11. if (this.inComment || !StringUtils.hasText(content)) {
  12. continue;
  13. }
  14. if (hasDoctype(content)) {
  15. isDtdValidated = true;
  16. break;
  17. }
  18. //如果读到<开始符号,验证模式一定会在开始符号之前
  19. if (hasOpeningTag(content)) {
  20. // End of meaningful data...
  21. break;
  22. }
  23. }
  24. return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
  25. }
  26. catch (CharConversionException ex) {
  27. // Choked on some character encoding...
  28. // Leave the decision up to the caller.
  29. return VALIDATION_AUTO;
  30. }
  31. finally {
  32. reader.close();
  33. }
  34. }

获取Document

通过验证就可以进行Document加载了,同样在XmlBeanFactoryReader类中没有实现,而是调用了loadDocument方法来实现

DocumentLoader I

  1. Document loadDocument(
  2. InputSource inputSource, EntityResolver entityResolver,
  3. ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
  4. throws Exception;

这是个接口,他的实现类为

DefaultDocumentLoader

  1. //通过SAX解析XML文档
  2. public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
  3. ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
  4. //创建DocumentBuilderFactory对象
  5. DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
  6. if (logger.isTraceEnabled()) {
  7. logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
  8. }
  9. //创建DocumentBuilder对象
  10. DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
  11. //解析inputSource来获取Document对象
  12. return builder.parse(inputSource);
  13. }

主要来看一下doLoadDocument方法传入的 getEntityResolver()参数

XmlBeanDefinitionReader

  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. }

EntityResoulver用法

如果SAX应用程序需要实现自定义处理外部实体,必须要实现此接口并使用setEntutyResolver方法向SAX驱动器注册一个实例,–对于解析一个XML,SAX首先读取该XML文档上的声明,根据声明去寻找对于的DTD定义,以便对文档进行验证,默认是从网络上下载对应的DTD声明,而EntityResolver的作用就是项目本身就可以提供一个DTD文件声明的方法,由程序来寻找DTD声明的过程,比如讲DTD文件放到项目的某处,在实现时直接将此文档读取返回给SAX即可,不用通过网络来寻找相应的声明

EntityResolver的接口方法声明

EntityResolver I

  1. public abstract InputSource resolveEntity (String publicId,String systemId)

如果解析验证模式为XSD的配置文件会读取到两个参数

publicId : null

systemId : xxxxxxxxxxx

而解析验证模式为DTD的配置文件

publicId : xxx

systemId : xxx

根据之前Spring通过getEntityResilver()方法对EntityResolver的获取,Spring中使用DelegatingEntityResolver类为EntityResolver的实现类,resolverEntity的实现方法如下

DelegatingEntityResolver

  1. public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId)
  2. throws SAXException, IOException {
  3. if (systemId != null) {
  4. if (systemId.endsWith(DTD_SUFFIX)) {
  5. //如果是dtd从这里解析
  6. return this.dtdResolver.resolveEntity(publicId, systemId);
  7. }
  8. else if (systemId.endsWith(XSD_SUFFIX)) {
  9. //通过META-INF/Spring.schemas解析
  10. return this.schemaResolver.resolveEntity(publicId, systemId);
  11. }
  12. }
  13. // Fall back to the parser's default behavior.
  14. return null;
  15. }

直接截取后缀来判断是那种解析类型

如果是DTD,会去当前路径下寻找,如果是XSD,默认是到META-INF/Spring.schemas文件中找到对应systemid的XSD文件并加载

BeansDtdResolver

  1. public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws IOException {
  2. if (logger.isTraceEnabled()) {
  3. logger.trace("Trying to resolve XML entity with public ID [" + publicId +
  4. "] and system ID [" + systemId + "]");
  5. }
  6. if (systemId != null && systemId.endsWith(DTD_EXTENSION)) {
  7. int lastPathSeparator = systemId.lastIndexOf('/');
  8. int dtdNameStart = systemId.indexOf(DTD_NAME, lastPathSeparator);
  9. if (dtdNameStart != -1) {
  10. String dtdFile = DTD_NAME + DTD_EXTENSION;
  11. if (logger.isTraceEnabled()) {
  12. logger.trace("Trying to locate [" + dtdFile + "] in Spring jar on classpath");
  13. }
  14. try {
  15. Resource resource = new ClassPathResource(dtdFile, getClass());
  16. InputSource source = new InputSource(resource.getInputStream());
  17. source.setPublicId(publicId);
  18. source.setSystemId(systemId);
  19. if (logger.isTraceEnabled()) {
  20. logger.trace("Found beans DTD [" + systemId + "] in classpath: " + dtdFile);
  21. }
  22. return source;
  23. }
  24. catch (FileNotFoundException ex) {
  25. if (logger.isDebugEnabled()) {
  26. logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in classpath", ex);
  27. }
  28. }
  29. }
  30. }
  31. // Fall back to the parser's default behavior.
  32. return null;
  33. }

解析和注册BeanDefinitions

XmlBeanDefinitionReader

  1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  2. //使用DefaultBeanDefinitionDocument实例化BeanDefinitionDocumentReader
  3. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  4. //记录统计前BeanDefinition的加载个数
  5. int countBefore = getRegistry().getBeanDefinitionCount();
  6. //加载及注册bean
  7. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  8. //记录本次加载的BeanDefinition个数
  9. return getRegistry().getBeanDefinitionCount() - countBefore;
  10. }

进入registerBeanDefinitions方法

DefaultBeanDefinitionDocumentReader

  1. @Override
  2. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  3. this.readerContext = readerContext;
  4. //提取DocumentElement
  5. doRegisterBeanDefinitions(doc.getDocumentElement());
  6. }
  1. protected void doRegisterBeanDefinitions(Element root) {
  2. BeanDefinitionParserDelegate parent = this.delegate;
  3. this.delegate = createDelegate(getReaderContext(), root, parent);
  4. if (this.delegate.isDefaultNamespace(root)) {
  5. //处理profile属性
  6. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  7. if (StringUtils.hasText(profileSpec)) {
  8. String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
  9. profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  10. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
  11. if (logger.isDebugEnabled()) {
  12. logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
  13. "] not matching: " + getReaderContext().getResource());
  14. }
  15. return;
  16. }
  17. }
  18. }
  19. //处理前解析,留给子类实现
  20. preProcessXml(root);
  21. parseBeanDefinitions(root, this.delegate);
  22. //处理后解析,留给子类实现
  23. postProcessXml(root);
  24. this.delegate = parent;
  25. }

首先程序会回去beans节点是否定义了profile属性,如果定义了则会需要到环境变量中去寻找,所以这里首先断言environment不可能为空,因为profile是可以同时指定多个的,需要程序对其拆分,并解析每个profile是都符合环境变量中所定义的,不定义则不会浪费性能去解析。

解析并注册BeanDefinition

DefaultBeanDefinitionDocumentReader

  1. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  2. //对bean的处理
  3. if (delegate.isDefaultNamespace(root)) {
  4. NodeList nl = root.getChildNodes();
  5. for (int i = 0; i < nl.getLength(); i++) {
  6. Node node = nl.item(i);
  7. if (node instanceof Element) {
  8. Element ele = (Element) node;
  9. if (delegate.isDefaultNamespace(ele)) {
  10. //对默认标签的处理
  11. parseDefaultElement(ele, delegate);
  12. }
  13. else {
  14. //对bean的处理
  15. delegate.parseCustomElement(ele);
  16. }
  17. }
  18. }
  19. }
  20. else {
  21. delegate.parseCustomElement(root);
  22. }
  23. }
  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. }

先从bean标签开始

  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. }

(1)首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bdHolder,经过这个方法后, bdHolder实例已经包含我们配置文件中配置的各种属性了,例如class, name,id, alias之类的属性。

(2)当返回的bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析。

(3)解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法。

(4)最后发出响应事件,通知想关的监听器,这个bean已经加载完成了。

BeanDefinitionParserDelegate

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

(1)提取元素中的id以及name属性。

(2)进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中。

(3)如果检测到bean没有指定beanName,那么使用默认规则为此Bean生成beanName

(4)将获取到的信息封装到BeanDefinitionHolder的实例中。

  1. public AbstractBeanDefinition parseBeanDefinitionElement(
  2. Element ele, String beanName, @Nullable BeanDefinition containingBean) {
  3. this.parseState.push(new BeanEntry(beanName));
  4. String className = null;
  5. //解析Class属性
  6. if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
  7. className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
  8. }
  9. String parent = null;
  10. //解析parent属性
  11. if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
  12. parent = ele.getAttribute(PARENT_ATTRIBUTE);
  13. }
  14. try {
  15. //创建用于承载AbstractBeanDefinition类型的GenericBeanDefinition
  16. AbstractBeanDefinition bd = createBeanDefinition(className, parent);
  17. //解析默认的bean的各种属性
  18. parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
  19. bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
  20. //解析元数据
  21. parseMetaElements(ele, bd);
  22. //解析lookup-method 属性
  23. parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
  24. //解析replace-method属性
  25. parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
  26. //解析构造函数参数
  27. parseConstructorArgElements(ele, bd);
  28. //解析property子元素
  29. parsePropertyElements(ele, bd);
  30. //解析qualifier子元素
  31. parseQualifierElements(ele, bd);
  32. bd.setResource(this.readerContext.getResource());
  33. bd.setSource(extractSource(ele));
  34. return bd;
  35. }
  36. catch (ClassNotFoundException ex) {
  37. error("Bean class [" + className + "] not found", ele, ex);
  38. }
  39. catch (NoClassDefFoundError err) {
  40. error("Class that bean class [" + className + "] depends on not found", ele, err);
  41. }
  42. catch (Throwable ex) {
  43. error("Unexpected failure during bean definition parsing", ele, ex);
  44. }
  45. finally {
  46. this.parseState.pop();
  47. }
  48. return null;
  49. }

创建用于属性承载的BeanDefinitionBeanDefinition是一个接口,在Spring中存在三种实现: RootBeanDefinition,ChildBeanDefinition以及GenericBeanDefinition。三种实现均继承了AbstractBeanDefiniton ,其中BeanDefinition是配置文件元素标签在容器中的内部表示形式。元素标签拥有class, scope, lazy-init等配置属性, BeanDefinition则提供了相应的beanClass, scope, lazylnit属性, BeanDefinition和中的属性是一一对应的。其中RootBeanDefinition是最常用的实现类,它对应一般性的元素标签, GenericBeanDefinition是自2.5版本以后新加入的bean文件配置属性定义类,是一站式服务类。在配置文件中可以定义父和子,父用RootBeanDefinition表示,而子用ChildBeanDefiniton表示,而没有父的就使用RootBeanDefinition表示。AbstractBeanDefinition对两者共同的类信息进行抽象Spring通过BeanDefinition将配置文件中的配置信息转换为容器的内部表示,并将这些BeanDefiniton注册到BeanDefinitonRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。

BeanDefinition

BeanDefinitionParserDelegate

  1. protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
  2. throws ClassNotFoundException {
  3. return BeanDefinitionReaderUtils.createBeanDefinition(
  4. parentName, className, this.readerContext.getBeanClassLoader());
  5. }
  6. //=====================
  7. public static AbstractBeanDefinition createBeanDefinition(
  8. @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
  9. GenericBeanDefinition bd = new GenericBeanDefinition();
  10. //parentName可能为空
  11. bd.setParentName(parentName);
  12. if (className != null) {
  13. if (classLoader != null) {
  14. //如果classLoader不为空,则传入使用的classLoader同一虚拟机加载类对象,否则只是记录classname
  15. bd.setBeanClass(ClassUtils.forName(className, classLoader));
  16. }
  17. else {
  18. bd.setBeanClassName(className);
  19. }
  20. }
  21. return bd;
  22. }

当我们创建了bean信息的承载实例后,便可以进行bean信息的各种属性解析了,parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

版权声明:本文为sunankang原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/sunankang/p/jame.html