明确几个问题:
什么是标准系统?
标准系统就是系统架构师初始系统,包括业务系统的所有基础集成技术,可以在这个系统上开发出任何应用系统。
标准系统使用的技术和最终的目标
a、使用springboot2、mybites、redis、session共享、Shiro、quartz、JWT等技术,
b、集成MyBites,使用druid做数据链接池,支持多数据源;
c、集成session共享,支持系统集群;
d、集成Shiro权限管理系统;
e、采用RESTFul风格;
f、采用JWT完成用户认证授权;
g、使用quartz完成任务管理;
多数据源一般采用切面的方式来实现,多数据源的好处有很多,比如读写分离,数据库分库等性能优化。
下面我们实现2个数据源,跟多的数据源可以自己进行扩展。如果数据库多到4个,建议做微服。
创建数据源名称接口类:
package com.james.framework.datasources;
public interface DataSourceNames {
String FIRST = "first";
String SECOND = "second";
}
然后定义一个annotation,用于注解用;
package com.james.framework.datasources.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String name() default "";
}
然后我们继承spring里的AbstractRoutingDataSource类
package com.james.framework.datasources;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
public class DynamicDataSource extends AbstractRoutingDataSource {
//线程安全
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
//动态数据源
public DynamicDataSource(DataSource defaultTargetDataSource, Map<String, DataSource> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(new HashMap<>(targetDataSources));
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
然后我们添加一个config,让springboot启动时能够使用动态数据源;
package com.james.framework.datasources;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DynamicDataSourceConfig {
@Bean
@Primary
@ConfigurationProperties("spring.datasource.druid.first")
public DataSource firstDataSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.second")
public DataSource secondDataSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean
public DynamicDataSource myDataSource(DataSource firstDataSource, DataSource secondDataSource) {
Map<String, DataSource> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceNames.FIRST, firstDataSource);
targetDataSources.put(DataSourceNames.SECOND, secondDataSource);
//默认第一个链接池
return new DynamicDataSource(firstDataSource, targetDataSources);
}
}
注意在第一个数据源上加的@Primary 由于数据源1和2 都是 DataSource,需要标明优先实例化第一个。
修改配置文件,配置多数据源,
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.jdbc.Driver
first: #数据源1
url: jdbc:mysql://127.0.0.1:3306/adu_framework?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
username: root
password: root
second: #数据源2
url: jdbc:mysql://localhost:3306/adu_framework?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
username: root
password: root
initial-size: 10
max-active: 100
min-idle: 10
max-wait: 60000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
stat-view-servlet:
enabled: true
url-pattern: /druid/*
#login-username: admin
#login-password: admin
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
大家可以对比上一步单数据源的配置,看看有什么不同。
我们这里是开发模式,所有2个数据源指向类同一个数据库。
那么如何使用这2个数据源呢?
其实很简单,就是在需要使用第二个数据源的情况下申明一下,比如下面的service:
package com.james.framework.modules.sys.service.Impl;
import com.james.framework.datasources.DataSourceNames;
import com.james.framework.datasources.annotation.DataSource;
import com.james.framework.modules.sys.dao.SysUserDao;
import com.james.framework.modules.sys.entity.SysUserEntity;
import com.james.framework.modules.sys.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SysUserServicesImpl implements SysUserService {
@Autowired
private SysUserDao sysUserDao;
@Override
public List<SysUserEntity> list() {
return sysUserDao.queryList(null);
}
@DataSource(name = DataSourceNames.SECOND)
public Integer delUser(Integer userId){
return this.sysUserDao.delete(userId);
}
}
list方法使用了默认的第一个数据源;
delUser方法通过注解@DataSource申明使用第二个数据源;下面运行测试一下是否可用。
现在数据库里有3个用户,我们要删除第3个用户,还是使用上一步开发的TestController。具体代码查看上一篇博客。
由于浏览器模拟不了DELETE请求,所以我们使用postman:
点击“send”按钮后,body看到是返回了“1”
查看数据库内数据确定被删除了。