一句話總結:
@ConditionalOnMissingBean 是 Spring Boot 提供的一個 條件注解(Conditional Annotation),意思是:
只有當 Spring 容器中 不存在 某個 Bean 時,當前的 Bean 或配置才會被加載。
這是一種典型的“按需裝配”策略,常用于自動配置類(Auto Configuration)中。
如果大家有看源碼的習慣,會發現到處都有這個注解:
RestTemplate:
Jackson - ObjectMapper:
-
Spring Boot 默認提供的 JSON 解析工具?ObjectMapper
-
如果你沒自己定義一個,它就幫你創建一個默認的
-
你可以完全覆蓋它,比如配置命名策略、日期格式等
框架或者組件庫想要提供一個默認實現,但又允許使用者自定義自己的實現。這個時候就可以使用?@ConditionalOnMissingBean。這就達到了 可擴展+默認兜底 的目的。
同理,還有個相對應的是:@ConditionalOnBean?
對比:@ConditionalOnBean?vs?@ConditionalOnMissingBean
注解 | 含義 |
---|---|
@ConditionalOnBean(SomeClass.class) | 只有當容器中存在 SomeClass 類型的 Bean 時才執行 |
@ConditionalOnMissingBean(SomeClass.class) | 只有當容器中不存在 SomeClass 類型的 Bean 時才執行 |
這兩個注解是互補的,分別用于不同的場景。
支持的用法形式
@ConditionalOnMissingBean(SomeClass.class) // 類型判斷
@ConditionalOnMissingBean(name = "someBeanName") // 名稱判斷
@ConditionalOnMissingBean(value = SomeClass.class) // 等價于上面的類型判斷
@ConditionalOnMissingBean(type = "com.example.SomeClass") // 用字符串形式避免類加載
你也可以組合使用,如:
@ConditionalOnMissingBean(value = SomeInterface.class,name = "customBean"
)
特別注意:
不能直接加在?@Component、@Service、@Controller?等用于直接注冊?Bean?的注解上,因為?Spring?在解析這些注解時并不會走自動配置的判斷邏輯,因此不會觸發?@ConditionalOnMissingBean?的條件判斷,最終會導致Bean?被強制注冊或?注冊失敗報錯!
代碼示例
自定義一個接口:
public interface MyService {void hello();
}
錯誤示例
先來一個錯誤示例(直接在@Component下加上了):
@Slf4j
@Component
@ConditionalOnMissingBean(MyService.class)
public class MyServiceDefault implements MyService {@Overridepublic void hello() {log.info("use default bean...");}
}
?項目不會報錯,直接啟動后查找Bean:
會發現根本沒注冊上。所以,雖然加在@Component等注解的類上語法允許,但 Spring 不會進行條件判斷和直接注冊,可能導致意料之外的報錯。所以不要這么用。
正確用法:
@Configuration?+?@Bean?的方式(大家回頭看看我最上面截的源碼截圖中,也都是這種方式)
@Configuration
public class MyServiceAutoConfiguration {@Bean@ConditionalOnMissingBean(MyService.class)public MyService myService() {return new MyServiceDefault();}
}
默認實現類:
@Slf4j
public class MyServiceDefault implements MyService {@Overridepublic void hello() {log.info("use default bean...");}
}
找個地方調用一下:
public class StarApplication {@Autowiredprivate MyService myService;public static void main(String[] args) {SpringApplication.run(StarApplication.class, args);}@PostConstructpublic void test() {myService.hello();}
}
現在模擬用戶實現了這個接口:
@Slf4j
@Component
public class MyServiceUser implements MyService {@Overridepublic void hello() {log.info("use self bean...");}
}
可以看到就一個Bean:
當然,你也可以去掉這個注解試試:
再次運行:
可以看到兩個地方都注冊上了。