問題
公司有一個整合開發平臺,匯入資料庫表會自動生成實體類、mapper和xml等檔案,這是一件很方便的事,可以省去很多沒有技術性的重複工作。
但是最近我在使用這個平臺的時候遇到了一個問題,那就是mapper衝突問題。當老表進行匯入的時候,會生成與之前專案中已有mapper一樣的名字,比如原專案中有個PersonMapper.java的檔案,匯入後就會有兩個PersonMapper.java,這樣就會衝突報錯,導致專案無法啟動。
怎麼解決呢?將原先的mapper命名進行修改?顯然是不現實的,也違背了開放封閉原則。
原因
首先需要明確,為什麼兩個一樣命名的mapper檔案會衝突,因為在在進行物件注入的時候,@Mapper
在生成beanName的時候預設把首字母小寫之後返回類名,如果存在兩個命名完全一樣的檔案返回的beanName也是一樣的,因此@Resource
會不知道注入哪一個從而衝突。
清楚原因後,要做的就是當注入的時候怎麼區分這兩個mapper檔案,進行不同的注入。
解決
在思考一番後,決定使用@MapperScan
去解決這個問題,核心在於使用其中的nameGenerator
欄位。
@MapperScan中的nameGenerator繼承於BeanNameGenerator
而在Spring的容器中,是使用BeanNameGenerator去命名檢測到的元件,可以透過重寫nameGenerator方法去重新命名檢測到的元件,具體解決程式碼如下所示:
@Configuration
@MapperScan(
value = {"xxx", "xxx"},
sqlSessionFactoryRef = "sqlSessionFactory",
nameGenerator = ComposerNameGenerator.class
)
public class ComposerNameGenerator implements BeanNameGenerator {
public ComposerNameGenerator() {
}
// definition 是被生成名字的BeanDefinition例項;
// registry是生成名字後註冊進的BeanDefinitionRegistry。
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
// 拿到Bean定義資訊裡面的BeanClassName全類名
// 注意這個不是必須的,因為如果是繼承關係,配上父類的依舊行了
String generatedBeanName = definition.getBeanClassName();
if (generatedBeanName == null) {
// 若沒有配置本類全類名,去拿到父類的全類名+$child"倆表示自己
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
}
// 工廠Bean的 就用方法的名字+"$created"
else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName() + "$created";
}
}
// 若一個都沒找到,拋錯
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither 'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
} else {
return generatedBeanName;
}
}
}
這樣配置後,即使是一樣命名的mapper檔案,只要不在同一個包中依舊可以正常注入。