Martin Barth
Martin Barth

Reputation: 796

Resolving Singletons in Spring Boot Rest Controllers with Kotlin

I am new to Spring and Spring Boot and I played around with different ways how to resolve Beans. In my example I've got a Bean that should always be a singleton. What surprises me is that there seems to be a way where this bean is resolved as, I assume, "prototype".

Could anyone explain to me why it's not a singleton when it is resolved in the signature of the method showSingletonBeans?

@SpringBootApplication
class DemoApplication

fun main(args: Array<String>) {
    runApplication<DemoApplication>(*args)
}

@Service("stackSingletonBean")
// @Scope("singleton")
class MySingletonBean {
    init {
        println("Created MySingletonBean " + this.hashCode())
    }
}

@RestController
class MyController {

    @Autowired
    // @Qualifier("singletonBean")
    lateinit var memberSingletonBean: MySingletonBean

    @Autowired
    lateinit var singeltonFactory: ObjectFactory<MySingletonBean>

    fun buildSingleton() : MySingletonBean {
        return singeltonFactory.`object`
    }

    @Lookup
    fun getSingletonInstance() : MySingletonBean? {
        return null
    }

    @GetMapping("/")
    fun showSingletonBeans(@Autowired stackSingletonBean: MySingletonBean) {
        println("member " + memberSingletonBean.hashCode() )
        println("stack " + stackSingletonBean.hashCode())
        println("lookup:" + getSingletonInstance().hashCode())
        println("factory: " + buildSingleton().hashCode())
    }
}

The log looks like that:

2020-08-13 18:44:32.604  INFO 172175 --- [           main] com.example.demo.DemoApplicationKt       : No active profile set, falling back to default profiles: default
2020-08-13 18:44:33.118  INFO 172175 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-08-13 18:44:33.124  INFO 172175 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-08-13 18:44:33.124  INFO 172175 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.37]
2020-08-13 18:44:33.164  INFO 172175 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-08-13 18:44:33.164  INFO 172175 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 528 ms
Created MySingletonBean 1747702724
2020-08-13 18:44:33.286  INFO 172175 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-08-13 18:44:33.372  INFO 172175 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-08-13 18:44:33.379  INFO 172175 --- [           main] com.example.demo.DemoApplicationKt       : Started DemoApplicationKt in 1.011 seconds (JVM running for 1.24)
2020-08-13 18:44:37.341  INFO 172175 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-08-13 18:44:37.341  INFO 172175 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-08-13 18:44:37.344  INFO 172175 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 3 ms
Created MySingletonBean 562566586
member 1747702724
stack 562566586
lookup:1747702724
factory: 1747702724
Created MySingletonBean 389331797
member 1747702724
stack 389331797
lookup:1747702724
factory: 1747702724

Upvotes: 1

Views: 361

Answers (1)

Mafor
Mafor

Reputation: 10681

Resolving controller method parameters is actually quite different mechanism. It has nothing to do with dependency injection and the @Autowired annotation: the annotation can be removed and it won't change the behavior.

Although @Autowired can technically be declared on individual method or constructor parameters since Spring Framework 5.0, most parts of the framework ignore such declarations. The only part of the core Spring Framework that actively supports autowired parameters is the JUnit Jupiter support in the spring-test module (see the TestContext framework reference documentation for details). https://docs.spring.io/

In your case, the stackSingletonBean is instantiated by the ModelAttributeMethodArgumentResolver. It's not aware of the @Service annotation nor of its scope: it simply uses the default constructor on each request.

Model attributes are sourced from the model, or created using a default constructor and then added to the model.

Note that use of @ModelAttribute is optional — for example, to set its attributes. By default, any argument that is not a simple value type( as determined by BeanUtils#isSimpleProperty) and is not resolved by any other argument resolver is treated as if it were annotated with @ModelAttribute. Web on Reactive Stack

Upvotes: 2

Related Questions