1,動態(tài)數(shù)據(jù)源類
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.getDbType();//負(fù)載均衡,需要在這里進(jìn)行從庫的key輪詢驱闷。
}
}
2耻台,使用本地ThreadLocal,存儲當(dāng)前線程的數(shù)據(jù)源遗嗽。
threadlocal將具體的值保存在線程自身的threadLocalMap中
public abstract class DynamicDataSourceHolder {
public static final String master = "master";
public static final String slave = "slave";
private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();//定義全局ThreadLocal對象粘我,不同線程使用的是同一個對象。
public static void setDbType(String dbTypeName){
DynamicDataSourceHolder.threadLocal.set(dbTypeName);
}
public static String getDbType(){
return DynamicDataSourceHolder.threadLocal.get();
}
public static void clearDbType(){
DynamicDataSourceHolder.threadLocal.remove();
}
}
3痹换,配置多個數(shù)據(jù)源
@Value("${datasource.username2}")
private String username2;
@Value("${datasource.password2}")
private String password2;
@Value("${datasource.url2}")
private String url2;
@Value("${datasource.username}")
private String username;
@Value("${datasource.password}")
private String password;
@Value("${datasource.url}")
private String url;
@Bean//配置主數(shù)據(jù)庫源
public DataSource master(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return getDataSource(druidDataSource);
}
@Bean//配置從數(shù)據(jù)庫源
public DataSource slave(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(url2);
druidDataSource.setUsername(username2);
druidDataSource.setPassword(password2);
return getDataSource(druidDataSource);
}
@Bean//配置動態(tài)數(shù)據(jù)源
public DynamicDataSource dynamicDataSource(){
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setDefaultTargetDataSource(master());//default數(shù)據(jù)源
Map<Object, Object> map = Maps.newHashMap();
map.put(DynamicDataSourceHolder.master, master());//key - value
map.put(DynamicDataSourceHolder.slave, slave());
dynamicDataSource.setTargetDataSources(map);
return dynamicDataSource;
}
4征字,自定義注解,標(biāo)識使用哪個數(shù)據(jù)源
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface DynamicSourceAno {
/**
* 默認(rèn)使用的數(shù)據(jù)源
* @return
*/
String value() default DynamicDataSourceHolder.master;
}
5娇豫,實(shí)現(xiàn)切面匙姜。
設(shè)置spring事務(wù)aop的執(zhí)行順序
@EnableTransactionManagement(order = 2) <tx:annotation-driven order="2"/>
@Aspect
@Component
@Order(1)//service層有@Transactional,事務(wù)也是aop實(shí)現(xiàn)的冯痢,這里定義order氮昧,要在事務(wù)開啟的aop執(zhí)行前,進(jìn)行數(shù)據(jù)源的切換浦楣,事務(wù)的aop結(jié)束后袖肥,進(jìn)行數(shù)據(jù)源的恢復(fù)默認(rèn)。
public class DynamicSourceAspect {
private static Logger logger = LoggerFactory.getLogger(DynamicSourceAspect.class);
@Before("within(@org.springframework.stereotype.Service *) && @annotation(dynamicSourceAno)")
//在service的所有方法執(zhí)行前振劳,并且?guī)в凶⒔釪ynamicSourceAno
public void beforeDynamicSource(DynamicSourceAno dynamicSourceAno){
try {
DynamicDataSourceHolder.setDbType(dynamicSource.value());
} catch (Throwable t) {
logger.info(t.getMessage(), t);
}
}
@After("within(@org.springframework.stereotype.Service *) && @annotation(dynamicSource)")
public void afterDynamicSource(DynamicSource dynamicSource){
try {
DynamicDataSourceHolder.clearDbType();
} catch (Throwable t) {
logger.info(t.getMessage(), t);
}
}
}
6椎组,ThreadLocal與Thread
1)Thread類。定義了兩個TheadLocalMap類型屬性
(可以用來保存對象)
历恐,通過ThreadLocal類來維護(hù)的set寸癌,get,remove等操作
弱贼。
image.png
2)ThreadLocal類蒸苇。ThreadLocalMap是ThreadLocal的靜態(tài)內(nèi)部類。
Entry類中value屬性用來保存和線程關(guān)聯(lián)的具體對象吮旅,key是ThreadLocal類型
image.png
3)ThreadLocal類操作當(dāng)前線程的ThreadLocalMap溪烤。
//保存value到當(dāng)前線程
public void set(T value) {
Thread t = Thread.currentThread();//得到當(dāng)前線程
ThreadLocalMap map = getMap(t);//得到當(dāng)前線程的ThreadLocalMap對象。
if (map != null)
map.set(this, value);//將自身對象作為key。每個線程保存這個對象氛什,都是使用相同的key莺葫,因?yàn)樵擃愋偷腡hreadLocal對象只new了一個。
else
createMap(t, value);
}
//當(dāng)?shù)谝淮握{(diào)用set方法時枪眉,會創(chuàng)建一個ThreadLocalMap對象。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//從當(dāng)前線程的threadlocals中去除指定key對應(yīng)的value
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//不同的線程使用相同的key來取值再层,得到的各自線程保存的對象贸铜。
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}