Parameter 1 of method gridFsTemplate in org.springframework.boot.autoconfigure.data.mongo.MongoDataA

在使用spring-boot-starter-data-mongodb配置多个数据源的MongoTemplate时,启动项目报错,错误日志如下:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-06-10 08:23:09.734 [//] [main] ERROR o.s.b.d.LoggingFailureAnalysisReporter Line:42  - 

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 1 of method gridFsTemplate in org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration required a single bean, but 2 were found:
    - XXXMongoTemplate: defined by method 'XXXMongoTemplate' in class path resource [com/saicmotor/telematics/icar/common/config/MongoDBConfig.class]
    - XXXXMongoTemplate: defined by method 'XXXXMongoTemplate' in class path resource [com/saicmotor/telematics/icar/common/config/MongoDBConfig.class]
    - XXXXXMongoTemplate: defined by method 'XXXXXMongoTemplate' in class path resource [com/saicmotor/telematics/icar/common/config/MongoDBConfig.class]
   


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

从Action中还有查阅一些资料后得知,如果项目中用到的不止一个mongo库,或者同一个库中的不同数据源,那么就需要配置多个MongoTemplate,每个MongoTemplate对应一个mongo数据源,这个时候就需要在这么多mongo数据源中指定一个数据源作为默认数据源,如何指定一个数据源为默认数据源呢?

答案就是使用@Primary注解,将该注解标注在使用次数最多的那个MongoTemplate的bean上就可以了,比如你有三个数据源,分别是A、B、C,然后声明了三个MongoTemplate Bean,分别是AMongoTemplate、BMongoTemplate、CMongoTemplate、其中项目中使用最多的是A数据源,那最好是把@Primary标注在AMonoTemplate的Bean声明上。当然只是建议,也可以放在BMongoTemplate或者CMongoTemplate的声明上。

@Primary可以理解为默认优先选择。一般同一个类被声明了多个Bean之后,都需要指定其中一个标注@Primary。那具体@Primary有什么作用呢,为什么非要选择其中一个Bean为优先选择呢?同样以MongoTemplate为例:

//Mongo配置类
@Configuration
public class MongoConfig{
    //这里可以通过@Value注入一些属性
    @Value("${mongoclient.host}")
    private String host;
    @Value("${mongoclient.account}")
    private String account;
    @Value("${mongoclient.password}")
    private String password;
    @Value("${mongoclient.database}")
    private String database;
    @Value("${mongoclient.ssl}")
    private String ssl;

    //通过该bean与mongo数据库建立连接
    @Bean
    public MongoClient mongoClient() {
        String[] array = host.split(",");
        List<ServerAddress> list = new ArrayList<ServerAddress>();
        for (int i = 0; i < array.length; i++) {
            String tmp = array[i];
            ServerAddress address = new ServerAddress(tmp.split(":")[0], Integer.parseInt(tmp.split(":")[1]));
            list.add(address);
        }
        MongoClientOptions.Builder builder = new MongoClientOptions.Builder();
        builder.alwaysUseMBeans(false);
        builder.connectionsPerHost(800);// 修改每个Host的连接数为800
        if (ssl != null && Boolean.parseBoolean(ssl)) {
            builder.socketFactory(SSLSocketFactory.getDefault());
        }
        MongoClientOptions mongoClientOptions = builder.build();
        if (!StringUtils.isEmpty(account) && !StringUtils.isEmpty(password)) {
            MongoCredential credentials = MongoCredential.createScramSha1Credential(account, database,
                    password.toCharArray());
            return new MongoClient(list, credentials, mongoClientOptions);
        } else {
            return new MongoClient(list, mongoClientOptions);
        }
    }
    
    //声明该mongo库的两个MongoTemplate,并将其中一个作为@Primary
    @Primary
    @Bean(name = "primaryMongoTemplate")
    @ConditionalOnClass(MongoClient.class)
    public MongoTemplate primaryMongoTemplate(MongoClient mongoClient) {
        MongoTemplate mongoTemplate = new MongoTemplate(new SimpleMongoDbFactory(mongoClient, "primaryMongo"));//primaryMongo为mongo集合名
        return mongoTemplate;
    }

    @Bean(name = "secondMongoTemplate")
    @ConditionalOnClass(MongoClient.class)
    public MongoTemplate secondMongoTemplate(MongoClient mongoClient) {
        MongoTemplate mongoTemplate = new MongoTemplate(new SimpleMongoDbFactory(mongoClient, "second"));//second为mongo集合名
        return mongoTemplate;
    }

}


然后可能需要在某个ServiceImpl中去使用这些MongoTemplate:

public class XXXServiceImpl implements XXXService {
    
    //注入两个MongoTemplate
    
    @Autowired
    //由于MongoTemplate有两个Bean实例,而@Autowired是按照类去装配Bean的,
    //理论上这时如果不加@Qualifier注解,spring容器不知道去装配哪个Bean实例的,所以要通过@Qualifier去按名称装配Bean实例
//注意:@Qualifier注解必须要在@AutoWired的后面
    @Qualifier(value = "primaryMongoTemplate")
    private MongoTemplate primaryMongoTemplate;

    @Autowired
    @Qualifier(value = "secondMongoTemplate")
    private MongoTemplate secondMongoTemplate;

    //使用primaryMongoTemplate查询
    public List<XXX> mongoQuery() {
        return primaryMongoTemplate.find(new Query(条件), XXX.class);
    }

    //使用secondMongoTemplate保存
    public void saveMongo() {
        secondMongoTemplate.save(XXX);
    }
    
}

上述基本满足了程序与mongo进行数据交互,可以通过上述方式从Mongo中查询数据,保存数据,但是上述例子并没有体现出来@Primary默认优先选择的作用,其实上面例子的注解中也说了在使用@Autowired注解时理论上要加上@Qualifier注解的,但是其实也可以不加。

public class XXServiceImpl implements XXService {
    
    @Autowired
    private MongoTemplate mongoTemplate;

    public List<XXXX> query () {
        return  mongoTemplate.find(new Query(), XXXX.class);
    } 

    这种方式注入MongoTemplate也是可以的,此时注入的MongoTemplate就是在Config类中加了@Primary注解的MongoTemplate的Bean,查询也不会报错,但是如果加了@Primary注解的数据源中没有XXXX对应的集合,那么此次查询将查不出来结果,所以理论上都要求@Qualifier注解具体制定MongoTemplate
}

所以总结:1、多数据源MongoTemplate要加@Primary指定默认优先选择

                   2、@Autowired注解后要加@Qualifier(value = "primaryMongoTemplate")按Bean名称具体装配MongoTemplate

                   3、@Qualifier注解要在@Autowired注解之后

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值