源代码:https://gitee.com/mr_wenpan/basis-enhance
使用参考:TestMongoMutilSourceController
@EnableMongoMultiSource
开启MongoDB多数据源功能启用与禁用,实现了动态可插拔功能。MongoMultiSourceClient
来获取并操作某个指定的数据源@Autowired
注入mongoTemplateConsistentHash
即可使用一致性hash算法。下载源码,然后mvn install
到自己的maven仓库。
<dependency> <groupId>org.basis.enhance</groupId> <artifactId>enhance-boot-mongo</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
spring: data: mongodb: # 默认数据源 uri: ${MONGODB_DEFAULT_URL:mongodb://用户名:密码@ip:端口/库名} # 开启多数据源分片 enable-sharding: true # 多数据源配置 datasource: datasource1: order: 1 uri: ${MONGODB_DEFAULT_URL:mongodb://用户名:密码@ip:端口/库名} datasource2: order: 2 uri: ${MONGODB_DEFAULT_URL:mongodb://用户名:密码@ip:端口/库名}
// 使用@EnableMongoMultiSource注解开启MongoDB多数据源 @EnableMongoMultiSource @SpringBootApplication public class EnhanceMongoDemoApplication { public static void main(String... args) { SpringApplication.run(EnhanceMongoDemoApplication.class, args); } }
// MongoDB多数据源使用示例 public void multiSourceExample() { // 通过一致性hash算法查找对应数据源的MongoTemplate MongoTemplate templateByHash = mongoMultiSourceClient.getMongoTemplateByHash("wenpan"); // 获取默认的MongoDB数据源 MongoTemplate defaultMongoTemplate = mongoMultiSourceClient.getDefaultMongoTemplate(); // 通过数据源名称获取对应的MongoDB数据源 MongoTemplate datasource1MongoTemplate = mongoMultiSourceClient.getMongoTemplate("datasource1MongoTemplate"); // 通过集合 + 分片key 获取对应的MongoDB数据源 MongoTemplate mongoTemplate = mongoMultiSourceClient.getMongoTemplate("enhance_delivery_confirm", "xxx"); // 使用对应数据源的MongoTemplate去操作MongoDB // 省略...... }
spring-boot-starter-data-mongodb
,先不用思考如何实现多数据源功能,配置好常规的springboot 和MongoDB整合,先把项目启动起来MongoAutoConfiguration
、MongoDatabaseFactoryDependentConfiguration
、MongoDatabaseFactoryConfiguration
这几个类,在这几个类中可以看到分别有对mongoClient、mongoTemplateFactory、mongoTemplate的注入1、对于如何获取application.yml配置文件中使用方动态配置的数据源信息(比如使用方可以配置3个数据源,也可以是4个数据源,也可以是五个数据源等等),并为这些数据源创建对应的可用连接,并创建出对应的MongoTemplate然后注入到容器这是一个难题
@ConfigurationProperties注解 + hashMap
将application.yml配置文件中有关MongoDB多数据源的配置映射到map中,map中一个key-value键值对就表示一个数据源的具体配置信息@Bean
的方式硬编码。所以我们利用factoryBean + spring的后置处理器
可以实现在程序启动过程中动态的获取application.yml文件中的数据源配置,然后根据数据源个数动态的为每个MongoDB数据源注入对应的mongoTemplatepublic final class MonogoMultiDataSourceRegistrar implements EnvironmentAware, ImportBeanDefinitionRegistrar { private final Logger logger = LoggerFactory.getLogger(getClass()); private Environment environment; @Override public void setEnvironment(@NonNull Environment environment) { this.environment = environment; } /** * 为每个mongo数据源注入BeanDefinition */ @Override public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { Set<String> names = EnvironmentUtil.loadMongoDataSourceName((AbstractEnvironment) environment); if (names.size() <= 0) { logger.error("no mongo multi datasource config, inject multi datasource failed. please check config."); return; } logger.info("register mongo datasource: {}", names); for (String name : names) { registerMongoTemplateBeanDefinition(name, MongoTemplateFactoryBean.class, registry); } } /** * 注册 MongoTemplate BeanDefinition */ protected final void registerMongoTemplateBeanDefinition(String alias, Class<?> type, BeanDefinitionRegistry registry) { // BeanDefinition构建器 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(type); // 设置通过名称注入 builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME); builder.addConstructorArgValue(null); builder.addPropertyValue(MongoDataSourceContext.FIELD_DATASOURCE_NAME, alias); BeanDefinition beanDefinition = builder.getBeanDefinition(); beanDefinition.setPrimary(false); String beanName = alias + EnhanceMongoConstant.MultiSource.MONGO_TEMPLATE; BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, beanName, new String[]{alias + "-template"}); BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); } /** * 创建 MongoTemplate 的 FactoryBean * FactoryBean一般用于构建复杂的bean */ protected final class MongoTemplateFactoryBean extends MongoDataSourceContext implements FactoryBean<Object> { private final Logger logger = LoggerFactory.getLogger(getClass()); /** * 返回要创建的bean对象 */ @Override public Object getObject() throws Exception { // 为该数据源创建一个Mongo连接工厂,连向指定的数据源 DynamicMongoTemplateFactory dynamicMongoTemplateFactory = getDynamicMongoTemplateFactory(); logger.info("Dynamic create a MongoTemplate named {}", getDataSourceName()); MongoTemplate mongoTemplate = dynamicMongoTemplateFactory.createMongoTemplate(); // 由于这里没有注入spring容器,需要手动设置上applicationContext mongoTemplate.setApplicationContext(applicationContext); return mongoTemplate; } @Override public Class<?> getObjectType() { return MongoTemplate.class; } } }
public DynamicMongoTemplateFactory getDynamicMongoTemplateFactory() { MongoProperties mongoProperties = getMongoProperties(dataSourceName); MongoClientSettingsBuilderCustomizer builderCustomizers = MongoClientCreator.createMongoPropertiesCustomizer(mongoProperties, environment); MongoClientSettings mongoClientSettings = MongoClientCreator.createMongoClientSettings(); MongoClient mongoClient = MongoClientCreator.createMongoClient(Collections.singletonList(builderCustomizers), mongoClientSettings); return new DynamicMongoTemplateFactory(mongoClient, mongoProperties, applicationContext); }
public MongoTemplate createMongoTemplate() throws ClassNotFoundException { // 创建mongo客户端工厂 SimpleMongoClientDatabaseFactory factory = new SimpleMongoClientDatabaseFactory(mongoClient, properties.getMongoClientDatabase()); MappingMongoConverter mappingMongoConverter = createMappingMongoConverter(factory); // 根据工厂创建template return new MongoTemplate(factory, mappingMongoConverter); }