Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I write a Kotlin extension function that uses an autowired Spring bean?

Tags:

spring

kotlin

I'm trying to create an extension function whose implementation uses a Spring bean. It didn't seem possible to do this by defining the extension function at the top level of a package. I tried this:

@Component
class Converter {

    companion object {
        @Autowired
        lateinit var transformer: Transformer

        fun Class1.convert(): Class2 {
            return Class2 (this, transformer.transform(someStringProperty))
        }
    }    
}

where transform is a function that transforms some string to another string, and Class1 is some class that has a someStringProperty property. My hope was that other classes could import pkg.Converter.Companion.convert and then be able to use x.convert() where x is an object of type Class1.

The syntax works, and using x.convert() in other classes compiles fine. But it results in an exception at run time: kotlin.UninitializedPropertyAccessException: lateinit property transformer has not been initialized

It looks like Spring isn't autowiring the variable because it's in a companion object and not an actual component object.

Annotating the companion object with @Component didn't work.

I don't think moving Class1.convert directly inside Converter would work, because then using x.convert() would need an instance of the Converter object, and I don't see how to do this with the extension function syntax.

Is there any way to accomplish this, or do I have to give up on the extension function syntax?

like image 925
ajb Avatar asked Oct 06 '17 00:10

ajb


People also ask

What is the difference between @bean and @autowired?

@Bean is just for the metadata definition to create the bean(equivalent to tag). @Autowired is to inject the dependancy into a bean(equivalent to ref XML tag/attribute).

Can you mention some of the extension methods used in Kotlin?

The list object call the extension function (MutableList<Int>. swap(index1: Int, index2: Int):MutableList<Int>) using list. swap(0,2) function call. The swap(0,2) function pass the index value of list inside MutableList<Int>.

What is Autowired in Kotlin?

Kotlin @Autowired constructor injection This annotation instructs the Spring framework to inject the owner dependency into the Car bean.

Can we Autowire a bean?

Enabling @Autowired Annotations The Spring framework enables automatic dependency injection. In other words, by declaring all the bean dependencies in a Spring configuration file, Spring container can autowire relationships between collaborating beans. This is called Spring bean autowiring.


2 Answers

The extension function can use the file scope private variable and in the component's @PostConstruct method, assign "this" to the private variable. This design adds a dependency from component to the extension function, now they are both binding to the same private variable. But if they both are in the same problem, I think it is tolerable and there seems no other better way.

@Component
public class ClassOne {
    var value = Random.nextInt()

    @PostConstruct
    private fun init() {
        c1 = this
    }
}

private lateinit var c1 : ClassOne
fun String.appendC1() : String {
    return this + c1.value
}

And its test code:

@RunWith(SpringRunner::class)
@ContextConfiguration(classes = [ClassOne::class])
class DependencyInjectionTest {
    @Autowired
    lateinit var autowiredConfiguration : ClassOne

    @Test
    fun testAccessComponentInExtension() {
        Assert.assertEquals(autowiredConfiguration.value, c1.value)
    }
}
like image 94
Houcheng Avatar answered Oct 01 '22 21:10

Houcheng


It can't be done because, because there's no way of auto-wiring static fields in Spring, as explained here: Can you use @Autowired with static fields?

There are certain workarounds but the general recommendation is don't do it.

Having said that if you are really committed to make this solution work you might get aways with it by using a Singleton with parameters as shown in this blog post: https://medium.com/@BladeCoder/kotlin-singletons-with-argument-194ef06edd9e. Although frankly I think such a convoluted solution would completely defeat the purpose

like image 44
jivimberg Avatar answered Oct 01 '22 20:10

jivimberg