【问题标题】:Changing the value of an @Bean at runtime in Java Spring Boot在 Java Spring Boot 中在运行时更改 @Bean 的值
【发布时间】:2019-06-07 13:53:34
【问题描述】:

JavaSpring Boot 框架中,有一个名为DataSource@Bean 用于连接到数据库,并由另一个名为JdbcTemplate@Bean 使用,用于在数据库。但是,有一个问题,这个用于连接数据库的@BeanDataSource 需要预先配置连接的属性(url、用户名和密码)。 @Bean DataSource 需要在项目启动时以“空”或“默认”值开头,并在运行时更改此值。我希望某个 Endpoint 执行更改 @Bean 值的操作,更准确地说。并且随着@BeanDataSourceJdbcTemplate的值的变化,将能够在多个数据库中执行动作。

一些细节:

  • 我已经评估了使用多个数据库的问题,就我而言,这是必要的。
  • 要连接的所有数据库具有相同的模型。
  • 我认为我不需要在运行时删除并创建另一个@BeanDataSource,也许只是覆盖Spring Boot本身已经自动创建的@Bean值。
  • 我已经让@Bean DataSource 以“空”值开头,方法是创建一个带有@Bean 注释的方法,并返回一个DataSource 对象,它实际上就是这个代码:DataSourceBuilder.build().create();
  • 我的英语不是很好,如果不是很容易理解,对不起。

DataSource@Bean代码:

@Bean
public DataSource dataSource() {
    return DataSourceBuilder.create().build();
}

主类:

@SpringBootApplication(scanBasePackages = "br.com.b2code")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class RunAdm extends SpringBootServletInitializer implements 
CommandLineRunner {

    public static final String URL_FRONTEND = "*";


    /**
     * Método main do módulo de Gestão.
     *
     * @param args argumentos de inicialização
     * @throws Exception uma exception genérica
     */
    public static void main(String[] args) throws Exception {
        SpringApplication.run(RunAdm.class, args);
    }

    @Override
    protected SpringApplicationBuilder 
    configure(SpringApplicationBuilder application) {
        return application.sources(RunAdm.class);
    }

    @Override
    public void run(String... args) throws Exception {
    }

}

一个类来举例说明我如何使用JdbcTemplate

@Repository
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ClienteQueryRepositoryImpl implements ClienteQueryRepository {

    private final @NonNull
    JdbcTemplate jdbc;

    @Override
    public List<Cliente> findAll() {
        return jdbc.query(ClienteQuerySQL.SELECT_ALL_CLIENTE_SQL, new ClienteRowMapper());
    }

}

【问题讨论】:

  • 您能否解释一下您为什么要这样做的目的?
  • @ThomasAndolf 我的系统必须是多租户的,不能预先配置多个数据库,这就是我发现它的目的。 :v
  • 是的,但为什么不能预先配置?
  • @ThomasAndolf 因为上面的命令需要它,它是计划的。

标签: java spring spring-boot dependency-injection datasource


【解决方案1】:

我认为作为一种通用方法,您可以考虑为实际数据源实现使用代理设计模式。

假设DataSource 是一个接口,通过用户和密码具有getConnection 方法(其他方法并不重要,因为这个答案是理论上的):

interface DataSource {
   Connection getConnection(String user, String password);
}

现在,为了维护许多数据库,您可能希望提供数据源的实现,该实现将充当将动态创建的其他数据源的代理(如您所说,在端点调用时)。

这是一个例子:

public class MultiDBDatasource implements DataSource {
    private DataSourcesRegistry registry;

    public Connection getConnection(String user, String password) {
        UserAndPassword userAndPassword = new UserAndPassword(user, password);

        registry.get(userAndPassword);
    }
}


@Component
class DataSourcesRegistry {
   private Map<UserAndPassword, DataSource> map = ...
   public DataSource get(UserAndPassword a)  { 
         map.get(a); 
   }


   public void addDataSource(UserAndPassword cred, DataSource ds) {
      // add to Map 
      map.put(...)
   }
}


@Controller
class InvocationEndPoint {

    // injected by spring
    private DataSourcesRegistry registry;

    @PostMapping ... 
    public void addNewDB(params) {
         DataSource ds = createDS(params); // not spring based 
         UserAndPassword cred = createCred(params);
         registry.addDataSource(cred, ds);
    } 

}

几点说明:

  1. 您应该“覆盖”spring 提供的 DataSource bean - 这可以通过在您自己的配置中定义您自己的具有相同名称的 bean 来完成,这将优先于 spring 的定义。

    李>
  2. Spring 不会创建动态数据源,它们将从“调用点”创建(为了简洁起见,本例中的控制器,在现实生活中可能是一些服务)。在任何情况下,spring 只会管理 Registry,而不是数据源。

    1. 请记住,这种方法非常高级,在现实生活中您必须考虑:
      • 连接池
      • 计量
      • 事务支持
      • 多线程访问 和许多其他的东西

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-23
    • 1970-01-01
    • 2020-07-27
    • 2023-01-24
    • 2019-05-30
    • 2017-03-17
    相关资源
    最近更新 更多