SpringBoot框架:两个方法同时调用时父方法使内部方法的DataSource注解失效的解决办法
一、问题如下:
使用的是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;
}
结果如下:

SpringBoot框架:两个方法同时调用时父方法使内部方法的DataSource注解失效的解决办法的更多相关文章
- springboot 使用maven 打包 报 (请使用 -source 7 或更高版本以启用 diamond 运算符) 错误解决办法
在使用springboot maven 打包时 报如下错误 (请使用 -source 7 或更高版本以启用 diamond 运算符) pom.xml编译插件 配置如下: <plugin> ...
- web自动化selenium click()方法失效的解决办法
使用Python写web-ui自动化脚本时,如果浏览器窗口比较小或者电脑屏幕比较小时, 可能会遇到页面元素的点击click()方法失效的问题,报错如下: Element <span>... ...
- 第二十六篇、因为自定item(nav)而使系统右滑返回手势失效的解决方法
@interface ViewController () <uigesturerecognizerdelegate> @end@implementation ViewController ...
- SpringBoot 内部方法调用,事务不起作用的原因及解决办法
在做业务开发时,遇到了一个事务不起作用的问题.大概流程是这样的,方法内部的定时任务调用了一个带事务的方法,失败后事务没有回滚.查阅资料后,问题得到解决,记录下来分享给大家. 场景 我在这里模拟一个场景 ...
- vue 父子组件传值以及方法调用,平行组件之间传值以及方法调用大全
vue项目经常需要组件间的传值以及方法调用,具体场景就不说了,都知道.基本上所有的传值都可以用vuex状态管理来实现,只要在组件内监听vuex就好. vue常用的传值方式以及方法有: 1. 父值传子( ...
- python类的内部方法
目录 一.绑定方法与非绑定方法 1.绑定方法 2.非绑定方法 二.property 1.什么是property? 2.为什么要用property? 3.如何使用property? 三.isinstan ...
- SpringBoot在Impl类中调用其它service层失败解决办法
在AImpl.java文件中引用BImpl.java的方法,编译正常,运行到调用的地方,报空指针异常,跟踪到异常位置,发现service为空,也就是按照之前controller层通过@Autowire ...
- Spring 内部方法调用失效问题(AOP)
AOP使用的是动态代理的机制,它会给类生成一个代理类,事务的相关操作都在代理类上完成.内部方式使用this调用方式时,使用的是实例调用,并没有通过代理类调用方法,所以会导致事务失效. 解决办法 方式一 ...
- SpringBoot框架——从SpringBoot看IoC容器初始化流程之方法分析
目录 一.概观Spring Boot 二.Spring Boot应用初始化 2.1 初始化入口 2.2 SpringApplication的run方法 2.3 方法分析 三.容器创建与初始化 3.1 ...
随机推荐
- RPC 框架通俗解释 转自知乎(洪春涛)
本地过程调用 RPC就是要像调用本地的函数一样去调远程函数.在研究RPC前,我们先看看本地调用是怎么调的.假设我们要调用函数Multiply来计算lvalue * rvalue的结果: 那么在第8行时 ...
- golang安装及vscode编辑器配置
安装Go语言及搭建Go语言开发环境 下载 下载地址:https://studygolang.com/dl 系统选择: 根据不同系统下载安装包: 安装 Windows MAC安装 点开可执行程序 下一步 ...
- nova 通过 python curl 创建虚拟机---keystone v3
#! /bin/python #coding=utf- import urllib2 import json import requests # token post_url = 'http://10 ...
- 多主机搭建etcd集群
下载https://github.com/etcd-io/etcd/releases/download/v3.4.10/etcd-v3.4.10-linux-amd64.tar.gz分别放到两台主机上 ...
- 使用dd命令快速生成大文件或者小文件
使用dd命令快速生成大文件或者小文件 需求场景: 在程序的测试中有些场景需要大量的小文件或者几个比较大的文件,而在我们的文件系统里一时无法找到那么多或者那么大的文件,此时linux的dd命令就能快速的 ...
- instanceof判断问题
有时候我们根据instanceof来判断对象的数据类型 但是 用instanceof判断基本数据类型时 会不靠谱 例如 let str = '123' let str1 = new String(&q ...
- 数组的三种方式总结 多维数组的遍历 Arrays类的常用方法总结
一.数组的三种声明方式总结 public class WhatEver { public static void main(String[] args) { //第一种 例: String[] tes ...
- windows server 2008 r2 环境下,实现域名和IP同时都能访问一个网站
有时候,用域名访问能得到一个页面,用IP地址访问也可以得到一个页面,比如 www.baidu.com 和 61.135.169.125 都可以打开百度页面.一开始要实现这种功能,还真有点不知所措,想了 ...
- 你懂RocketMQ 的架构原理吗?
前言 前面我们跟大家聊了聊什么是消息中间件,以及哪些场景使用哪些消息中间件更加合适. 我们了解到RocketMQ是java语言开发的,我们能更深入的阅读源码了解它的底层原理,而且它具有优秀的消息中间件 ...
- 2申请高德地图key 初始化地图
https://console.amap.com/dev/key/app vue-amap-基于-vue-2x-与高德的地图组件 https://elemefe.github.io/vue-amap/ ...