简体   繁体   English

在Java Spring Boot中在运行时更改@Bean的值

[英]Changing the value of an @Bean at runtime in Java Spring Boot

In Java with Spring Boot framework there is an @Bean named DataSource that is used to make connections to a database, and is used by another @Bean called JdbcTemplate that serves to perform actions on the database. 在带有Spring Boot框架的Java ,有一个名为DataSource@Bean用于建立与数据库的连接,并由另一个名为JdbcTemplate @Bean使用,用于对数据库执行操作。 But, there is a problem, this @Bean DataSource , which serves to make the connection to the database, requires the properties for the connection to be preconfigured (url, username and password). 但是,有一个问题,这个用于建立与数据库连接的@Bean DataSource需要预先配置连接属性(url,用户名和密码)。 The @Bean DataSource needs to start with an "empty" or "default" value at project startup, and at runtime it changes this value. @Bean DataSource需要在项目启动时以“空”或“默认”值开始,并且在运行时它会更改此值。 I want a certain Endpoint to execute the action of changing the value of @Bean , to be more exact. 我希望某个端点执行更改@Bean值的@Bean ,更确切地说。 And with the change of the value of the @Bean DataSource the JdbcTemplate , consequently, will be able to perform actions in several database. 因此,随着@Bean DataSource值的更改, JdbcTemplate将能够在多个数据库中执行操作。

Some details: 一些细节:

  • I have already evaluated this issue of using multiple databases, and in my case, it will be necessary. 我已经评估了使用多个数据库的这个问题,在我看来,这是必要的。
  • All databases to be connected have the same model. 要连接的所有数据库都具有相同的模型。
  • I do not think I need to delete and create another @Bean DataSource at runtime, maybe just override the @Bean values ​​that the Spring Boot itself already creates automatically. 我认为我不需要在运行时删除并创建另一个@Bean DataSource ,也许只是覆盖Spring Boot本身已经自动创建的@Bean值。
  • I have already made the @Bean DataSource start with an "empty" value by making a method with the @Bean annotation and that returns a DataSource object that is literally this code: DataSourceBuilder.build().create(); 我已经作出的@Bean DataSource通过与方法用一个“空”值开始@Bean注释和返回一个DataSource对象,它是从字面上此代码: DataSourceBuilder.build().create(); .
  • My English is not very good so if it's not very understandable, sorry. 我的英语不是很好,如果它不是很容易理解,对不起。

DataSource @Bean code: DataSource @Bean代码:

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

Main class: 主要课程:

@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 {
    }

}

A class to exemplify how I use JdbcTemplate : 一个类来举例说明我如何使用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());
    }

}

I think as a general approach you might consider a Proxy Design Pattern for the actual DataSource Implementation. 我认为作为一种通用方法,您可以考虑实际数据源实现的代理设计模式。

Let's suppose the DataSource is an interface that has getConnection method by user and password (other methods are not really important because this answer is theoretical): 假设DataSource是一个具有用户和密码的getConnection方法的接口(其他方法并不重要,因为这个答案是理论上的):

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

Now, in order to maintain many databases you might want to provide an implementation of the datasource which will act as a proxy for other datasources that will be created on the fly (upon the endpoint call as you say). 现在,为了维护许多数据库,您可能希望提供数据源的实现,该实现将充当将动态创建的其他数据源的代理(如您所说,在端点调用时)。

Here is an example: 这是一个例子:

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

}

A couple of notes: 几个笔记:

  1. You should "override" the bean of DataSource offered by spring - this can be done by defining your own bean with the same name in your own configuration that will take precedence over spring's definition. 你应该“覆盖”spring提供的DataSource bean - 这可以通过在你自己的配置中定义你自己的bean来完成,这个bean将优先于spring的定义。

  2. Spring won't create Dynamic Data Source, they'll be created from the "invocation point" (controller in this case for brevity, in real life probably some service). Spring不会创建动态数据源,它们将从“调用点”创建(控制器在这种情况下为了简洁,在现实生活中可能是一些服务)。 in any case only Registry will be managed by spring, not the data sources. 在任何情况下,只有注册表将由spring管理,而不是数据源。

    1. Keep in mind that this approach is very high-level, in a real life you'll have to think about: 请记住,这种方法非常高级,在现实生活中你必须要考虑:
      • Connection Pooling 连接池
      • Metering 测光
      • Transaction Support 交易支持
      • Multithreaded Access and many other things 多线程访问和许多其他事情

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM