转载

Spring Ioc之BeanDefinition的加载

本文研究Spring配置文件解析到注册的过程。

我们写个demo来进行debug

XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("iocbeans.xml"));
复制代码

第一步获取XML的resource。

第二步才是资源的加载。

public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, (BeanFactory)null);
    }

    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader = new XmlBeanDefinitionReader(this);
        //这里才是资源加载的真正实现
        this.reader.loadBeanDefinitions(resource);
    }
      public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return this.loadBeanDefinitions(new EncodedResource(resource));
    }

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
       //...省略部分代码
    //通过属性记录当前已经加载的资源
        Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
        if(currentResources == null) {
            currentResources = new HashSet(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }

        if(!((Set)currentResources).add(encodedResource)) {
            throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        } else {
            int var5;
            try {
                //获取inputStream
                InputStream inputStream = encodedResource.getResource().getInputStream();

                try {
                    InputSource inputSource = new InputSource(inputStream);
                    if(encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }
				//核心逻辑部分
         var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                } finally {//关闭流
                    inputStream.close();
                }
            } catch (IOException var15) {
                throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
            } finally {
                ((Set)currentResources).remove(encodedResource);
                if(((Set)currentResources).isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.remove();
                }

            }

            return var5;
        }
    }
复制代码

总结下上面代码的逻辑:

首先对传入的resource参数作封装,原因是可能存在编码的情况。然后准备inputSource对象,方便后面的读取。最后传入doLoadBeanDefinitions方法。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        try {
            //1.加载xml文件,转化为doc格式
            Document doc = this.doLoadDocument(inputSource, resource);
            //2.根据返回的doc,注册bean信息
            return this.registerBeanDefinitions(doc, resource);
        } catch (BeanDefinitionStoreException var4) {
            throw var4;
        }
复制代码

上面1中,会涉及对XML的验证,此处略过。

解析及注册BeanDefinitions

通过上面1,我们已经拿到了Document.开始进入registerBeanDefinitions(doc, resource);

public int registerBeanDefinitions(Document doc, Resource resource)  {
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
    //记录统计前的bean个数
        int countBefore = this.getRegistry().getBeanDefinitionCount();
    //加载及注册bean
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
    //记录本次加载bean的个数
        return this.getRegistry().getBeanDefinitionCount() - countBefore;
    }
复制代码

其中的参数doc是通过上一节loadDocument加载转换出来的。在这个方法中很好地应用了面向对象中单一职责的原则,将逻辑处理委托给单一的类进行处理,而这个逻辑处理类就是BeanDefinitionDocumentReader。BeanDefinitionDocumentReader是一个接口,而实例化的工作是在createBeanDefinitionDocumentReader()中完成的,而通过此方法BeanDefinitionDocumentReader真正的类型是DefaultBeanDefinitionDocumentReader了,进入后,发现这个方法的重要目的之一就是提取root,以便于再次将root作为参数继续BeanDefiniton的注册。

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        this.logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        this.doRegisterBeanDefinitions(root);
    }

protected void doRegisterBeanDefinitions(Element root) {
//专门处理解析
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
    if(this.delegate.isDefaultNamespace(root)) {
        String profileSpec = root.getAttribute("profile");
        if(StringUtils.hasText(profileSpec)) {
       String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
            if(!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                return;
            }
        }
    }
		//protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
        if(this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute("profile");
            if(StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                if(!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    return;
                }
            }
        }
	//	解析前处理,留给子类实现
        this.preProcessXml(root);
        
        this.parseBeanDefinitions(root, this.delegate);
        //解析后处理,子类实现
        this.postProcessXml(root);
        this.delegate = parent;
    }
}
复制代码

进入 this.preProcessXml(root)和this.postProcessXml(root)方法会发现它们是空方法。既然是空方法那有什么用呢?我们可以快速反映出这是模版的设计模式。一个类要么是面向继承设计的,要么就用final修饰。这两个方法就是为子类设计的。

下面就开始进行XML的读取了。

void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
       //对beans读取
        if(delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();

            for(int i = 0; i < nl.getLength(); ++i) {
                Node node = nl.item(i);
                if(node instanceof Element) {
                    Element ele = (Element)node;
                    if(delegate.isDefaultNamespace(ele)) {
                        //对bean的处理
                        this.parseDefaultElement(ele, delegate);
                    } else {
                        //对bean的处理
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            delegate.parseCustomElement(root);
        }

    }
复制代码

默认标签的解析

默认标签的解析是在parseDefaultElement函数中处理的,函数中的功能一目了然。分别对4种不同的标签作不同的处理。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if(delegate.nodeNameEquals(ele, "import")) {
            this.importBeanDefinitionResource(ele);
        } else if(delegate.nodeNameEquals(ele, "alias")) {
            this.processAliasRegistration(ele);
        } else if(delegate.nodeNameEquals(ele, "bean")) {
            this.processBeanDefinition(ele, delegate);
        } else if(delegate.nodeNameEquals(ele, "beans")) {
            this.doRegisterBeanDefinitions(ele);
        }

    }
复制代码

bean标签的解析及注册

在上面的4种标签解析中,bean标签的解析最重要也最为复杂,我们搞懂她的处理方式,其他的标签解析就会迎刃而解。我们进入代码看下

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    //1.委托该方法进行元素解析。经过该方法,bdHolder中拥有配置文件的各种属性了。(class,name,id,alias)
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if(bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

            try {//对解析后的bdHolder进行注册
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
            } catch (BeanDefinitionStoreException var5) {
                this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
            }
//发出响应事件,通知相关的监听器,这个bean已经加载完成了
   this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }

    }
复制代码

解析BeanDefinition

首先我们从元素解析及信息提取开始, BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele),进入BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法。

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
        return this.parseBeanDefinitionElement(ele, (BeanDefinition)null);
    }

    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        String id = ele.getAttribute("id");//对id解析
        String nameAttr = ele.getAttribute("name");//对name解析
        List<String> aliases = new ArrayList();
        if(StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
            aliases.addAll(Arrays.asList(nameArr));
        }

        String beanName = id;
        if(!StringUtils.hasText(id) && !aliases.isEmpty()) {
            beanName = (String)aliases.remove(0);
        }
        if(containingBean == null) {
            this.checkNameUniqueness(beanName, aliases, ele);
        }
	//	对标签其他属性的解析
        AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName, containingBean);
        if(beanDefinition != null) {
            if(!StringUtils.hasText(beanName)) {
                try {
                    if(containingBean != null) {  beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
                    } else {
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        String beanClassName = beanDefinition.getBeanClassName();
                        if(beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            aliases.add(beanClassName);
                        }
                    }
                } catch (Exception var9) {
                    this.error(var9.getMessage(), ele);
                    return null;
                }
            }

            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        } else {
            return null;
        }
    }

public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {
        this.parseState.push(new BeanEntry(beanName));
        String className = null;
    //解析class属性
        if(ele.hasAttribute("class")) {
            className = ele.getAttribute("class").trim();
        }

        try {//解析parent属性
            String parent = null;
            if(ele.hasAttribute("parent")) {
                parent = ele.getAttribute("parent");
            }
			//创建用于承载属性的GenericBeanDefinition
            AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);
            //硬编码解析默认bean的各种属性
            this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            //提取description
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));
           //解析元数据
            this.parseMetaElements(ele, bd);
            //解析lookup-method
            this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            //解析replaced-method
            this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            //解析构造函数
            this.parseConstructorArgElements(ele, bd);
            //解析proterty子元素
            this.parsePropertyElements(ele, bd);
            //解析qualifier子元素
            this.parseQualifierElements(ele, bd);
            bd.setResource(this.readerContext.getResource());
            bd.setSource(this.extractSource(ele));
            AbstractBeanDefinition var7 = bd;
            return var7;
        } catch (ClassNotFoundException var13) {
        } finally {
            this.parseState.pop();
        }
        return null;
    }
复制代码

终于,bean标签的所有属性,我们都看到了怎么解析的了。

  1. 创建用于属性承载的BeanDefinition BeanDefiniton是一个接口,在Spring中存在三种实现:RootBeanDefiniton、ChildBeanDefinitio以及GenericBeanDefinition。三种实现均继承AbstractBeanDefinition,其中BeanDefiniton是配置文件元素标签在容器中的表示。它们一一对应。其中RootBeanDefinition是最常用的实现类,它对应一般性的元素标签,GenericBeanDefinition是新加入的bean文件配置属性定义类,是一站式服务类。在配置文件中可以定义父和子,父用RootBeanDefiniton表示,而子用childBeanDefinition表示,而没有父的就使用RootBeanDefiniton表示。Spring通过BeanDefinition将配置文件的信息转化为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。BeanDefinitionRegistry就是spring的内存数据库,主要以map形式保存,后续直接在BeanDefinitionRegistry获取配置信息。 由此可知,要解析属性首先要创建用于承载属性的实例子,也就是创建GenericBeanDefinition类型的实例。而createBeanDefinition(className, parent)的作用就是实现此功能。 public static AbstractBeanDefinition createBeanDefinition(String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException { GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setParentName(parentName); if(className != null) { if(classLoader != null) { //如果classLoader不为null,则加载类对象.否则只是记录className bd.setBeanClass(ClassUtils.forName(className, classLoader)); } else { bd.setBeanClassName(className); } }

    return bd;
        }
    复制代码

    解析各种属性 当我们创建了bean信息的承载实例后,就可以进行bean信息的各种属性解析了。parseBeanDefinitionAttributes是对element所有元素属性进行解析: public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, BeanDefinition containingBean, AbstractBeanDefinition bd) { //解析scope属性 if(ele.hasAttribute("singleton")) { this.error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele); } else if(ele.hasAttribute("scope")) { bd.setScope(ele.getAttribute("scope")); } else if(containingBean != null) { bd.setScope(containingBean.getScope()); } //解析abstract属性 if(ele.hasAttribute("abstract")) { bd.setAbstract("true".equals(ele.getAttribute("abstract"))); } //解析lazy-init属性 String lazyInit = ele.getAttribute("lazy-init"); if("default".equals(lazyInit)) { lazyInit = this.defaults.getLazyInit(); }

    bd.setLazyInit("true".equals(lazyInit));
        //解析autowire属性
            String autowire = ele.getAttribute("autowire");
            bd.setAutowireMode(this.getAutowireMode(autowire));
        //解析dependency-check属性
            String dependencyCheck = ele.getAttribute("dependency-check");
            bd.setDependencyCheck(this.getDependencyCheck(dependencyCheck));
            String autowireCandidate;
       //解析depends-on属性
            if(ele.hasAttribute("depends-on")) {
                autowireCandidate = ele.getAttribute("depends-on");
                bd.setDependsOn(StringUtils.tokenizeToStringArray(autowireCandidate, ",; "));
            }
    //解析autowire-candidate属性
            autowireCandidate = ele.getAttribute("autowire-candidate");
            String destroyMethodName;
            if(!"".equals(autowireCandidate) && !"default".equals(autowireCandidate)) {
                bd.setAutowireCandidate("true".equals(autowireCandidate));
            } else {
                destroyMethodName = this.defaults.getAutowireCandidates();
                if(destroyMethodName != null) {
                    String[] patterns = StringUtils.commaDelimitedListToStringArray(destroyMethodName);
                    bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
                }
            }
    //解析primary属性
            if(ele.hasAttribute("primary")) {
                bd.setPrimary("true".equals(ele.getAttribute("primary")));
            }
    //解析init-method属性
            if(ele.hasAttribute("init-method")) {
                destroyMethodName = ele.getAttribute("init-method");
                if(!"".equals(destroyMethodName)) {
                    bd.setInitMethodName(destroyMethodName);
                }
            } else if(this.defaults.getInitMethod() != null) {
                bd.setInitMethodName(this.defaults.getInitMethod());
                bd.setEnforceInitMethod(false);
            }
    		//解析destroy-method属性
            if(ele.hasAttribute("destroy-method")) {
                destroyMethodName = ele.getAttribute("destroy-method");
                bd.setDestroyMethodName(destroyMethodName);
            } else if(this.defaults.getDestroyMethod() != null) {
                bd.setDestroyMethodName(this.defaults.getDestroyMethod());
                bd.setEnforceDestroyMethod(false);
            }
    		//解析factory-method属性
            if(ele.hasAttribute("factory-method")) {
                bd.setFactoryMethodName(ele.getAttribute("factory-method"));
            }
    			//解析factory-bean属性
            if(ele.hasAttribute("factory-bean")) {
                bd.setFactoryBeanName(ele.getAttribute("factory-bean"));
            }
    
            return bd;
        }
    复制代码

经过上面的处理,Spring完成了对所有bean属性的解析。也就是说,XML中所有配置都可以在GenericBeanDefinition的实例类中找到对应的配置。GenericBeanDefinition只是子类实现,而大部分通用的属性都保存在AbstractBeanDefinition中。我们看下这个类

//...省略静态变量以及final常量
private volatile Object beanClass;
    private String scope;
    private boolean abstractFlag;
    private boolean lazyInit;
    private int autowireMode;
    private int dependencyCheck;
    private String[] dependsOn;
    private boolean autowireCandidate;
    private boolean primary;
    private final Map<String, AutowireCandidateQualifier> qualifiers;
    private boolean nonPublicAccessAllowed;
    private boolean lenientConstructorResolution;
    private ConstructorArgumentValues constructorArgumentValues;
    private MutablePropertyValues propertyValues;
    private MethodOverrides methodOverrides;
    private String factoryBeanName;
    private String factoryMethodName;
    private String initMethodName;
    private String destroyMethodName;
    private boolean enforceInitMethod;
    private boolean enforceDestroyMethod;
    private boolean synthetic;
    private int role;
    private String description;
    private Resource resource;
复制代码

注册解析的BeanDefinition

我们解析完配置文件,对于得到的beanDefinition已经可以满足后续使用了,唯一剩下的工作就是注册了。我们进入到

#DefaultBeanDefinitionDocumentReader
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)  {
    //使用beanName做唯一标识注册
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    //注册所有别名
        String[] aliases = definitionHolder.getAliases();
        if(aliases != null) {
            String[] var4 = aliases;
            int var5 = aliases.length;
            for(int var6 = 0; var6 < var5; ++var6) {
                String alias = var4[var6];
                registry.registerAlias(beanName, alias);
            }
        }

    }
复制代码

从上面代码可以看出,解析的beanDefinition都会被注册到BeanDefinitioinRegistry类型的registry中,而对于beanDefinition的注册分成了两部分:beanName注册和别名的注册.

  1. 通过beanName注册BeanDefinition 对于beanDefinition的注册,本质就是beanName作为key,放入map。 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {

    if(beanDefinition instanceof AbstractBeanDefinition) {
                try {//最后一次校验
                    ((AbstractBeanDefinition)beanDefinition).validate();
                } catch (BeanDefinitionValidationException var9) 
                }
            }
    
            BeanDefinition oldBeanDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
            if(oldBeanDefinition != null) {
            //...省略 如果beanName 存在,且不允许覆盖,就抛出异常
                if(!this.isAllowBeanDefinitionOverriding()) {
                   
                }
                this.beanDefinitionMap.put(beanName, beanDefinition);
            } else {
                if(this.hasBeanCreationStarted()) {
                    Map var4 = this.beanDefinitionMap;
                    //加锁 放进map
                    synchronized(this.beanDefinitionMap) {
                        this.beanDefinitionMap.put(beanName, beanDefinition);
                        List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
                        updatedDefinitions.addAll(this.beanDefinitionNames);
                        updatedDefinitions.add(beanName);
                        this.beanDefinitionNames = updatedDefinitions;
                        if(this.manualSingletonNames.contains(beanName)) {
                            Set<String> updatedSingletons = new LinkedHashSet(this.manualSingletonNames);
                            updatedSingletons.remove(beanName);
                            this.manualSingletonNames = updatedSingletons;
                        }
                    }
                } else {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    this.beanDefinitionNames.add(beanName);
                    this.manualSingletonNames.remove(beanName);
                }
    
                this.frozenBeanDefinitionNames = null;
            }
    
            if(oldBeanDefinition != null || this.containsSingleton(beanName)) {
                this.resetBeanDefinition(beanName);//重置所有beanName缓存
            }
    
        }
    复制代码
    1. 通过别名注册BeanDefinition public void registerAlias(String name, String alias) { //如果beanName与alias相同的话 不记录alias,并删除对应的alias if(alias.equals(name)) { this.aliasMap.remove(alias); } else { String registeredName = (String)this.aliasMap.get(alias); if(registeredName != null) { if(registeredName.equals(name)) { return; }

      if(!this.allowAliasOverriding()) {
                  }
      
                  this.checkForAliasCircle(name, alias);
                  //注册
                  this.aliasMap.put(alias, name);
              }
      
          }
      复制代码
原文  https://juejin.im/post/5bc051d7e51d450e63226636
正文到此结束
Loading...