spring系列之IOC容器实例化过程三

spring系列之IOC容器实例化过程三,第1张

spring系列之IOC容器实例化过程三 refresh方法中的obtainFreshBeanFactory方法

文章开始之前先啰嗦几句,写spring系列的文章的时候是想着每个星期更新一篇文章,但是上篇文章到现在这篇文章已经过了差不多两个星期,跟自己开始的预期偏离的有点远了,心里还是有点不是那么不得意的,所以趁着春节期间,我争取每天一篇将spring系列全部发完。希望各位看完能点点赞

回到正题,上回我们说到refresh的prepareRefresh方法,今天我们来说obtainFreshBeanFactory这个方法。先贴上refresh方法的代码:

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

再提一嘴,可能博主用的spring的版本和你们的不同,代码跟你们的有些不是那么一致,但是大家需要明白的是,springIOC的基本流程和结构是不会变的,至少到目前为止,还没有大的变化,因此spring版本不同并不影响我们学习springIOC的核心原理。

其实从obtainFreshBeanFactory这个方法的名称就大概能知道这个方法是获取一个刷新了的bean工厂,进入obtainFreshBeanFactory方法:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}

既然是获取一个刷新了的bean工厂,第一步调用refreshBeanFactory刷新bean工厂就显得那么顺理成章,此方法是一个抽象方法,需要子类去实现,最终调用的是AbstractRefreshableApplicationContext类中的方法,实现如下:

@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
		//如果已经存在,那么销毁之前的
			destroyBeans();
			closeBeanFactory();
		}
		try {
		//创建了一个DefaultListableBeanFactory对象,这个需要多加注意,之后这个bean工厂出现的次数有点多
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

AbstractRefreshableApplicationContext.customizeBeanFactory方法用于给子类提供一个自由配置的机会,默认实现:

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    if (this.allowBeanDefinitionOverriding != null) {
        //默认false,不允许覆盖
        beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.allowCircularReferences != null) {
        //默认false,不允许循环引用
        beanFactory.setAllowCircularReferences(this.allowCircularReferences);
    }
}

AbstractXmlApplicationContext.loadBeanDefinitions,这个便是核心的bean加载了:

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    // Configure the bean definition reader with this context's
    // resource loading environment.
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    // Allow a subclass to provide custom initialization of the reader,
    // then proceed with actually loading the bean definitions.
    //默认空实现
    initBeanDefinitionReader(beanDefinitionReader);
    loadBeanDefinitions(beanDefinitionReader);
}

XmlBeanDefinitionReader、ResourceLoader、BeanDefinitionReader这几个词汇应该都不陌生了,在springIOC的容器结构的时候就做了一个详细的介绍,这里就不过多介绍了,我们来说一下EntityResolver,EntityResolver接口在org.xml.sax中定义。DelegatingEntityResolver用于schema和dtd的解析(schema和dtd是xml文件的约束,想详细了解的可以去问度娘)。
我们再来看看**loadBeanDefinitions(beanDefinitionReader);**方法:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) {
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
        reader.loadBeanDefinitions(configResources);
    }
    String[] configLocations = getConfigLocations();
    //here
    if (configLocations != null) {
        reader.loadBeanDefinitions(configLocations);
    }
}

AbstractBeanDefinitionReader.loadBeanDefinitions:

@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
    Assert.notNull(locations, "Location array must not be null");
    int counter = 0;
    for (String location : locations) {
        counter += loadBeanDefinitions(location);
    }
    return counter;
}

之后调用:

//第二个参数为空
public int loadBeanDefinitions(String location, Set actualResources) {
    ResourceLoader resourceLoader = getResourceLoader();
    //参见ResourceLoader类图,ClassPathXmlApplicationContext实现了此接口
    if (resourceLoader instanceof ResourcePatternResolver) {
        // Resource pattern matching available.
        try {
            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            int loadCount = loadBeanDefinitions(resources);
            if (actualResources != null) {
                for (Resource resource : resources) {
                    actualResources.add(resource);
                }
            }
            return loadCount;
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "Could not resolve bean definition resource pattern [" + location + "]", ex);
        }
    }
    else {
        // Can only load single resources by absolute URL.
        Resource resource = resourceLoader.getResource(location);
        int loadCount = loadBeanDefinitions(resource);
        if (actualResources != null) {
            actualResources.add(resource);
        }
        return loadCount;
    }
}

getResource的实现在AbstractApplicationContext:

@Override
public Resource[] getResources(String locationPattern) throws IOException {
    //构造器中初始化,PathMatchingResourcePatternResolver对象
    return this.resourcePatternResolver.getResources(locationPattern);
}

PathMatchingResourcePatternResolver是ResourceLoader继承体系的一部分。

@Override
public Resource[] getResources(String locationPattern) throws IOException {
    Assert.notNull(locationPattern, "Location pattern must not be null");
    //classpath:
    if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
        // a class path resource (multiple resources for same name possible)
        //matcher是一个AntPathMatcher对象
        if (getPathMatcher().isPattern(locationPattern
            .substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
            // a class path resource pattern
            return findPathMatchingResources(locationPattern);
        } else {
            // all class path resources with the given name
            return findAllClassPathResources(locationPattern
                .substring(CLASSPATH_ALL_URL_PREFIX.length()));
        }
    } else {
        // only look for a pattern after a prefix here
        // (to not get fooled by a pattern symbol in a strange prefix).
        int prefixEnd = locationPattern.indexOf(":") + 1;
        if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
            // a file pattern
            return findPathMatchingResources(locationPattern);
        }
        else {
            // a single resource with the given name
            return new Resource[] {getResourceLoader().getResource(locationPattern)};
        }
    }
}

isPattern:

@Override
public boolean isPattern(String path) {
    return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
}

可以看出配置文件路径是支持ant风格的,也就是可以这么写:

new ClassPathXmlApplicationContext("con*.xml");

然后就是配置文件的加载:

//加载
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//解析
int loadCount = loadBeanDefinitions(resources);

最终逐个调用XmlBeanDefinitionReader的loadBeanDefinitions方法:

@Override
public int loadBeanDefinitions(Resource resource) {
    return loadBeanDefinitions(new EncodedResource(resource));
}

EncodedResource扮演的其实是一个装饰器的模式,为InputStreamSource添加了字符编码(虽然默认为null)。这样为我们自定义xml配置文件的编码方式提供了机会。

之后关键的源码只有两行:

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    InputStream inputStream = encodedResource.getResource().getInputStream();
    InputSource inputSource = new InputSource(inputStream);
    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}

InputSource是org.xml.sax的类。
doLoadBeanDefinitions:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) {
    document doc = doLoaddocument(inputSource, resource);
    return registerBeanDefinitions(doc, resource);
}

doLoaddocument:

protected document doLoaddocument(InputSource inputSource, Resource resource) {
    return this.documentLoader.loaddocument(inputSource, getEntityResolver(), this.errorHandler,
        getValidationModeForResource(resource), isNamespaceAware());
}

documentLoader是一个DefaultdocumentLoader对象,此类是documentLoader接口的唯一实现。getEntityResolver方法返回ResourceEntityResolver,上面说过了。errorHandler是一个SimpleSaxErrorHandler对象。

校验模型其实就是确定xml文件使用xsd方式还是dtd方式来校验,忘了的话左转度娘。Spring会通过读取xml文件的方式判断应该采用哪种。

NamespaceAware默认false,因为默认配置了校验为true。

DefaultdocumentLoader.loaddocument:

@Override
public document loaddocument(InputSource inputSource, EntityResolver entityResolver,
    ErrorHandler errorHandler, int validationMode, boolean namespaceAware) {
    //这里就是老套路了,可以看出,Spring还是使用了dom的方式解析,即一次全部load到内存
    documentBuilderFactory factory = createdocumentBuilderFactory(validationMode, namespaceAware);
    documentBuilder builder = createdocumentBuilder(factory, entityResolver, errorHandler);
    return builder.parse(inputSource);
}

createdocumentBuilderFactory比较有意思:

protected documentBuilderFactory createdocumentBuilderFactory(int validationMode, boolean namespaceAware{
    documentBuilderFactory factory = documentBuilderFactory.newInstance();
    factory.setNamespaceAware(namespaceAware);
    if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
        //此方法设为true仅对dtd有效,xsd(schema)无效
        factory.setValidating(true);
        if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
            // Enforce namespace aware for XSD...
             //开启xsd(schema)支持
            factory.setNamespaceAware(true);
             //这个也是Java支持Schema的套路,可以问度娘
            factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
        }
    }
    return factory;
}

XmlBeanDefinitionReader.registerBeanDefinitions:

public int registerBeanDefinitions(document doc, Resource resource) {
    BeanDefinitiondocumentReader documentReader = createBeanDefinitiondocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

createBeanDefinitiondocumentReader:

protected BeanDefinitiondocumentReader createBeanDefinitiondocumentReader() {
    return BeanDefinitiondocumentReader.class.cast
      //反射
      (BeanUtils.instantiateClass(this.documentReaderClass));
}

documentReaderClass默认是DefaultBeanDefinitiondocumentReader,这其实也是策略模式,通过setter方法可以更换其实现。

注意cast方法,代替了强转。

createReaderContext:

public XmlReaderContext createReaderContext(Resource resource) {
    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
        this.sourceExtractor, this, getNamespaceHandlerResolver());
}

problemReporter是一个FailFastProblemReporter对象。

eventListener是EmptyReaderEventListener对象,此类里的方法都是空实现。

sourceExtractor是NullSourceExtractor对象,直接返回空,也是空实现。

getNamespaceHandlerResolver默认返回DefaultNamespaceHandlerResolver对象,用来获取xsd对应的处理器。

XmlReaderContext的作用感觉就是这一堆参数的容器,糅合到一起传给documentReader,并美其名为Context。可以看出,Spring中到处都是策略模式,大量 *** 作被抽象成接口。

DefaultBeanDefinitiondocumentReader.registerBeanDefinitions:

@Override
public void registerBeanDefinitions(document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    Element root = doc.getdocumentElement();
    doRegisterBeanDefinitions(root);
}

doRegisterBeanDefinitions:

protected void doRegisterBeanDefinitions(Element root) {
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);
    //默认的命名空间即
    //http://www.springframework.org/schema/beans
    if (this.delegate.isDefaultNamespace(root)) {
        //检查profile属性
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            //profile属性可以以,分割
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                return;
            }
        }
    }
    preProcessXml(root);
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);
    this.delegate = parent;
}

delegate的作用在于处理beans标签的嵌套,其实Spring配置文件是可以写成这样的:

    
    
    
    
        
    

xml(schema)的命名空间其实类似于java的报名,命名空间采用URL,比如Spring的是这样:

    

注意一下profile的检查, AbstractEnvironment.acceptsProfiles:

@Override
public boolean acceptsProfiles(String... profiles) {
    Assert.notEmpty(profiles, "Must specify at least one profile");
    for (String profile : profiles) {
        if (StringUtils.hasLength(profile) && profile.charAt(0) == '!') {
            if (!isProfileActive(profile.substring(1))) {
                return true;
            }
        } else if (isProfileActive(profile)) {
            return true;
        }
    }
    return false;
}

原理很简单,注意从源码可以看出,profile属性支持!取反。

preProcessXml方法是个空实现,供子类去覆盖,目的在于给子类一个把我们自定义的标签转为Spring标准标签的机会, 想的真周到。

DefaultBeanDefinitiondocumentReader.parseBeanDefinitions:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    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)) {
                    parseDefaultElement(ele, delegate);
                } else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    } else {
        delegate.parseCustomElement(root);
    }
}

可见,对于非默认命名空间的元素交由delegate处理。即import, alias, bean, 嵌套的beans四种元素。
parseDefaultElement:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    //"import"
    if (delegate.nodeNameEquals(ele, import_ELEMENT)) {
        importBeanDefinitionResource(ele);
    }
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    }
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        processBeanDefinition(ele, delegate);
    }
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // recurse
        doRegisterBeanDefinitions(ele);
    }
}

写法示例:



importBeanDefinitionResource套路和之前的配置文件加载完全一样,不过注意被import进来的文件是先于当前文件 被解析的。

加入有一个bean名为componentA-dataSource,但是另一个组件想以componentB-dataSource的名字使用,就可以这样定义:

 

processAliasRegistration核心源码:

protected void processAliasRegistration(Element ele) {
    String name = ele.getAttribute(NAME_ATTRIBUTE);
    String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
    getReaderContext().getRegistry().registerAlias(name, alias);
    getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}

从前面的源码可以发现,registry其实就是DefaultListableBeanFactory,它实现了BeanDefinitionRegistry接口。registerAlias方法的实现在SimpleAliasRegistry:

@Override
public void registerAlias(String name, String alias) {
    Assert.hasText(name, "'name' must not be empty");
    Assert.hasText(alias, "'alias' must not be empty");
    //名字和别名一样
    if (alias.equals(name)) {
        //ConcurrentHashMap
        this.aliasMap.remove(alias);
    } else {
        String registeredName = this.aliasMap.get(alias);
        if (registeredName != null) {
            if (registeredName.equals(name)) {
                // An existing alias - no need to re-register
                return;
            }
            if (!allowAliasOverriding()) {
                throw new IllegalStateException
                    ("Cannot register alias '" + alias + "' for name '" +
                    name + "': It is already registered for name '" + registeredName + "'.");
            }
        }
        checkForAliasCircle(name, alias);
        this.aliasMap.put(alias, name);
    }
}

所以别名关系的保存使用Map完成,key为别名,value为本来的名字。

bean节点是Spring最最常见的节点了。

DefaultBeanDefinitiondocumentReader.processBeanDefinition:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // Register the final decorated instance.
            BeanDefinitionReaderUtils.registerBeanDefinition
                (bdHolder, getReaderContext().getRegistry());
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to register bean definition with name '" +
                    bdHolder.getBeanName() + "'", ele, ex);
        }
        // Send registration event.
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

id & name处理
最终调用BeanDefinitionParserDelegate.parseBeanDefinitionElement(Element ele, BeanDefinition containingBean),源码较长,分部分说明。

首先获取到id和name属性,name属性支持配置多个,以逗号分隔,如果没有指定id,那么将以第一个name属性值代替。id必须是唯一的,name属性其实是alias的角色,可以和其它的bean重复,如果name也没有配置,那么其实什么也没做。

String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List aliases = new ArrayList();
if (StringUtils.hasLength(nameAttr)) {
    //按,分隔
    String[] nameArr = StringUtils.tokenizeToStringArray
        (nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
    aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
    //name的第一个值作为id
    beanName = aliases.remove(0);
}
//默认null
if (containingBean == null) {
    //校验id是否已重复,如果重复直接抛异常
    //校验是通过内部一个HashSet完成的,出现过的id都会保存进此Set
    checkNameUniqueness(beanName, aliases, ele);
}

如果name和id属性都没有指定,那么Spring会自己生成一个, BeanDefinitionParserDelegate.parseBeanDefinitionElement:

beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
aliases.add(beanClassName);

可见,Spring同时会把类名作为其别名。

最终调用的是BeanDefinitionReaderUtils.generateBeanName:

public static String generateBeanName(
        BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) {
    String generatedBeanName = definition.getBeanClassName();
    if (generatedBeanName == null) {
        if (definition.getParentName() != null) {
            generatedBeanName = definition.getParentName() + "$child";
             //工厂方法产生的bean
        } else if (definition.getFactoryBeanName() != null) {
            generatedBeanName = definition.getFactoryBeanName() + "$created";
        }
    }
    String id = generatedBeanName;
    if (isInnerBean) {
        // Inner bean: generate identity hashcode suffix.
        id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + 
            ObjectUtils.getIdentityHexString(definition);
    } else {
        // Top-level bean: use plain class name.
        // Increase counter until the id is unique.
        int counter = -1;
         //用类名#自增的数字命名
        while (counter == -1 || registry.containsBeanDefinition(id)) {
            counter++;
            id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
        }
    }
    return id;
}

还是分部分说明(parseBeanDefinitionElement)
首先获取到bean的class属性和parent属性,配置了parent之后,当前bean会继承父bean的属性。之后根据class和parent创建BeanDefinition对象

String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
    className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
    parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
AbstractBeanDefinition bd = createBeanDefinition(className, parent);

BeanDefinition的创建在BeanDefinitionReaderUtils.createBeanDefinition:

public static AbstractBeanDefinition createBeanDefinition(
        String parentName, String className, ClassLoader classLoader) {
    GenericBeanDefinition bd = new GenericBeanDefinition();
    bd.setParentName(parentName);
    if (className != null) {
        if (classLoader != null) {
            bd.setBeanClass(ClassUtils.forName(className, classLoader));
        }
        else {
            bd.setBeanClassName(className);
        }
    }
    return bd;
}

之后是解析bean的其它属性,其实就是读取其配置,调用相应的setter方法保存在BeanDefinition中:

parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

之后解析bean的decription子元素


    SimpleBean

就仅仅是个描述。

然后是meta子元素的解析,meta元素在xml配置文件里是这样的:


    

注释上说,这样可以将任意的元数据附到对应的bean definition上。解析过程源码:

public void parsemetaElements(Element ele, BeanmetadataAttributeAccessor attributeAccessor) {
    NodeList nl = ele.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        if (isCandidateElement(node) && nodeNameEquals(node, meta_ELEMENT)) {
            Element metaElement = (Element) node;
            String key = metaElement.getAttribute(KEY_ATTRIBUTE);
            String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
             //就是一个key, value的载体,无他
            BeanmetadataAttribute attribute = new BeanmetadataAttribute(key, value);
             //sourceExtractor默认是NullSourceExtractor,返回的是空
            attribute.setSource(extractSource(metaElement));
            attributeAccessor.addmetadataAttribute(attribute);
        }
    }
}

AbstractBeanDefinition继承自BeanmetadataAttributeAccessor类,底层使用了一个linkedHashMap保存metadata。这个metadata具体是做什么暂时还不知道。

lookup-method解析:

此标签的作用在于当一个bean的某个方法被设置为lookup-method后,每次调用此方法时,都会返回一个新的指定bean的对象。用法示例:




    

replace-mothod解析:

此标签用于替换bean里面的特定的方法实现,替换者必须实现Spring的MethodReplacer接口,有点像aop的意思。

配置文件示例:

  

    
        
      
 

解析之后将数据放在ReplaceOverride对象中,里面有一个linkedList专门用于保存arg-type。

构造参数(constructor-arg)解析:

作用一目了然,使用示例:


    
        Cat
    

type一般不需要指定,除了泛型集合那种。除此之外,constructor-arg还支持name, index, ref等属性,可以具体的指定参数的位置等。构造参数解析后保存在BeanDefinition内部一个ConstructorArgumentValues对象中。如果设置了index属性,那么以Map的形式保存,反之,以List的形式保存。

property解析:

非常常用的标签,用以为bean的属性赋值,支持value和ref两种形式,示例:


    

value和ref属性不能同时出现,如果是ref,那么将其值保存在不可变的RuntimeBeanReference对象中,其实现了BeanReference接口,此接口只有一个getBeanName方法。如果是value,那么将其值保存在TypedStringValue对象中。最终将对象保存在BeanDefinition内部一个MutablePropertyValues对象中(内部以ArrayList实现)。

qualifier解析:

配置示例:


    
    
    
	

    
    
    


SimpleBean部分源码:

@Autowired
@Qualifier("student")
private Student student;

此标签和@Qualifier, @Autowired两个注解一起使用才有作用。@Autowired注解采用按类型查找的方式进行注入,如果找到多个需要类型的bean便会报错,有了@Qualifier标签就可以再按照此注解指定的名称查找。两者结合相当于实现了按类型+名称注入。type属性可以不指定,因为默认就是那个。qualifier标签可以有attribute子元素,比如:


    

貌似是用来在qualifier也区分不开的时候使用。attribute键值对保存在BeanmetadataAttribute对象中。整个qualifier保存在AutowireCandidateQualifier对象中。

再讲个bean的注册:
BeanDefinitionReaderUtils.registerBeanDefinition:

public static void registerBeanDefinition(
    BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
    // Register bean definition under primary name.
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    // Register aliases for bean name, if any.
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}

registry其实就是DefaultListableBeanFactory对象,registerBeanDefinition方法主要就干了这么两件事:

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
    this.beanDefinitionMap.put(beanName, beanDefinition);
    this.beanDefinitionNames.add(beanName);
}

一个是Map,另一个是List,一目了然。registerAlias方法的实现在其父类SimpleAliasRegistry,就是把键值对放在了一个ConcurrentHashMap里。ComponentRegistered事件触发:默认是个空实现,前面说过了。

这篇文章的篇幅比较长,但是也是比较重要的一票希望大家看完都能有所收获,最后祝大家新年快乐!!!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5717036.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存