上周遇到一个的注入bean的问题,而且是发生在常用的@Autowired注解上,于是点开源码,调试到报错的代码,进行探究。本文有点长,而且源码居多,目前还不太会组织读源码的文章,因为Spring实在是太过庞大,写的不清楚,就当记录了。
问题描述
项目是同时由xml和注解配置的bean,通常xml里面引入的是外部服务的bean,在其中引入了bean如下
<bean id="classA" class="com.java.packagea.ClassA" init-method="init"></bean>
通过注解引入另一个bean如下:
package com.java.packageb@Service("classA")public class ClassA{}
在某个ClassB中,需要引入packageB中的ClassA,如下
package com.java.packagecimport com.java.packageb.ClassA@Componentpublic class ClassB{@AutowiredClassA classA;}
启动Spring报错can not autowired class[com.java.packageb.ClassA]。
经排查,在Spring的BeanFactory中,只加载了xml的bean,没有加载注解注释的同名bean。
Spring加载Bean的源码
开始读源码,先找到 AbstractApplicationContext
,通篇看一下这个类的目录结构,我们知道AbstractApplicationContext最顶层继承的是BeanFactory,其重要用途就是加载Bean和获取Bean,抛开细枝末节,来到refresh()方法,在refresh的第一行打一个断点,可以大致看到容器启动以后是如何调用到refresh()方法,可以看到web.xml中配置的 ContextLoaderListener
中有一个 initWebApplicationContext
方法,继而调用到 ContextLoader
的 initWebApplicationContext
方法,最终会调用到ApplicationContext的具体实现类的refresh方法。Spring中大量使用了模板方法,在父类中写了方法调用流程,而具体的方法实现由子类完成,所以读源码的时候可以从基类开始读,需要看具体实现时再打开子类查看。回到 AbstractApplicationContext
的refresh方法,和Bean加载相关的主要看前两句,如下:
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.// 检查环境中的占位符是否完整prepareRefresh();// Tell the subclass to refresh the internal bean factory.// 将需要注入的业务bean转化为beandefinition,初始化BeanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);//后续代码省略}}
进入obtainFreshBeanFactory()看下具体做了哪些事
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {refreshBeanFactory();ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (logger.isDebugEnabled()) {logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);}return beanFactory;}
首先这是一个抽象方法,所以具体的实现是在子类中的,从方法上的注释可以看出这个是初始化BeanFactory的方法,我们点击这个refreshBeanFactory(),可以看到是个抽象方法
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
进入具体的实现类,可以看到AbstractApplicationContext下有很多实现类,又分为两个分支,一个基类是 AbstractRefreshableApplicationContext
,一个是 GenericApplicationContext
,从名字和
注释可以了解到前者是可以多次调用refresh()方法,后者只能调用一次refresh()方法,找到 AbstractRefreshableApplicationContext
下面的 refreshBeanFactory
方法,如下
@Overrideprotected final void refreshBeanFactory() throws BeansException {//如果已有beanFactory,执行cleanif (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {//创建默认的DefaultListableBeanFactoryDefaultListableBeanFactory beanFactory = createBeanFactory();beanFactory.setSerializationId(getId());// 设置BeanFactory的一些参数,是否可以循环引用,是否可以覆盖等customizeBeanFactory(beanFactory);// 关键的一步,将bean definition加载到 bean factoryloadBeanDefinitions(beanFactory);synchronized (this.beanFactoryMonitor) {this.beanFactory = beanFactory;}}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}}
继续看,loadBeanDefinitions是个抽象方法,我们点到一个子类实现,也就是说进入了AbstractRefreshableApplicationContext的子类中,我们找到xml配置对应的web项目的子类实现, XmlWebApplicationContext
,实现如下:
@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// Create a new XmlBeanDefinitionReader for the given BeanFactory.// 生成beanFactory对应的XmlBeanDefinitionReader,实际上是将beanFactory注入,在后续方法设置对应的值XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// Configure the bean definition reader with this context's// resource loading environment.// 环境配置beanDefinitionReader.setEnvironment(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);// 加载BeanDefinitionsloadBeanDefinitions(beanDefinitionReader);}
继续看下loadBeanDefinitions里面实际做了什么,在这个类中继续找这个方法(而不是直接看哪里调用了这个类,这样会跳到父类的抽象方法里)
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {//加载Resource列表,看子类实现,从classPath加载xml或者从文件加载ResourceResource[] configResources = getConfigResources();//如果加载到Resources,直接用reader解析if (configResources != null) {reader.loadBeanDefinitions(configResources);}//没有加载到Resources,用过configLocation地址进行解析//即看子类是否自己完成加载,如果没有则父类用location完成Resource的注入String[] configLocations = getConfigLocations();if (configLocations != null) {reader.loadBeanDefinitions(configLocations);}}
而后我们进入到 XmlBeanDefinitionReader
,进入父类 AbstractBeanDefinitionReader
找到 loadBeanDefinitions(Stringlocation)
方法,可以看到在通过地址找配置时,最终是通过自己实现的ResourcePatternResolver解析自定义的路径下的配置,转化为Resource数组。
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {ResourceLoader resourceLoader = getResourceLoader();if (resourceLoader == null) {throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");}if (resourceLoader instanceof ResourcePatternResolver) {// Resource pattern matching available.try {//用自定义的ResourcePatternResolver加载location下面的配置Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);int loadCount = loadBeanDefinitions(resources);if (actualResources != null) {for (Resource resource : resources) {actualResources.add(resource);}}...省略
对于有配置的情况,
@Overridepublic int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {Assert.notNull(resources, "Resource array must not be null");int counter = 0;for (Resource resource : resources) {//由子类XmlBeanDefinitionReader实现具体加载方法counter += loadBeanDefinitions(resource);}return counter;}
再次回到子类XmlBeanDefinitionReader中找到loadBeanDefinitions方法,先转换为一个包装类,然后进入load方法
@Overridepublic int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {// 包装为有编码的Resourcereturn loadBeanDefinitions(new EncodedResource(resource));}public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {Assert.notNull(encodedResource, "EncodedResource must not be null");if (logger.isInfoEnabled()) {logger.info("Loading XML bean definitions from " + encodedResource.getResource());}Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();//这里是一处防止循环加载的设计,将已经加载的EncodedResource放入Set<EncodedResource>类型的Threadlocal对象,每次往里面添加,如果set失败说明重复引用了Source,会报错循环加载if (currentResources == null) {currentResources = new HashSet<EncodedResource>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}if (!currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}try {InputStream inputStream = encodedResource.getResource().getInputStream();try {InputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());}// 进行加载的实际方法return doLoadBeanDefinitions(inputSource, encodedResource.getResource());}finally {inputStream.close();}}catch (IOException ex) {throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);}finally {currentResources.remove(encodedResource);if (currentResources.isEmpty()) {this.resourcesCurrentlyBeingLoaded.remove();}}}
上面通过HashSet类的ThreadLocal来保证重复引用Source的时候抛出异常,我们继续进入 doLoadBeanDefinitions
,截取前半部分代码
try {//将流转化为Document对象,这部分是Dom处理和Spring本身关系不大,略过Document doc = doLoadDocument(inputSource, resource);//注册beanreturn registerBeanDefinitions(doc, resource);}...异常处理省略
直接进入上面的registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {//documentReader DefaultBeanDefinitionDocumentReader为实际的类型BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();documentReader.setEnvironment(getEnvironment());int countBefore = getRegistry().getBeanDefinitionCount();//开始注册dom中的具体内容documentReader.registerBeanDefinitions(doc, createReaderContext(resource));return getRegistry().getBeanDefinitionCount() - countBefore;}
继续进入的registerBeanDefinitions方法
@Overridepublic void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;logger.debug("Loading bean definitions");//获得root节点,开始解析Element root = doc.getDocumentElement();//解析doRegisterBeanDefinitions(root);}
进入解析方法
protected void doRegisterBeanDefinitions(Element root) {// Any nested <beans> elements will cause recursion in this method. In// order to propagate and preserve <beans> default-* attributes correctly,// keep track of the current (parent) delegate, which may be null. Create// the new (child) delegate with a reference to the parent for fallback purposes,// then ultimately reset this.delegate back to its original (parent) reference.// this behavior emulates a stack of delegates without actually necessitating one.BeanDefinitionParserDelegate parent = this.delegate;//这段逻辑防止递归调用时不断生成子delegate,每次操作完以后将初始的delete重新赋值回原来的值this.delegate = createDelegate(getReaderContext(), root, parent);if (this.delegate.isDefaultNamespace(root)) {String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {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;}
继续进入到 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;//处理默认命名空间的bean,例如http://www.springframework.org/schema/beans 开头的内置的beanif (delegate.isDefaultNamespace(ele)) {parseDefaultElement(ele, delegate);}else {//其他类型的外部命名空间的bean,比如公司自建的组件的beandelegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}}
进入 parseDefaultElement
,发现有针对不同类型的解析,其中跳过import,alias的解析,直接进入bean的解析
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {importBeanDefinitionResource(ele);}else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {processAliasRegistration(ele);}//这一步是bean解析else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {processBeanDefinition(ele, delegate);}else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// recursedoRegisterBeanDefinitions(ele);}}
可以看到上面是把最开始的delegate委托类一直引入到这个方法中了,进入processBeanDefinition,
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {//这一句即为实际的解析,其中BeanBeanDefinitionHolder 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));}}
直接进入delegete委托类 BeanDefinitionParserDelegate
找到parseBeanDefinitionElement方法,我们分解一下这块代码,太长,首先前部分是做一些校验和预处理
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {//获取id标签对应的值String id = ele.getAttribute(ID_ATTRIBUTE);//获取name标签你对应的值String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);//将nameStr转化为数组,即别名的数组List<String> aliases = new ArrayList<String>();if (StringUtils.hasLength(nameAttr)) {String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);aliases.addAll(Arrays.asList(nameArr));}//如果有id,则id即为beanName,否则用别名做nameString beanName = id;if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {beanName = aliases.remove(0);if (logger.isDebugEnabled()) {logger.debug("No XML 'id' specified - using '" + beanName +"' as bean name and " + aliases + " as aliases");}}if (containingBean == null) {//这里会对beanName的唯一性进行判断checkNameUniqueness(beanName, aliases, ele);}....
查看 checkNameUniqueness
方法,可以看到如果beanName即xml中的id如果重复会报错,在userNames里面维护了一个列表,通过校验的beanName会存放在这个Set里面。
protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) {String foundName = null;if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) {foundName = beanName;}if (foundName == null) {foundName = CollectionUtils.findFirstMatch(this.usedNames, aliases);}if (foundName != null) {error("Bean name '" + foundName + "' is already used in this <beans> element", beanElement);}this.usedNames.add(beanName);this.usedNames.addAll(aliases);}
继续查看上面的方法,进入到解析部分,解析部分调用到了这个方法:
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {//将beanName放入一个栈结构中,用于追踪当前解析到哪里this.parseState.push(new BeanEntry(beanName));String className = null;if (ele.hasAttribute(CLASS_ATTRIBUTE)) {className = ele.getAttribute(CLASS_ATTRIBUTE).trim();}try {String parent = null;if (ele.hasAttribute(PARENT_ATTRIBUTE)) {parent = ele.getAttribute(PARENT_ATTRIBUTE);}//创建AbstractBeanDefinition对象AbstractBeanDefinition bd = createBeanDefinition(className, parent);//用element对bd赋值parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));parseMetaElements(ele, bd);parseLookupOverrideSubElements(ele, bd.getMethodOverrides());parseReplacedMethodSubElements(ele, bd.getMethodOverrides());parseConstructorArgElements(ele, bd);parsePropertyElements(ele, bd);parseQualifierElements(ele, bd);bd.setResource(this.readerContext.getResource());bd.setSource(extractSource(ele));//返回bdreturn bd;}...}
读到这里大致清楚了如何将XMl转化为Java类即Spring认识的BeanDefinition,流程大致是将xml文件通过XmlBeanDefinitionReader将流组装成的Resource进行解析,reader将Resource转成Document,再委托BeanDefinitionParserDelegate类来解析Document中的每个Element。最终将Element解析为BeanDefinition
回到XmlBeanDefinitionReader,我们看下解析完成以后,reader又做了什么
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.// 拿到BeanDefinitionHolder,对Bean进行注册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));}}
看下注册具体有哪些操作,进入BeanDefinitionReaderUtils
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {// Register bean definition under primary name.String beanName = definitionHolder.getBeanName();// 对bean和BeanDefinition进行注册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
@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}BeanDefinition oldBeanDefinition;oldBeanDefinition = this.beanDefinitionMap.get(beanName);if (oldBeanDefinition != null) {//如果存在这个BeanName且不允许继承Bean 抛出异常if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +"': There is already [" + oldBeanDefinition + "] bound.");}else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTUREif (this.logger.isWarnEnabled()) {this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +"' with a framework-generated bean definition: replacing [" +oldBeanDefinition + "] with [" + beanDefinition + "]");}}else {if (this.logger.isInfoEnabled()) {this.logger.info("Overriding bean definition for bean '" + beanName +"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");}}}//beanDefinitionNames 添加beanName//beanDefinitionMap 中添加beanName和beanDefinitionelse {this.beanDefinitionNames.add(beanName);this.manualSingletonNames.remove(beanName);this.frozenBeanDefinitionNames = null;}this.beanDefinitionMap.put(beanName, beanDefinition);if (oldBeanDefinition != null || containsSingleton(beanName)) {resetBeanDefinition(beanName);}}
读完上面这段我们可以知道,最终是将XML中的配置转化为了BeanDefinition,并且将BeanName和BeanDefinition存放在Map中,供后续调用。
从上面我们也可以知道,如果是XML注入的话,回去看BeanFactory中是否已经有同名的BeanName,如果有且配置为不允许复写,才会报错,否则后面注入的bean将前面的bean覆盖。
然后我们来快速看看注解形式是如何生效的,首先我们在xml中配置
<context:component-scan base-package="com.xxx"/>
我们看如何调用到注解的bean,首先回到 DefaultBeanDefinitionDocumentReader
这个类的委托这段代码,当不是http://www.springframework.org/schema/beans的命名空间时,调用下面这个方法
if (delegate.isDefaultNamespace(ele)) {parseDefaultElement(ele, delegate);}else {delegate.parseCustomElement(ele);}
进入这个方法,看到
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {String namespaceUri = getNamespaceURI(ele);NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}//进入这个方法return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}
说实话,看到这里有点晕,因为这个readerContext是前面注入进来的,大致可以猜出是某个地方set了一个map这里又拿出来,一通回看时候我已经彻底晕了。决定删繁就简先看下我要关注的问题,直接debug过来,发现handler.parse走到了NamespaceHandlerSupport这个类。
@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) {return findParserForElement(element, parserContext).parse(element, parserContext);}
继续下去,最终通过这个map映射到了实际的执行类里面ComponentScanBeanDefinitionParser
public BeanDefinition parse(Element element, ParserContext parserContext) {String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);// Actually scan for bean definitions and register them.ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);//这一步 注册所有的注解beanSet<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);registerComponents(parserContext.getReaderContext(), beanDefinitions, element);return null;}
进去看注入的bean怎么操作
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();for (String basePackage : basePackages) {Set<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}if (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}//这里有个校验,很容易看出,校验通过就进入注册bean到Beanfactoryif (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}
所以我们看看这个check方法是做什么的
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {//如果beanFactory里面没有这个bean,直接return true 进行registerif (!this.registry.containsBeanDefinition(beanName)) {return true;}//找到beanFactory中的该beanName对应的beanBeanDefinition existingDef = this.registry.getBeanDefinition(beanName);BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();if (originatingDef != null) {existingDef = originatingDef;}// 关键判断如果判断为true 则不会注入 我们看看判断是什么if (isCompatible(beanDefinition, existingDef)) {return false;}throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");}
在BeanFactory里面有个关键判断,如果为true则会跳过后面的注入逻辑,我们看看这里判断是什么
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {return (!(existingDefinition instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding beannewDefinition.getSource().equals(existingDefinition.getSource()) || // scanned same file twicenewDefinition.equals(existingDefinition)); // scanned equivalent class twice}
其中ScannedGenericBeanDefinition类基于ASM ClassReader,与AnnotatedGenericBeanDefinition不同,用于能够被Spring容器扫描到的类(例如被@Component注解的类), 这里的意思是,如果已经注册的类不是注解类,这个判断为true,外层返回false,就会跳过注册逻辑。
所以本文最开始的疑问得到解开。根据我们的注解组织顺序,xml的bean的引入时间早于component-scan配置,则先注入xml,当存在同名bean时,注解注入会因为上述原因(已存在BeanFactory的bean不是注解类型)跳过注册,导致最终注入的是xml里面的bean。反过来,先注入了注解的bean,随后加载到xml配置时,如果遇到同名的beanName,直接选择覆盖。因此无论顺序如何,最终同名的beanName注入在beanFactory里面的一定是xml里面的配置。
经过测试也验证了上述观点。




