Spring Kotlin DSL: get all beans of certain type - spring

Suppose I have an interface Yoyo and different realizations of this interface:
interface Yoyo {
fun haha() {
println("hello world")
}
}
#Component class Yoyo1 : Yoyo
#Component class Yoyo2 : Yoyo
#Component class Yoyo3 : Yoyo
#Component class YoyoN : Yoyo
Now I would like to instantiate all beans and do some logic after the context has been initialized:
#SpringBootApplication
class YoyoApp
fun main(args: Array<String>) {
SpringApplicationBuilder()
.sources(YoyoApp::class.java)
.initializers(beans {
bean {
CommandLineRunner {
val y1 = ref<Yoyo1>()
val y2 = ref<Yoyo2>()
val y3 = ref<Yoyo3>()
val yN = ref<YoyoN>()
arrayOf(y1, y2, y3, yN).forEach { it.haha() }
}
}
})
.run(*args)
}
Instead of manually getting ref to all beans (which is rather tedious), I would like to do this:
val list = ref<List<Yoyo>>()
list.forEach { it.haha() }
However I get an exception:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.util.List<?>' available
I know I could do this instead, but I would like use the new Kotlin DSL instead:
#Component
class Hoho : CommandLineRunner {
#Autowired
lateinit var list: List<Yoyo>
override fun run(vararg args: String?) {
list.forEach { it.haha() }
}
}
Is it possible? Any ideas?
P.S. Here is the gist.

The context mentioned in the previous answer by #zsmb13 was left internal in favor of the provider<Any>() function (starting with Spring 5.1.1). So in the end I ended up with the following:
interface Yoyo {
fun haha() {
println("hello world from: ${this.javaClass.canonicalName}")
}
}
#Component class Yoyo1 : Yoyo
#Component class Yoyo2 : Yoyo
#Component class Yoyo3 : Yoyo
#Component class YoyoN : Yoyo
#SpringBootApplication
class YoyoApp
fun main(args: Array<String>) {
SpringApplicationBuilder()
.sources(YoyoApp::class.java)
.initializers(beans {
bean {
CommandLineRunner {
val list = provider<Yoyo>().toList()
list.forEach { it.haha() }
}
}
})
.run(*args)
}

The ref function used in the DSL can be found here in the source of the framework. There is no equivalent for getting all beans of a type, but you could add your own extension to the BeanDefinitionDsl class to do this:
inline fun <reified T : Any> BeanDefinitionDsl.refAll() : Map<String, T> {
return context.getBeansOfType(T::class.java)
}
Only problem is that the context required for this is internal in the currently released version of the framework. This commit from 8 days ago makes it publicly available "for advanced use-cases", but there hasn't been a new release of the framework since, so it's not available yet.
(The same commit also makes the class to extend directly the BeanDefinitionDsl class and not BeanDefinitionDsl.BeanDefinitionContext.)
Conclusion: you'll probably have to wait for the next release that includes the commit mentioned above, and then you'll be able to create this extension for yourself. I've also submitted a pull request in hopes that this could be included in the framework itself.

Related

spring junit test data preparrer class access repositories trough field

I have simple class annotated with #Component and injected repositories like
#Component
class TestsDataPreparer(
private val carRepository: CarRepository
) {
fun withCar(builder: Car.() -> Unit = {}): Car {
return carRepository.save(
Car(
name = builder.name!!
)
)
}
}
which is clear..
But i wonder if it would be ok to do something like this, or if it is considered as anti-pattern.
#Component
class TestsDataPreparer(
val carRepository: CarRepository
) {
fun withCar(builder: Car.() -> Unit = {}): Car {
return carRepository.save(
Car(
name = builder.name!!
)
)
}
}
#Test
fun testCar() {
testsDataPreparer.withCar{this.name="Super-cool-car!"}
assertThat(testsDataPreparer.carRepository.findAll()).hasSize(1)
}
So the question is if it is okay to not inject repository in test class itself, but reuse it from TestsDataPreparer class
Yes, making an originally private field public just for testing can be considered an antipattern. Instead, you can create a CarRepository instance and then pass it to TestsDataPreparer when you create it. But for unit testing, you don't actually need that, you can use a mock and verify that the correct method was called (CarRepository.save).

Kotlin generics with Spring Events

I am trying to setup an event driven architecture on my Spring Application. I have an interface that blueprints all my processes and I have multiple #Components that inherited by that interface. The below is a simplification of my code:
interface Process<T: Any> {
val type: String
val name: String
fun complete(data: T)
fun cancel(data:T)
}
#Component
class MyProcess(prival val service) : Process<MyProcessDTO>
...
where
class MyProcessDTO {
val id: string
val isComplex: Boolean
}
The thing is that I have more than 10 beans than inherit from Process interface and each of them has a different *DTO type.
My idea would be to make a generic event from the given Process interface and publish the derived event types within each #Component. See my idea below :
class ProcessEvent<T: Process<T>> {
}
#Component
class MyProcess(private val service, val publisher: ApplicationEventPublisher) : Process<MyProcessDTO> {
fun publishEvent(myProcessEvent: ProcessEvent<MyProcessDTO>) {
this.service.makeRequest()
this.publisher.publish(myProcessEvent)
}
}
In that case MyProcess should publish a ProcessEvent with MyProcessDTO type.
Please note that the above is just an approximation of my goal.
Thanks

Autowired not working in Scala Spring Boot project

Taking into account the following example where I'm trying to use the Sample configuration bean within SampleStarter to start the service with the bean properly filled. The .scala file has SampleStarter.scala as name, with Sample being defined within that exact same file.
#SpringBootApplication
#Service
object SampleStarter {
#(Autowired #setter)
var sample: Sample = _ // always null, #Autowired not working?
def getInstance() = this
def main(args: Array[String]): Unit = {
SpringApplication.run(classOf[Sample], args: _*)
sample.run()
}
}
#Configuration
#ConfigurationProperties("sample")
#EnableConfigurationProperties
class Sample {
#BeanProperty
var myProperty: String = _
def run(): Unit = { // bean config seems OK, when debugging with #PostConstruct the 'myProperty' value is filled properly
print(myProperty)
}
}
Whenever I hit sample.run() after SpringApplication.run(classOf[Sample], args: _*), sample property is always null. I reckon this has something to do with object in Scala since all their members are static AFAIK. I took this SO question How to use Spring Autowired (or manually wired) in Scala object? as inspiration but still can't make my code to work.
Is there something wrong the way I'm instantiating the classes or is it something related to Scala?
EDIT
Following #Rob's answer, this is what I did trying to replicate his solution.
#SpringBootApplication
#Service
object SampleStarter {
#(Autowired #setter)
var sample: Sample = _
def getInstance() = this
def main(args: Array[String]): Unit = {
SpringApplication.run(classOf[Sample], args: _*)
sample.run()
}
}
#Configuration // declares this class a source of beans
#ConfigurationProperties("sample") // picks up from various config locations
#EnableConfigurationProperties // not certain this is necessary
class AppConfiguration {
#BeanProperty
var myProperty: String = _
#Bean
// Error -> Parameter 0 of constructor in myproject.impl.Sample required a bean of type 'java.lang.String' that could not be found.
def sample: Sample = new Sample(myProperty)
}
class Sample(#BeanProperty var myProperty: String) {
def run(): Unit = {
print(myProperty)
}
}
#Configuration declares a source file that is capable of providing #Beans. This is not what you want. You want/need to have Sample as a POJO class (POSO?) and then have another class "AppConfiguration" (say) annotated with #Configuration that creates an #Bean of type Sample
My scala is very rusty so this may not compile at all but the structure should be roughly correct.
#Configuration // declares this class a source of beans
#ConfigurationProperties("sample") // picks up from various config locations
#EnableConfigurationProperties // not certain this is necessary
class AppConfiguration {
#Bean
def getSample(#BeanProperty myProperty: String): Sample = new Sample(myProperty)
}
class Sample {
var myProperty: String
def Sample(property : String) = {
this.myProperty = myProperty
}
def run(): Unit = {
print(myProperty)
}
}
Now you have a Sample class as an #Bean and it can be #Autowired in where ever necessary.
#SpringBootApplication
#Service
object SampleStarter {
// note its typically better to inject variables into a constructor
// rather than directly into fields as the behaviour is more predictable
#Autowired
var sample: Sample
def getInstance() = this // not required ??
def main(args: Array[String]): Unit = {
SpringApplication.run(classOf[Sample], args: _*)
sample.run()
}
}
FWIW, you can start the SpringBoot application independently and have the logic for #Service entirely separate. An #Service is another type of #Bean and is made available to the rest of the SpringBootApplication.

Spring framework and java like Object collectors In Scala

In Spring framework and Java world, there is an interesting object collector pattern that I use.
For example consider below -
public interface Calculator {
SomeOutput calculate(SomeInput input);
}
#Component
public class CalImpl1 implements Calculator {
public SomeOutput calculate(SomeInput input){
//some implementation
}
}
#Component
public class CalImpl2 implements Calculator {
public SomeOutput calculate(SomeInput input){
//some implementation
}
}
Now this can easily injected in another class using Spring DI
#Component
public class Main {
//This line collects all to implementors of this and set it here.
#Autowired
public List<Calculator> calculators;
//other methods
}
Now problem is I am not sure how same thing can be achieved in scala. I have done some search and found cake pattern (http://loicdescotte.github.io/posts/scala-di/) used in scala but that didn't seem to achieve same thing as object collectors like above. I also want to follow open close principle which I think gets violated in cake pattern but using object collectors I can easily achieve it.
is there a way achieve same object collectors like implementation in scala?
There are templates in lighbend activator that illustration using spring DI on Play, Akka and Scala applications. Please see this: https://www.lightbend.com/activator/templates#filter:spring
I haven't used Spring as DI, I usually use Guice (explicitly used because it's default on play framework 2) and Implicits parameters both as a compilation DI.
Sample:
class B
class X(x: Int)(implicit c: B)
//DI - mostly define in main method/application
implicit val c: B = new B
val x = new X(2)
Explicitly using java.util.List worked for me. This is not the prettiest solution but it shows that it basically works. Haven't tried that but implementing a corresponding PropertyEditor you could stick with the Scala types.
trait Calculator {
def calculate(input: SomeInput) : SomeOutput
}
#Component
class CalImpl1 extends Calculator {
override def calculate(input: SomeInput): SomeOutput = ...
}
#Component
class CalImpl2 extends Calculator {
override def calculate(input: SomeInput): SomeOutput = ...
}
#Component
class Main #Autowired()(calculators: java.util.List[Calculator]) {
// or inject field if constructor injection is not desired
// #Autowired
// var calculators: java.util.List[Calculator] = _
}
object Main {
def main(args: Array[String]) = {
val ctx = new AnnotationConfigApplicationContext("your package name here")
val main = ctx.getBean(classOf[Main])
// calculators should now be wired in the returned instance
}
}

Kotlin Spring class initialization with aspect

I'm trying to use kotlin in my java8 spring project.
I'm doing it by replacing classes (java->kotlin) one by one.
One of my class in Finder:
Finder.java has such structure:
#Compoment
class Finder {
private SomeObject someObject;
Finder() {
someObject = new SomeObject();
}
public void doSomething() { //aspect looks here
someObject.do();
}
}
I've replaced it by Finder.kt
#Compoment
open public class Finder {
private val someObject : SomeObject
constructor() {
someObject = SomeObject()
}
public fun doSomething() { //aspect looks here
someObject.do() //NPE here
}
}
While debuggind, I've found, that constuctor was called, someObject was created when Finder instance was creating. But FinderEnhancerBySpring generated class instance was autowired to Detector instance. It was not initiliazed, so i've got NPE when I try to access to someObject.
Also Finder class has other autowired fields (to simplify code I haven't wrote it here), they were not initiliazed also.
UPD: I've found the aspect on the one of Finder's method. When I deleted it, type the autowired instance became Finder (not FinderEnhancerBySpring) and fully initiliazed.
What can be wrong here?
(0.13.1514 - kotlin version)
All kotlin methods are final (speaking java) by default, so I've to allow overide it with open keyword:
public open fun doSomething() { //aspect looks here
someObject.do()
}

Resources