You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

非Spring Boot环境下能否实现等效@ConditionalOnMissingBean的自定义条件

实现结论

完全可以实现。Spring 5.3.13 原生提供了条件装配扩展点,不需要引入Spring Boot依赖,就能复刻@ConditionalOnMissingBean的核心行为。

具体实现

1. 自定义条件注解

先定义和@ConditionalOnMissingBean用法一致的注解,支持指定要检查的Bean类型、Bean名称:

import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnMissingBeanCondition.class)
public @interface ConditionalOnMissingBean {
    // 要检查存在性的Bean类型
    Class<?>[] value() default {};
    // 要检查存在性的Bean名称
    String[] beanNames() default {};
}

2. 实现条件判断逻辑

注意不要直接实现Condition接口,要实现ConfigurationCondition接口,指定条件判断在Bean注册阶段执行,避免因为判断时机过早,其他Bean还没完成扫描导致结果错误:

import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.ConfigurationCondition;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.util.MultiValueMap;

import java.util.List;

public class OnMissingBeanCondition implements ConfigurationCondition {

    @Override
    public ConfigurationPhase getConfigurationPhase() {
        return ConfigurationPhase.REGISTER_BEAN;
    }

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        MultiValueMap<String, Object> annotationAttrs = metadata.getAllAnnotationAttributes(
                ConditionalOnMissingBean.class.getName()
        );

        // 注解没传参数时,默认检查当前标注的Bean类型是否已存在
        if (annotationAttrs == null || annotationAttrs.isEmpty()) {
            if (metadata instanceof MethodMetadata methodMetadata) {
                try {
                    Class<?> targetType = Class.forName(
                            methodMetadata.getReturnTypeName(),
                            false,
                            context.getClassLoader()
                    );
                    String[] existingBeans = context.getBeanFactory().getBeanNamesForType(targetType);
                    return existingBeans.length == 0;
                } catch (ClassNotFoundException e) {
                    return true;
                }
            }
            return true;
        }

        // 检查指定类型的Bean是否已存在
        List<Object> targetTypes = annotationAttrs.get("value");
        if (targetTypes != null) {
            for (Object type : targetTypes) {
                Class<?> clazz = (Class<?>) type;
                if (context.getBeanFactory().getBeanNamesForType(clazz).length > 0) {
                    return false;
                }
            }
        }

        // 检查指定名称的Bean是否已存在
        List<Object> targetBeanNames = annotationAttrs.get("beanNames");
        if (targetBeanNames != null) {
            for (Object beanName : targetBeanNames) {
                String name = (String) beanName;
                if (context.getBeanFactory().containsBeanDefinition(name)
                        || context.getBeanFactory().containsSingleton(name)) {
                    return false;
                }
            }
        }

        return true;
    }
}
使用方式

和Spring Boot提供的@ConditionalOnMissingBean用法完全一致,直接标注在@Bean方法或者组件类上即可:

@Configuration
public class DefaultServiceConfiguration {
    @Bean
    @ConditionalOnMissingBean(UserService.class)
    public UserService defaultUserService() {
        return new DefaultUserServiceImpl();
    }
}
注意事项
  • 必须指定条件判断的执行阶段为REGISTER_BEAN,如果用默认的PARSE_CONFIGURATION阶段,会出现部分Bean还没被扫描注册就提前判断,导致结果不符合预期
  • 上述实现覆盖了最常用的按类型、按名称判断的场景,如果需要支持按注解匹配、忽略指定类型、考虑父容器Bean等高级特性,只要在注解中新增对应参数,在matches方法中补充判断逻辑即可
  • 判断过程中不要调用getBean()方法提前实例化Bean,仅通过BeanFactory检查Bean定义、已注册的单例对象即可,避免破坏Spring Bean的生命周期

内容的提问来源于stack exchange,提问作者Itzhak Eretz Kdosha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.22 16:12:26