??
問題背景
在使用dubbo的時(shí)候突然想到如果不同提供者的類名一樣的話那調(diào)用時(shí)怎么辦(因?yàn)檎{(diào)用提供者的服務(wù)包名結(jié)構(gòu)必須要一致)披泪。于是找了一下這個(gè)問題
問題分析
(可能比較冗余,如果不想看可以直接跳結(jié)論)
java描述一個(gè)類(相同加載器假褪,相同虛擬機(jī))歼疮,確實(shí)是依賴類的完整名(包名+類名)杂抽,也就是說,不同包下的同名類韩脏,互相完全沒有關(guān)系缩麸,是兩個(gè)完全不同的類。但spring對(duì)這種類的處理卻和這種方式不同赡矢。很有趣的問題杭朱,因此決定把它搞清楚阅仔。
是spring中,被它所管理的類是通過BeanDefinition來描述的弧械,而注冊(cè)一個(gè)bean八酒,是通過這個(gè)函數(shù)registerBeanDefinition(String beanName, BeanDefinition beanDefinition)進(jìn)行的。
最終bean會(huì)被存儲(chǔ)在這樣一個(gè)map里:private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);
現(xiàn)在的報(bào)錯(cuò)原因刃唐,就是因?yàn)樵谧?cè)bean之前羞迷,對(duì)beanName做了唯一性驗(yàn)證,而這個(gè)驗(yàn)證正好失敗画饥。
我們繼續(xù)看spring的源碼
//掃碼獲取bean的主要函數(shù)
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); //獲取beanName
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) { //該步報(bào)錯(cuò),conflicts
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
現(xiàn)在問題變成了為什么beanNameGenerator.generateBeanName(candidate, this.registry);會(huì)返回類名衔瓮,而不是package+類名。
我們繼續(xù)看generateBeanName的實(shí)現(xiàn)抖甘,有兩個(gè)實(shí)現(xiàn)
DefaultBeanNameGenerator
AnnotationBeanNameGenerator
先看DefaultBeanNameGenerator的實(shí)現(xiàn)热鞍,它的實(shí)現(xiàn)相對(duì)簡(jiǎn)單,核心代碼是這句String generatedBeanName = definition.getBeanClassName();而它的實(shí)現(xiàn)如下
@Override
public String getBeanClassName() {
Object beanClassObject = this.beanClass;
if (beanClassObject instanceof Class) {
return ((Class<?>) beanClassObject).getName(); //返回類的完整名单山,packageName + className
}
else {
return (String) beanClassObject;
}
}
也就是說碍现,DefaultBeanNameGenerator返回的beanName是packageName+beanName。
總結(jié)
spring并不支持不同的包下類名相同的設(shè)定米奸。這是因?yàn)槟J(rèn)的spring檢索bean的唯一id(@Service,@Component等)為bean的name昼接,并不包含package name信息。想要規(guī)避這種問題有兩種方式
a 對(duì)bean顯式命名悴晰,@Service("yourName")
b 使用xml的方式聲明bean
在調(diào)用同名的類時(shí)第二個(gè)需要寫全包名結(jié)構(gòu)慢睡,使用 @Qualifier 注釋和 @Autowired 注釋通過指定哪一個(gè)真正的 bean 將會(huì)被裝配來消除混亂。如:
@RestController
public class TestController {
@Autowired
private TestService testService;
@Autowired @Qualifier("dTestService")
private com.example.demo.service.TestService t2;
@RequestMapping(value = "t")
public String testt(String str){
return t2.s1(str);
}
@RequestMapping(value = "d")
public String testd(String str){
return testService.testWord(str);
}
@RequestMapping(value = "test")
public String test(String str){
return "Hello "+str+" test";
}
}
@Qualifier限定描述符除了能根據(jù)名字進(jìn)行注入铡溪,更能進(jìn)行更細(xì)粒度的控制如何選擇候選者