一、問題如下:
使用的是SpringBoot框架:通過AOP和自定義註解完成druid連線池的動態資料來源切換(三)中的兩個資料庫spring_boot_demo和other_data。
在UserController中同時呼叫兩個方法getAgeOfUser()和getAgeOfUser2(),這裡方法裡都是使用UserService中的同一方法接收資料。
不同的是在getAgeOfUser2()上使用了DataSource(DataSourceName.SECOND)自定義註解來切換資料來源,當兩個方法同時被呼叫時,程式碼如下:
package com.example.demo.controller; import com.example.demo.annotation.DataSource; import com.example.demo.enums.DataSourceName; import com.example.demo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author 我命傾塵 */ @RestController public class UserController { @Autowired UserService userService; /** * 從spring_boot_demo資料庫中得到資料 * @return int */ public int getAgeOfUser(){ return userService.getAgeByUsername("springbootdemo"); } /** * 從other_data資料庫中得到資料 * @return */ @DataSource(DataSourceName.SECOND) public int getAgeOfUser2(){ return userService.getAgeByUsername("springbootdemo"); } /** * 兩個方法同時執行 * @return String */ @RequestMapping("/user/age") public String getAge(){ int age1=getAgeOfUser(); int age2=getAgeOfUser2(); return "spring_boot_demo:"+age1+";other_data:"+age2; } }
得到的執行結果如下:
很顯然,切換資料來源沒有成功,加了切換註解的方法得到的顯示結果還是主資料來源的資料。
二、問題思考:
相比於之前使用單方法切換資料來源成功的測試結果,這次的測試和之前的差別無非以下兩點:
1、不使用註解(即預設資料來源)的方法和使用註解(切換輔資料來源)的方法同時被呼叫; 2、方法被巢狀在一個不使用註解的父方法中呼叫。
那麼針對以上兩個情況再進行測試,去掉使用預設資料來源的方法,只在父方法中巢狀一個進行呼叫:
@RequestMapping("/user/age") public String getAge(){ int age2=getAgeOfUser2(); return "other_data:"+age2; }
得到的結果如下所示:
結果得到的還是主資料來源的資料,也就是說,切換資料來源的註解失效與另一個無註解的方法無關,而是因為被巢狀在一個無註解的父方法上。
在無註解的父方法上加上切換資料來源的註解,再次進行測試如下:
@RequestMapping("/user/age") @DataSource(DataSourceName.SECOND) public String getAge(){ int age2=getAgeOfUser2(); return "other_data:"+age2; }
得到的結果是:
到這兒,基本就清楚了。
之所以切換資料來源的註解失效,是因為方法巢狀時,子方法的DataSource註解被父方法覆蓋了。
三、問題解決
1、使用代理物件:
(1)引入spring-aspects依賴包:
<!-- spring-aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.1.2.RELEASE</version> </dependency>
(2)修改入口類的@EnableAspectJAutoProxy註解為:
@EnableAspectJAutoProxy(exposeProxy = true)
exposeProxy = true的作用是暴露當前代理物件到當前執行緒繫結。
(3)修改controller層中方法的呼叫方式:
AopContext.currentProxy()使用ThreadLocal儲存了代理物件,所以直接把方法之間的呼叫方式改為代理物件之間的呼叫即可。
/** * 兩個方法同時執行 * @return String */ @RequestMapping("/user/age") public String getAge(){ int age1=((UserController)AopContext.currentProxy()).getAgeOfUser(); int age2=((UserController)AopContext.currentProxy()).getAgeOfUser2(); return "spring_boot_demo:"+age1+";other_data:"+age2; }
程式碼如上所示,使用((UserController)AopContext.currentProxy()).getAgeOfUser()的方式,通過代理物件呼叫方法,所得結果為:
兩個資料庫的結果同時獲取並顯示了。
2、把DataSource註解放到service層的方法中:
(1)修改Service層的方法,加上註解:
@Override public int getAgeByUsername(String username) { return userMapper.getAgeByUsername(username); } @Override @DataSource(DataSourceName.SECOND) public int getAgeByUsername2(String username) { return userMapper.getAgeByUsername(username); }
(2)在controller中直接呼叫即可:
/** * 兩個方法同時執行 * @return String */ @RequestMapping("/user/age") public String getAge(){ int age1=userService.getAgeByUsername("springbootdemo"); int age2=userService.getAgeByUsername2("springbootdemo"); return "spring_boot_demo:"+age1+";other_data:"+age2; }
結果如下: