在开发过程中命名了一个MonitorTask类,该类上面加了@Component的注解
然后在另一个名为MqConsumer的类中,使用@Autowired来装配
结果在项目启动时报错,提示信息:
Field monitorTask in com.xxx.xxxx.mq.MqConsumer required a bean of type 'com.xxx.xxx.monitor.MonitorTask' that could not be found.
看提示信息像是MonitorTask没有被扫描进入Spring容器,我反复检查了启动类和该类的位置,import的引入位置,发现并无异常。
然后我按照没扫描进去进行排查,先找到ClassPathBeanDefinitionScanner,在其doScan这里加了断点,这里根据启动类的路径扫描符合要求BeanDefinition。
// ClassPathBeanDefinitionScanner
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) {
// 这里是获取到了路径下所有class文件,然后筛选出了符合要求Bean封装成BeanDefinition返回
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 获取beanName 优先使用注解上标明的 没有就类名首字母小写
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
// 处理加了注解的类
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 检查
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 当前BeanDefinition注册到容器
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
// 这个方法检查要扫描出的beanDefinition是否和容器内部的有冲突
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
if (!this.registry.containsBeanDefinition(beanName)) {
return true;
}
// 拿到当前容器存在相同beanName的BeanDefinition
BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
// 拿到原始的BeanDefinition
BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
if (originatingDef != null) {
existingDef = originatingDef;
}
// 原始BeanDefinition 和 当前BeanDefinition 比较
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() + "]");
}
在findCandidateComponents()上打了断点,检查到返回的BeanDefinition中有MonitorTask,
在checkCandidate()处打了条件断点,条件是"monitorTask".equals(beanName)眼看着通过了检查,并且注册到了容器。检查返回的beanDefinitions中也有MonitorTask对应的BeanDefinition
然后我又找到开始实例化的位置,在DefaultListableBeanFactory的preInstantiateSingletons()这里
public void preInstantiateSingletons() throws BeansException {
if (logger.isDebugEnabled()) {
logger.debug("Pre-instantiating singletons in " + this);
}
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
// 实例化所有剩余的(非延迟初始化)单例。
for (String beanName : beanNames) {
// 容器里拿出BeanDefinition
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 判断不是抽象的 是单例的 不是懒加载的
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// FactoryBean类型的
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
// 普通单例bean
getBean(beanName);
}
}
}
// .....
}
在getMergedLocalBeanDefinition(beanName)打条件断点"monitorTask".equals(beanName),发现获取到的是com.xxx.xxx.rpc.monitor.task.MonitorTask,不是原来那个com.xxx.xxx.monitor.MonitorTask,我又去找这个类,发现是依赖的jar包中通过spring.factories文件的配置加上@Bean注入到容器中的,然后这个人写代码的时候直接使用的第一个首字母小写的方式命名的方法名。导致beanName和我创建的重复了,我写的那个应该是被覆盖掉了。
因为公司项目的中有很多干扰,知道原因后我想看一下在哪覆盖的,所以我就自己新创建了一个项目,定义了两个不同全类名但是类名相同的类,一个加注解,另一个通过@Bean这种方式,方法名使用第一个首字母小写的方式。
在新创建的项目中我找到了ConfigurationClassPostProcessor在processConfigBeanDefinitions()方法this.reader.loadBeanDefinitions(configClasses);打断点
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
然后找到对应@Configuration的configClass进入loadBeanDefinitionsForConfigurationClass
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// 使用条件注解判断是否需要跳过这个配置类
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
// 如果自身是被@Import注释修饰,注册自己
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 将@Bean方法注册为bean
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
// 获取注解元数据
MethodMetadata metadata = beanMethod.getMetadata();
// 获取方法名
String methodName = metadata.getMethodName();
// 判断方法上的@Conditional注解,判断是否需要跳过
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
if (configClass.skippedBeanMethods.contains(methodName)) {
return;
}
// 获取@Bean注解上的属性
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
Assert.state(bean != null, "No @Bean annotation attributes");
List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
// 如果names不为null就使用name属性的第一个值作为beanName,不然使用方法名
String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
for (String alias : names) {
this.registry.registerAlias(beanName, alias);
}
// 判断是否存在和该beanName相同的已经存在的bean,如果已经存在,是否允许覆盖
// 这里因为是一开始的bean是因为扫描注册的,这里返回的是false
if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
"' clashes with bean name for containing configuration class; please make those names unique!");
}
return;
}
// 将configClass转换为ConfigurationClassBeanDefinition
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
// ....
// 注册到容器
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 根据beanName获取BeanDefinition
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
// 存在
if (existingDefinition != null) {
// 不允许覆盖就报错
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
// .....
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// 检查该工厂的bean创建阶段是否已经开始,也就是说,在此期间是否有任何bean被标记为已创建。
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
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 (existingDefinition != null || containsSingleton(beanName)) {
// 清理原来的BeanDefinition
resetBeanDefinition(beanName);
}
}
在isAllowBeanDefinitionOverriding()这里默认是true 但是在调试的时候发现在prepareContext的时候被设置成了false
结果报错信息不是原来项目的错误
The bean 'test', defined in class path resource [com/xxx/config/MyConfig.class],
could not be registered. A bean with that name has already been defined in file
发现是不能注册进去的错误,然后又重新去调试了下原来的项目,发现就是这个是否运行覆盖的配置,如果允许的话,就会重新put进新的,这在自动装配的时候才会发现没有注册到容器。
上面是1.5.22.RELEASE也就是原来那个项目的prepareContext(),这里没设置allowBeanDefinitionOverriding,所以就是默认的true。
而我自己新建的项目是2.1.1RELEASE,在prepareContext()这里设置了allowBeanDefinitionOverriding,设置成了false。