文章开始之前先啰嗦几句,写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, SetactualResources) { 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); Listaliases = 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
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事件触发:默认是个空实现,前面说过了。
这篇文章的篇幅比较长,但是也是比较重要的一票希望大家看完都能有所收获,最后祝大家新年快乐!!!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)