最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring?AOP實(shí)現(xiàn)多數(shù)據(jù)源動態(tài)切換

 更新時間:2022年03月14日 09:58:43   作者:Carson-Zhao  
本文主要介紹了Spring?AOP實(shí)現(xiàn)多數(shù)據(jù)源動態(tài)切換,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

需求背景

去年底,公司項(xiàng)目有一個需求中有個接口需要用到平臺、算法、大數(shù)據(jù)等三個不同數(shù)據(jù)庫的數(shù)據(jù)進(jìn)行計算、組裝以及最后的展示,當(dāng)時這個需求是另一個老同事在做,我只是負(fù)責(zé)自己的部分。
直到今年回來了,這個項(xiàng)目也做得差不多了,這會兒才有時間區(qū)仔細(xì)看同事的代碼,是怎么去實(shí)現(xiàn)多數(shù)據(jù)源動態(tài)切換的。

擴(kuò)展:當(dāng)業(yè)務(wù)也來越復(fù)雜,數(shù)據(jù)量越來越龐大時,就可能會對數(shù)據(jù)庫進(jìn)行分庫分表、讀寫分離等設(shè)計來減輕壓力、提高系統(tǒng)性能,那么多數(shù)據(jù)源動態(tài)切換勢必是必不可少!

經(jīng)過了一星期零零碎碎的下班時間,從了解原理、實(shí)現(xiàn)、優(yōu)化的過程,自己終于總算是弄出來了,接下來一起看看!

思考

  • 如何讓Spring知道我們配置了多個數(shù)據(jù)源?
  • 配置了多個數(shù)據(jù)源后,Spring是如何決定使用哪一個數(shù)據(jù)源?
  • Spring是如何動態(tài)切換數(shù)據(jù)源?

分析及實(shí)現(xiàn)

配置多數(shù)據(jù)源信息

spring:
  datasource:
    local:
      database: local
      username: root
      password: 
      jdbc-url: jdbc:mysql://ip:port/test_user?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
      driver-class-name: com.mysql.cj.jdbc.Driver
    server:
      database: server
      username: root
      password: 
      jdbc-url: jdbc:mysql://ip:port/test_user?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
      driver-class-name: com.mysql.cj.jdbc.Driver

這是我的兩個數(shù)據(jù)庫:本地數(shù)據(jù)庫+個人服務(wù)器數(shù)據(jù)庫

服務(wù)器數(shù)據(jù)庫

本地數(shù)據(jù)庫

Spring如何獲取配置好的多個數(shù)據(jù)源信息?

Spring提供了三種方式進(jìn)行獲取

@Value注解獲?。▽?shí)體類需配合@Component),最簡單,但當(dāng)配置信息較多時,寫起來比較繁瑣

@ConfigurationProperties注解獲取,需要定義前綴,可大批量獲取配置信息

@Environment注解從Spring環(huán)境中獲取,實(shí)現(xiàn)較為復(fù)雜,本人很少用

同事使用的方式是第一種方式,但是我個人覺得這樣侵入性較大,每增加一個數(shù)據(jù)源,就要重新定義變量然后用@Value去重新配置,很麻煩,所以我就選擇了第二種方式

通過@ConfigurationProperties注解獲取,需要定義前綴,可大批量獲取配置信息

@Data
@Component
@ConfigurationProperties(prefix = "spring.datasource")
public class DBProperties {
 
    private HikariDataSource server;
 
    private HikariDataSource local;
}

將所有的數(shù)據(jù)源加載到Spring中,可供其選擇使用

@Slf4j
@Configuration
public class DataSourceConfig {
 
    @Autowired
    private DBProperties dbProperties;
 
    @Bean(name = "multiDataSource")
    public MultiDataSource multiDataSource(){
        MultiDataSource multiDataSource = new MultiDataSource();
        //1.設(shè)置默認(rèn)數(shù)據(jù)源
        multiDataSource .setDefaultTargetDataSource(dbProperties.getLocal());
        //2.配置多數(shù)據(jù)源
        HashMap<Object, Object> dataSourceMap = Maps.newHashMap();
 
        dataSourceMap.put("local", dbProperties.getLocal());
        dataSourceMap.put("server", dbProperties.getServer());
        //3.存放數(shù)據(jù)源集
        multiDataSource.setTargetDataSources(dataSourceMap);
        return multiDataSource;
    }
}

如此之后,確實(shí)是可以讀取YML中的數(shù)據(jù)源信息,但是總覺得怪怪的。
果然!當(dāng)我實(shí)現(xiàn)了整個功能后,我發(fā)現(xiàn),如果我想要再加一個數(shù)據(jù)源,我還是得去求改DBProperties和DataSourceConfig這兩類的內(nèi)容,就很煩,我這個人比較懶,所以我就將這部分內(nèi)容優(yōu)化了一下:

優(yōu)化后的YML

spring:
  datasource:
    names:
       - database: dataSource0
         username: root
         password: 
         jdbc-url: jdbc:mysql://ip:port/test_user?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
         driver-class-name: com.mysql.cj.jdbc.Driver
       - database: dataSource1
         username: root
         password: 
         jdbc-url: jdbc:mysql://ip:port/test_user?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
         driver-class-name: com.mysql.cj.jdbc.Driver

優(yōu)化后的DBProperties

@Data
@Component
@ConfigurationProperties(prefix = "spring.datasource")
public class DBProperties {
 
    private List<HikariDataSource> DBNames;
 
}

優(yōu)化后的DataSourceConfig

@Slf4j
@Configuration
public class DataSourceConfig {
 
    @Autowired
    private DBProperties dbProperties;
 
 
    @Bean(name = "multiDataSource")
    public MultiDataSource multiDataSource(){
        MultiDataSource multiDataSource = new MultiDataSource();
        
        List<HikariDataSource> names = dbProperties.getNames();
        if (CollectionUtils.isEmpty(names)){
            throw new RuntimeException(" please configure the data source! ");
        }
 
        multiDataSource.setDefaultTargetDataSource(names.get(0));
 
        HashMap<Object, Object> dataSourceMap = Maps.newHashMap();
        int i = 0;
        for (HikariDataSource name : names) {
            dataSourceMap.put("dataSource"+(i++),name);
        }
 
        multiDataSource.setTargetDataSources(dataSourceMap);
        return multiDataSource;
    }
}

這樣子,我之后無論配置了多少個數(shù)據(jù)源信息,我都不需要再去修改配置代碼

Spring如何選擇使用數(shù)據(jù)源?

選擇一個數(shù)據(jù)源

通過繼承AbstractRoutingDataSource接口,重寫determineCurrentLookupKey方法,選擇具體的數(shù)據(jù)源

@Slf4j
public class MultiDataSource extends AbstractRoutingDataSource {
    
    @Override
    protected Object determineCurrentLookupKey() {
 
        return MultiDataSourceHolder.getDatasource();
 
    }
    
}

利用ThreadLocal實(shí)現(xiàn)數(shù)據(jù)源線程隔離

public class MultiDataSourceHolder {
 
    private static final ThreadLocal<String> threadLocal =new ThreadLocal<>();
 
    public static void setDatasource(String datasource){
        threadLocal.set(datasource);
    }
 
    public static String getDatasource(){
        return threadLocal.get();
    }
 
    public static void clearDataSource(){
        threadLocal.remove();
    }
 
}

準(zhǔn)備工作做好,下面開始將動態(tài)切換操作串聯(lián)起來

利用AOP切面+自定義注解

自定義注解

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MultiDataSource {
 
    String DBName();
 
}

AOP切面

@Slf4j
@Aspect
@Component
public class DataSourceAspect {
 
    @Pointcut(value = "@within(com.xiaozhao.base.aop.annotation.MultiDataSource) || @annotation(com.xiaozhao.base.aop.annotation.MultiDataSource)")
    public void dataSourcePointCut(){}
 
 
    @Before("dataSourcePointCut() && @annotation(multiDataSource)")
    public void before(MultiDataSource multiDataSource){
 
        String dbName = multiDataSource.DBName();
 
        if (StringUtils.hasLength(dbName)){
 
            MultiDataSourceHolder.setDatasource(multiDataSource.DBName());
            log.info("current dataSourceName ====== "+dbName);
 
        }else {
 
            log.info("switch datasource fail, use default, or please configure the data source for the annotations,");
 
        }
    }
 
 
    @After("dataSourcePointCut()")
    public void after(){
        MultiDataSourceHolder.clearDataSource();
    }
}

好了!功能已然實(shí)現(xiàn),打完收工!

。。。。

如果我工作中也這樣,估計要被測試打死!為了敷衍一下,來進(jìn)行一下測試

一套代碼直接打完:

Controller+Service+Dao

@RestController
@RequestMapping("user")
public class UserController {
 
    @Autowired
    private UserService userService;
 
 
 
    @GetMapping("/info")
    public UserVO getUser(){
        return userService.creatUser();
    }
}
 
 
 
 
public interface UserService {
    UserVO creatUser();
 
    UserVO setUserInfo(String phone);
}
 
 
 
 
@Service
@EnableAspectJAutoProxy(exposeProxy = true)
public class UserServiceImpl implements UserService {
 
    @Autowired
    private UserMapper userMapper;
 
    @Autowired
    private InfoMapper infoMapper;
 
 
    @Override
    public UserVO creatUser() {
        UserVO userVO = userMapper.getUserInfoMapper();
 
        return ((UserService) AopContext.currentProxy()).setUserInfo(userVO.getPhone());
    }
 
    @MultiDataSource(DBName = "dataSource1")
    public UserVO setUserInfo(String phone) {
 
        UserVO userInfo = infoMapper.getUserInfo();
 
        UserVO user = new UserVO();
        user.setUserName(userInfo.getUserName());
        user.setPassword(userInfo.getPassword());
        user.setAddress(userInfo.getAddress());
        user.setPhone(phone);
        return user;
    }
}
 
 
 
 
@Mapper
public interface InfoMapper {
 
    @Select("select id,user_name as userName,password,phone,address from test_user")
    UserVO getUserInfo();
}
 
 
 
@Mapper
public interface UserMapper {
 
    @Select("select id,user_name as userName,password,phone from user")
    UserVO getUserInfoMapper();
 
}

測試結(jié)果:紅框數(shù)據(jù)來自于服務(wù)器數(shù)據(jù)庫,綠框數(shù)據(jù)來自于本地數(shù)據(jù)庫

遇到的問題同一個類中,A方法調(diào)用B方法用AopContext.currentProxy()報錯問題:在類上加@EnableAspectJAutoProxy(exposeProxy = true)————解決!配置多數(shù)據(jù)源時,注意將url修改成jdbc-url切面時,用JoinPoint獲取方法,判斷是否被注解修飾(雖然純屬多余)結(jié)果為false————有待考究!

結(jié)語

到此這篇關(guān)于Spring AOP實(shí)現(xiàn)多數(shù)據(jù)源動態(tài)切換的文章就介紹到這了,更多相關(guān)Spring AOP多數(shù)據(jù)源動態(tài)切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java中l(wèi)og使用小結(jié)

    java中l(wèi)og使用小結(jié)

    本文主要介紹了java中l(wèi)og使用小結(jié),一文搞清楚java所有日志框架,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-09-09
  • jedis的borrow行為方法源碼解讀

    jedis的borrow行為方法源碼解讀

    這篇文章主要為大家介紹了jedis的borrow行為方法源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • java不用循環(huán)語句打印數(shù)組元素的實(shí)例

    java不用循環(huán)語句打印數(shù)組元素的實(shí)例

    下面小編就為大家?guī)硪黄猨ava不用循環(huán)語句打印數(shù)組元素的實(shí)例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-03-03
  • 為什么Java要把字符串設(shè)計成不可變的

    為什么Java要把字符串設(shè)計成不可變的

    為什么Java要把字符串設(shè)計成不可變的,這篇文章給出了Java字符串設(shè)計成不可變的原因,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Java IO學(xué)習(xí)之緩沖輸入流(BufferedInputStream)

    Java IO學(xué)習(xí)之緩沖輸入流(BufferedInputStream)

    這篇文章主要介紹了Java IO學(xué)習(xí)之緩沖輸入流(BufferedInputStream)的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Spring bean不被GC的真正原因及分析

    Spring bean不被GC的真正原因及分析

    這篇文章主要介紹了Spring bean不被GC的真正原因及分析,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • Java實(shí)戰(zhàn)之自助進(jìn)行多張圖片合成拼接

    Java實(shí)戰(zhàn)之自助進(jìn)行多張圖片合成拼接

    在當(dāng)今數(shù)字化時代,圖像處理技術(shù)在各個領(lǐng)域都發(fā)揮著至關(guān)重要的作用,本文為大家詳細(xì)介紹了如何使用Java實(shí)現(xiàn)多張圖片合成拼接,需要的可以了解下
    2025-02-02
  • java實(shí)現(xiàn)動態(tài)時鐘并設(shè)置鬧鐘功能

    java實(shí)現(xiàn)動態(tài)時鐘并設(shè)置鬧鐘功能

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)動態(tài)時鐘并設(shè)置鬧鐘功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • 一篇文章帶你深入了解Java基礎(chǔ)

    一篇文章帶你深入了解Java基礎(chǔ)

    這篇文章主要給大家介紹了關(guān)于Java中方法使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • java中request常用方法小結(jié)

    java中request常用方法小結(jié)

    這篇文章主要介紹了java中request常用方法小結(jié),需要的朋友可以參考下
    2014-10-10

最新評論

尚志市| 金昌市| 济宁市| 宽城| 康平县| 元朗区| 紫阳县| 浦县| 和顺县| 朝阳市| 分宜县| 安达市| 汾阳市| 文昌市| 长寿区| 西畴县| 昌宁县| 平利县| 宝清县| 沙坪坝区| 金堂县| 调兵山市| 宝应县| 赤壁市| 邯郸市| 遂平县| 泸水县| 云南省| 分宜县| 平定县| 新郑市| 方城县| 武汉市| 伊宁市| 沙河市| 平泉县| 岳西县| 盖州市| 卓资县| 县级市| 崇州市|