一、問題如下:
使用的是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; }
結果如下: