SpringBoot框架:两个方法同时调用时父方法使内部方法的DataSource注解失效的解决办法

一、问题如下:

  使用的是SpringBoot框架:通过AOP和自定义注解完成druid连接池的动态数据源切换(三)中的两个数据库spring_boot_demoother_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;
    }

  结果如下:

  

  

posted @ 2020-09-22 09:59  我命倾尘  阅读(2015)  评论(0编辑  收藏  举报