spring注入怎么样设置默认值?

可能标题不准确,描述一下问题:

有这么一个接口:

public interface InterceptorPredicate {
    default boolean allowed(HttpServletRequest request) {
        return true;
    }
}

然后有个拦截器:

public class MyInterceptor implements HandlerInterceptor {
    private InterceptorPredicate predicate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (predicate.allowed(request)) {
            do something...
        }
        return true;
    }

    public MyInterceptor(InterceptorPredicate predicate) {
        this.predicate = predicate;
    }
}

我要怎么样做到说如果外部有实现了InterceptorPredicate的bean,那么就用外部的,否则用默认的实现类?

就像DataSource可以用spring自己默认的,也可以用自定义的一样。

阅读 4.1k
2 个回答

这个可以用 FactoryBean 来解决
业务类依赖 FactoryBean, FactoryBean 里面的 getObject 方法根据实际情况构造需要的对象 (例如你可以在那里访问 ApplicationContext, 能捞得到对象就返回捞到的对象, 否则则自己 new 一个)

另外实际上 MyBatis 也用到了这个 (SqlSessionFactoryBean等等等)

FactoryBean去定义具体获取方式不同,其实也有Spring提供类似的机制,可以完成这个获取方式,即有自定义的用自定义,没有的话,使用默认的
比如:

@ConditionalOnMissingBean

@ConditionalOnMissingBean这个注解,你在配置你的默认实现时,给它上面加一个

@Bean
@ConditionalOnMissingBean(InterceptorPredicate.class)
public InterceptorPredicate defaultPre(){
    return new YourDefaultPredicate();
}

这样如果配置之后,如果别人有配置了InterceptorPredicate的实现,那你这个默认的实现自动失效了,然后你在你的MyInterceptor里可以直接@Autowired即可

@Component
public class MyInterceptor implements HandlerInterceptor {
    @Autowired
    private InterceptorPredicate predicate;
    
}

其实这种做法,官方里也很常用,尤其在一些autoconfigure,比如redisTemplate
image.png

亦或者mongoTemplate

image.png

ObjectProvider

从该接口的注释就可以看得出,它就是你想要的,尤其是optionality,选择性,意味着你可以选择你Bean的实现
image.png

其中的方法getIfAvailable,从名字你就可以看出来,如果Bean存在就返回
image.png

不说其他,我们看看其他autoconfigure包里是怎么使用的,你就明白了
image.png

所以这样你的MyInterceptor就好配置啦

@Bean
public HandlerInterceptor myInterceptor(ObjectProvider<InterceptorPredicate> predicateProvider){
    InterceptorPredicate predicate = predicateProvider.getIfAvailable();
    if(predicate == null) {
        predicate = new YourDefaultPredicate();
    }
    return new MyInterceptor(predicate);
}

当然我们也可以看到MyInterceptor还有一个重载的方法,传入一个默认的Supplier
image.png

所以我们也可以这样写

@Bean
public HandlerInterceptor myInterceptor(ObjectProvider<InterceptorPredicate> predicateProvider){
    InterceptorPredicate predicate = predicateProvider.getIfAvailable(YourDefaultPredicate::new);
    return new MyInterceptor(predicate);
}

如果只是简单的为空默认替换,其实上面两种基本都够用了,如果还有一些额外的不只是判空替换规则的话,你就还需要@Conditional注解了,去定制你的bean注入的条件

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏