0

I read these lines from a colleague's code:

    @Bean(name = "mysql")
    @ConfigurationProperties(prefix = "spring.mysql")
    @Primary
    public DataSource mysqlDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public ClassA classA () {
        return new ClassA (this.mysqlDataSource());
    }

    @Bean
    public ClassB classB () {
        return new ClassB (this.mysqlDataSource());
    }

I thought this will create 2 DataSources for Bean classA and classB. for injecting the datasource, we need something like:

    @Bean
    public ClassA classA (DataSource ds) {
        return new ClassA (ds);
    }

But Spring just create one datasource, and this.mysqlDataSource() returns the same one everytime. how does it happen? If I do need another DataSource, i need create it on the fly?

liumin
  • 98
  • 1
  • 10
  • Spring will create only one instance by default by using `@Bean` and normally, it is the right way to do it. Assuming your `DataSource` works as a real datasource, why would you need 2 beans ? – Abbin Varghese Nov 26 '19 at 13:53
  • The second question is just asked for curiosity. by ex: I need a datasource with different timeout. What I really want to know is why this.mysqlDataSource() does not create a new Datasource with spring? – liumin Nov 26 '19 at 14:53

2 Answers2

0

The method this.mysqlDataSource() returns the bean because Spring create a proxy for configuration class. You can see details here

By default Spring container creates bean with scope "singleton". So you have single DataSource instance in container and this instance will be injected to ClassA and ClassB objects. If you want to have different instances you should change scope to "prototype".

You can use annotation @Scope("prototype") to do it.

  • thanks for your reply. but my question is **`this.mysqlDataSource()`** is not a injection, but spring treat it like it is. – liumin Nov 27 '19 at 08:01
  • The method `this.mysqlDataSource()` returns the bean. The method is proxied via CGLIB and therefore the cached version of the bean is returned (a new one is not created). You can look at [this detailed answer](https://stackoverflow.com/a/27990468/12443348) – Alexander Bukharov Nov 27 '19 at 08:24
  • thanks. the link is useful. the other response have more detail directly. – liumin Nov 28 '19 at 08:29
0

Spring says @Component and @Configuration has different meanings. If you use @Configuration instead of @Component, CGLIB proxying will be used.

"The @Bean methods in a regular Spring component are processed differently than their counterparts inside a Spring @Configuration class. The difference is that @Component classes are not enhanced with CGLIB to intercept the invocation of methods and fields. CGLIB proxying is the means by which invoking methods or fields within @Bean methods in @Configuration classes creates bean metadata references to collaborating objects; such methods are not invoked with normal Java semantics but rather go through the container in order to provide the usual lifecycle management and proxying of Spring beans even when referring to other beans via programmatic calls to @Bean methods. In contrast, invoking a method or field in an @Bean method within a plain @Component class has standard Java semantics, with no special CGLIB processing or other constraints applying."

https://docs.spring.io/spring/docs/5.0.4.RELEASE/spring-framework-reference/core.html#spring-core

Alternatively, you can keep @ConfigurationProperties in the class level and remove @Bean from DataSource so that mysqlDataSource() will be treated as a regular method..

Abbin Varghese
  • 2,422
  • 5
  • 28
  • 42