Spring Boot 2.2.0.RELEASE及以上版本自定义@Profile注解实现Bean按环境创建时出现冲突的问题
这个问题的核心在于Spring Boot 2.2.x及后续版本对元注解形式的@Profile处理逻辑发生了变化,导致你的自定义Profile注解没有被正确识别为条件过滤注解。
为什么升级后会抛出冲突异常?
在Spring Boot 2.1.x版本中,Spring Framework会自动递归解析自定义注解上的元注解,并将@Profile的条件逻辑应用到被标注的Bean上——简单说就是,你的@Dev注解因为元注解是@Profile("dev"),所以Spring会把它当成和@Profile("dev")一样的条件来过滤Bean,只会创建符合当前激活profile的实例。
但从Spring Boot 2.2.x(对应Spring Framework 5.2.x)开始,Spring对条件注解的元注解传播机制做了调整:默认情况下,像@Profile这类条件注解的逻辑不会自动通过自定义注解传递下去。这就导致你的@Dev和@Prod注解失去了profile过滤的能力,Spring不再根据激活的profile筛选Bean,而是尝试创建所有标注了@Component的类——两个同名的facadeBean自然就冲突了。
为什么直接用@Profile就正常?
直接使用@Profile注解时,Spring可以直接识别这是一个原生的条件注解,会严格执行profile匹配逻辑,只实例化符合当前激活profile的Bean,不会同时创建两个同名实例,因此不会触发冲突异常。
修复方案:让自定义Profile注解重新生效
要让你的自定义注解在新版本中恢复profile过滤功能,只需要给自定义注解添加@Conditional(ProfileCondition.class)元注解即可——因为@Profile本身就是基于ProfileCondition这个条件类来实现过滤逻辑的,显式添加这个注解后,Spring就能正确识别自定义注解的profile条件了。
修改后的自定义注解代码:
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.ProfileCondition; import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Profile("dev") @Conditional(ProfileCondition.class) public @interface Dev { } @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Profile("prod") @Conditional(ProfileCondition.class) public @interface Prod { }
这样修改后,Spring就会像处理原生@Profile注解一样,根据当前激活的profile来筛选对应的Bean,不会再出现同名Bean冲突的问题。
内容的提问来源于stack exchange,提问作者Victor1125

