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;
    }

  結果如下:

  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM