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

      结果如下:

      

      

  • 相关阅读:
    java线程池源码阅读
    websocketcometd源码阅读基于注解初始化(三)
    websocketcometd源码阅读与spring整合初始化(四)
    cometd源码阅读初始化(二)
    cometd源码阅读WebSocketTransport普通消息处理过程(八)
    websocketcometd源码阅读transport(五)
    一.安装软件
    GitHub一篇文章系列
    JavaJVM调优jstack找出最耗cpu的线程&定位问题代码
    Git总目录
  • 原文地址:https://www.cnblogs.com/guobin-/p/13710292.html
Copyright © 2020-2023  润新知