1、开启数据库配置在启动类中增加 @EnableDataSources
@SpringBootApplication @EnableDataSources public class OrderServiceApplication { public static void main(String[] args) { new SpringApplicationBuilder(OrderServiceApplication.class).web(WebApplicationType.SERVLET).run(args); } }
2、在配置文件中增加配置
## jdbc 配置信息 jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.initialSize=10 jdbc.maxActive=100 jdbc.minIdle=5 jdbc.maxWait=5000 jdbc.dataAddres.order-1.url=jdbc:mysql://127.0.0.1/mysql jdbc.dataAddres.order-1.userName=name jdbc.dataAddres.order-1.password=password jdbc.dataAddres.order-1.master=true jdbc.dataAddres.order-2.url=jdbc:mysql://127.0.0.1/mysql jdbc.dataAddres.order-2.userName=name jdbc.dataAddres.order-2.password=password jdbc.dataAddres.order-2.master=true jdbc.dataAddres.order-3.url=jdbc:mysql://127.0.0.1/mysql jdbc.dataAddres.order-3.userName=name jdbc.dataAddres.order-3.password=password jdbc.dataAddres.order-3.master=false jdbc.dataAddres.order-4.url=jdbc:mysql://127.0.0.1/mysql jdbc.dataAddres.order-4.userName=name jdbc.dataAddres.order-4.password=password jdbc.dataAddres.order-4.master=false jdbc.rules.order=^com\\.xxx\\.xxx\\.service\\..+$
主要是@EnableDataSources配置类
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration @Import({DataImportBeanDefinitionRegistrar.class,DataSourcesConfig.class}) public @interface EnableDataSources { }
包含DataImportBeanDefinitionRegistrar和DataSourcesConfig 2个类
DataImportBeanDefinitionRegistrar类主要是获取配置文件中的属性来,注册数据源、动态切换h
public class DataImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware { private Environment environment; /** 默认数据源名字 */ private String defaultName; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { StandardEnvironment standardEnvironment = (StandardEnvironment)environment; Iterator<PropertySource<?>> sources = standardEnvironment.getPropertySources().iterator(); List<ConfigurationPropertySource> configs = new ArrayList<>(); //获取数据源 while(sources.hasNext()){ PropertySource<?> source = sources.next(); Iterable<ConfigurationPropertySource> a = ConfigurationPropertySources.from(source); a.forEach(info->configs.add(info)); } //绑定数据源 Binder binder = new Binder(configs); try { //解析属性 BindResult<DataResources> response = binder.bind("jdbc", DataResources.class); DataResources dataResources = response.get(); if(Objects.nonNull(dataResources)){ //多数据源配置Map ManagedMap<String, Object> keys = new ManagedMap<>(); //注册数据源 boolean state = registerDataSource(registry, keys, dataResources); if(!state){ return; } //注册路由切换 registerRoutingDataSource(registry,keys); //事务注册 DataSourceTransactionManager registerTransactionManager(registry); //拦截器 DatasourceAutoChanger registerDatasourceAutoChanger(registry,dataResources.getRules()); //事务拦截器 TransactionInterceptor registerTransactionInterceptor(registry); } } catch (Exception e) { //e.printStackTrace(); } } /** * 注册数据源 * @param registry BeanDefinitionRegistry * @param keys ManagedMap * @param dataResources DataResources * @return boolean */ private boolean registerDataSource(BeanDefinitionRegistry registry,ManagedMap<String, Object> keys,DataResources dataResources){ //注册数据源 final Map<String, Object> propertyValues = getPropertyValues(dataResources); Map<String, DataResources.DataAddress> dataAddress = dataResources.getDataAddres(); //判断是否配置数据源 if(Objects.isNull(dataAddress) && dataAddress.size() == 0){ return false; } dataAddress.forEach(( key,value)->{ String name = key.split("-")[0]; if(defaultName == null){ defaultName = name; } name = DatasourceAutoChanger.addDataName(name,value.getMaster()); System.out.println("数据库主从名称:"+name); RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClass(DruidDataSource.class); beanDefinition.setInitMethodName("init"); Map<String, Object> propertys = getPropertyValuesAddress(value); propertys.putAll(propertyValues); beanDefinition.getPropertyValues().addPropertyValues(propertys); registry.registerBeanDefinition(name,beanDefinition); keys.put(name,new RuntimeBeanReference(name)); }); return true; } /** * 注册动态切换数据源<br> * @param registry BeanDefinitionRegistry * @param keys ManagedMap<String, Object> */ private void registerRoutingDataSource(BeanDefinitionRegistry registry,ManagedMap<String, Object> keys){ RootBeanDefinition dataSourceRute = new RootBeanDefinition(); dataSourceRute.setBeanClass(RoutingDataSource.class); MutablePropertyValues rutePropertyValues = dataSourceRute.getPropertyValues(); rutePropertyValues.addPropertyValue("defaultTargetDataSource",new RuntimeBeanReference(DatasourceAutoChanger.getMasterNameFrist(defaultName))); rutePropertyValues.addPropertyValue("targetDataSources",keys); registry.registerBeanDefinition("dataSource",dataSourceRute); } /** * 事务注册<br> * @param registry */ private void registerTransactionManager(BeanDefinitionRegistry registry){ RootBeanDefinition transactionManagerBean = new RootBeanDefinition(); transactionManagerBean.setBeanClass(DataSourceTransactionManager.class); transactionManagerBean.getPropertyValues().addPropertyValue("dataSource",new RuntimeBeanReference("dataSource")); registry.registerBeanDefinition("transactionManager",transactionManagerBean); } /** * 自动切换数据源<br> * @param registry * @param rules */ private void registerDatasourceAutoChanger( BeanDefinitionRegistry registry,Map<String,String> rules){ //拦截器 datasourceAutoChanger RootBeanDefinition datasourceAutoChanger = new RootBeanDefinition(); datasourceAutoChanger.setBeanClass(DatasourceAutoChanger.class); datasourceAutoChanger.getPropertyValues().addPropertyValue("rules",rules); registry.registerBeanDefinition("datasourceAutoChanger",datasourceAutoChanger); } /** * 事务拦截注册<br> * @param registry */ private void registerTransactionInterceptor( BeanDefinitionRegistry registry){ //拦截器 datasourceAutoChanger RootBeanDefinition transactionInterceptorBean = new RootBeanDefinition(); transactionInterceptorBean.setBeanClass(TransactionInterceptor.class); transactionInterceptorBean.getPropertyValues().addPropertyValue("transactionManager",new RuntimeBeanReference("transactionManager")); Properties properties = new Properties(); properties.put("save*","PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,-java.lang.Exception"); properties.put("create*","PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,-java.lang.Exception"); properties.put("insert*","PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,-java.lang.Exception"); properties.put("delete*","PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,-java.lang.Exception"); properties.put("update*","PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,-java.lang.Exception"); properties.put("remove*","PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,-java.lang.Exception"); transactionInterceptorBean.getPropertyValues().addPropertyValue("transactionAttributes",properties); registry.registerBeanDefinition("transactionInterceptor",transactionInterceptorBean); } /** * 获取数据连接公共属性<br/> * @param dataResources 属性 * @return Map<String,Object> */ private Map<String,Object> getPropertyValues(DataResources dataResources){ Map<String,Object> propertyValue = new HashMap<>(); propertyValue.put("driverClassName",dataResources.getDriverClassName()); propertyValue.put("initialSize",dataResources.getInitialSize()); propertyValue.put("minIdle",dataResources.getMinIdle()); propertyValue.put("maxActive",dataResources.getMaxActive()); propertyValue.put("maxWait",dataResources.getMaxWait()); propertyValue.put("timeBetweenEvictionRunsMillis",dataResources.getTimeBetweenEvictionRunsMillis()); propertyValue.put("minEvictableIdleTimeMillis",dataResources.getMinEvictableIdleTimeMillis()); propertyValue.put("validationQuery",dataResources.getValidationQuery()); propertyValue.put("testWhileIdle",dataResources.isTestWhileIdle()); propertyValue.put("testOnBorrow",dataResources.isTestOnBorrow()); propertyValue.put("testOnReturn",dataResources.isTestOnReturn()); return propertyValue; } /** * 获取连接数据库用户账号和路径<br/> * @param dataAddress * @return Map<String,Object> */ private Map<String,Object> getPropertyValuesAddress(DataResources.DataAddress dataAddress){ Map<String,Object> propertyValue = new HashMap<>(); propertyValue.put("url",dataAddress.getUrl()); propertyValue.put("username",dataAddress.getUserName()); propertyValue.put("password",dataAddress.getPassword()); return propertyValue; } @Override public void setEnvironment(Environment environment) { this.environment = environment; } }
DataSourcesConfig类主要是方法拦截配置
@Configuration public class DataSourcesConfig { @Bean("dataSourceAutoProxyCreator") public BeanNameAutoProxyCreator beanNameAutoProxyCreator(){ BeanNameAutoProxyCreator dataSourceAutoProxyCreator = new BeanNameAutoProxyCreator(); dataSourceAutoProxyCreator.setBeanNames("**ServiceImpl"); dataSourceAutoProxyCreator.setInterceptorNames("datasourceAutoChanger","transactionInterceptor"); return dataSourceAutoProxyCreator; } }
下面是用到的几个类
RoutingDataSource类
public class RoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DatasourceHolder.getRouteKey(); } }
DatasourceHolder类
public class DatasourceHolder { private static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static String getRouteKey() { return threadLocal.get(); } public static void setRouteKey(String key) { threadLocal.set(key); } }
DatasourceAutoChanger类
public class DatasourceAutoChanger implements MethodBeforeAdvice { /** 数据源 */ static Map<String, DataName> datas = new HashMap<>(); /** 路由规则 */ Map<String,String> rules = new HashMap<>(); @Override public void before(Method method, Object[] objects, Object o) throws Throwable { String name = method.getDeclaringClass().getName() + "." + method.getName(); isFind: for (Map.Entry<String,String> entity : rules.entrySet()){ String expressions = entity.getValue(); for (String expression:expressions.split(",")){ if(Pattern.matches(expression,name)){ boolean master = true; if(method.getName().startsWith("select") || method.getName().startsWith("get") || method.getName().startsWith("find") || method.getName().startsWith("list")){ master = false; } DataName dataName = datas.get(entity.getKey()); if(Objects.nonNull(dataName)){ String random = dataName.randomName(entity.getKey(), master); System.out.println("选择的数据源:"+random); DatasourceHolder.setRouteKey(random); } break isFind; } } } } public static void main(String[] args) { Random random = new Random(); for (int i = 0 ;i<100;i++){ System.out.println(random.nextInt(6)); } } public Map<String, String> getRules() { return rules; } public void setRules(Map<String, String> rules) { this.rules = rules; } /** * 添加数据源名称<br> * @param name String * @param master boolean * @return String */ public static String addDataName(String name,boolean master){ DataName dataName = datas.get(name); if(Objects.isNull(dataName)){ dataName = new DataName(); datas.put(name,dataName); } return dataName.addDataName(name,master); } /** * 获取主库<br> * @param name * @return */ public static String getMasterNameFrist(String name){ DataName dataName = datas.get(name); if(dataName == null){ return null; } return dataName.getMasterNames().get(0); } static class DataName{ /** 主库名 */ List<String> masterNames = new ArrayList<>(); /** 从库名 */ List<String> slaveNames = new ArrayList<>(); /** * 添加数据源名称<br> * @param name String * @param master boolean * @return */ public String addDataName(String name,boolean master){ Integer length = master?masterNames.size():slaveNames.size(); name = name + (master?"-master-":"-slave-")+(++length); if(master){ masterNames.add(name); }else{ slaveNames.add(name); } return name; } /** * 添加数据源名称<br> * @param name String * @param master boolean * @return */ public String randomName(String name,boolean master){ Integer length = master?masterNames.size():slaveNames.size(); Integer index = 0; if(length > 1){ Random random = new Random(); index = random.nextInt(length); } System.out.println("index:"+index); return master?masterNames.get(index):slaveNames.get(index); } public List<String> getMasterNames() { return masterNames; } public void setMasterNames(List<String> masterNames) { this.masterNames = masterNames; } public List<String> getSlaveNames() { return slaveNames; } public void setSlaveNames(List<String> slaveNames) { this.slaveNames = slaveNames; } } }