Functional bean definition Kotlin DSL - add initializer to single test class? - spring

I have a Spring Boot test that is using Redis and I would like to load only Redis related
beans for that test.
I have a function that defines beans:
fun BeanDefinitionDsl.redisBeans() {
bean {
// ...
}
}
And I would like to have only those beans added to the single test class.
Is there a way to do so without adding bean initializer to application properties in the test resource folder?

It turns out that I can have normal Junit test (not a #SpringBootTest)
and get context built with initializers like:
SpringApplicationBuilder()
.bannerMode(Mode.OFF)
.initializers(MyCustomInitializer())
.sources(DummyConfig::class.java)
.properties(
mapOf(
"spring.autoconfigure.exclude" to "..."
)
)
.web(WebApplicationType.NONE)
.build()
.run()
.getBean(MyBeanThatIWantToTest::class.java)
// ...
#EnableAutoConfiguration
#Configuration
class DummyConfig

Related

How to get #Configuration files in Spring to run

I'm writing a micro service using dependency injection, and I have a Java class annotated with #Configuration which creates all my beans. However, my autowired fields are not detecting the beans. How do I get my #Configuration class to run before the application starts?
I tried annotating the classes with the Autowired fields as #ContextConfiguration(classes = Config.class), but this didn't work.
My spring configuration file:
#Configuration
public class Config {
#Bean
public AmazonDynamoDB amazonDynamoDB() {
return ...
}
#Bean
public DynamoDBMapper dynamoDBMapper(AmazonDynamoDB amazonDynamoDB) {
return ...
}
}
I expect the Configuration file to be run and the beans injected, but the beans are not being detected.
There's no main method, since I'm writing this in a service which is created using dependency injection in another service. I'm not sure where I'd tell my application to use my Config file.
probably place a #EnableConfigurationProperties( {Config.class}) above your #SpringBootApplication main class.

How to use Kotlin beans dsl initializer in SpringBootTest

I have a simple application with several beans declared with kotlin beans dsl:
#SpringBootApplication
class App
val beans = beans {
bean<A>()
}
fun main(args: Array<String>) {
runApplication<MatchmakerApp>(*args) {
addInitializers(beans)
}
}
#RestController
class AppController(val a: A) {
// some code
}
class A
and I have an integration test:
#RunWith(SpringRunner::class)
#SpringBootTest
class AppControllerTest {
#Test
fun dummyTest() {
assert(true)
}
}
Launching this test I'm getting
UnsatisfiedDependencyException: Error creating bean with name appController
Caused by: NoSuchBeanDefinitionException: No qualifying bean of type 'A' available:`
It seems beans initializer was not invoked during SpringBootTest context creation.
What do we need to add kotlin bean dsl initializer in SpringBootTest?
The general way with #ContextConfiguration(initializers = ...) does not work here, because it looks for classes.
add FuBeansInitializer in the same package with App class in the test directory:
class FuBeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {
override fun initialize(context: GenericApplicationContext) = beans.initialize(context)
}
add context.initializer.classes into test application.properties:
context.initializer.classes=path.to.FuBeansInitializer
As a result, there will be nothing modified in the source files. And tests will work fine.
You can even have multiple ApplicationContextInitializer and provide a comma separated list of them in your properties(order matters). This can be useful if you used Initializer in your main code and would want to override some of your beans using, again, bean definition dsl.

How to use "Functional bean definition Kotlin DSL" with Spring Boot and Spring WebFlux?

At https://github.com/spring-projects/spring-framework/blob/master/spring-context/src/main/kotlin/org/springframework/context/support/BeanDefinitionDsl.kt the comment shows how to define Spring Beans via the new "Functional bean definition Kotlin DSL". I also found https://github.com/sdeleuze/spring-kotlin-functional. However, this example uses just plain Spring and not Spring Boot. Any hint how to use the DSL together with Spring Boot is appreciated.
Spring Boot is based on Java Config, but should allow experimental support of user-defined functional bean declaration DSL via ApplicationContextInitializer support as described here.
In practice, you should be able to declare your beans for example in a Beans.kt file containing a beans() function.
fun beans() = beans {
// Define your bean with Kotlin DSL here
}
Then in order to make it taken in account by Boot when running main() and tests, create an ApplicationContextInitializer class as following:
class BeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {
override fun initialize(context: GenericApplicationContext) =
beans().initialize(context)
}
And ultimately, declare this initializer in your application.properties file:
context.initializer.classes=com.example.BeansInitializer
You will find a full example here and can also follow this issue about dedicated Spring Boot support for functional bean registration.
Another way to do it in Spring Boot would be :
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args) {
addInitializers(
beans {
// Define your bean with Kotlin DSL here
}
)
}
}
You can define your beans in *Config.kt file and implement initalize method of ApplicationContextInitializer interface.
override fun initialize(applicationContext: GenericApplicationContext) {
....
}
Some bean definition here.
bean<XServiceImpl>("xService")
bean("beanName") {
BeanConstructor(ref("refBeanName"))
}

How can I set the default behavior of lazy init in Spring Boot?

I am working on my first Spring Boot application and I have the following problem.
I want to set the that for default all beans are lazy loaded. I know that I can add the #Lazy to all my #Component beans but I want that for default all beans are setted at lazy...
In Spring Boot I don't have an XML configuration file or a configuration class but I only have an application.properties configuration file.
So, how can I set that the default behavior for all the bean is lazy=true
To implement a BeanFactoryPostProcessor that sets lazy initialization by default (which can be required if you are e.g. defining some of the beans dynamically, outside of your #Configuration class(es)), the following approach worked for me:
#Component
public class LazyBeansFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory( ConfigurableListableBeanFactory beanFactory ) throws BeansException {
for ( String name : beanFactory.getBeanDefinitionNames() ) {
beanFactory.getBeanDefinition( name ).setLazyInit( true );
}
}
}
This essentially puts the #Lazy annotation on all your #Component and #Services. You might want to invent a mechanism to annotate classes with #Eager if you go this route, or just hardwire a list in the LazyBeansFactoryPostProcessor above.
Further reading
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/BeanFactoryPostProcessor.html
Since the version 2.2.2.RELEASE of spring-boot you can use the property below in your application.properties file
spring.main.lazy-initialization=true
for further reading and a good example please refer to
https://www.baeldung.com/spring-boot-lazy-initialization
https://spring.io/blog/2019/03/14/lazy-initialization-in-spring-boot-2-2

Spring Boot - bean definition

I'm looking into using Spring Boot for a new application but I'm having trouble figuring out the best approach to create the application beans.
At a high-level, this would be a web application that can have one or more beans of the same type - each with different property values. If I need to add a new bean of the same type, I should only have to configure it. Typically, if I was using Spring MVC, I would just define each bean in application context and load in the property values via a context file. Spring Boot prefers to do away with xml config, but I'm not sure how to translate the bean definitions into a Spring Boot solution. How do I still take advantage of IoC using Spring Boot.
Actually this has nothing to do with Spring Boot. As you mentioned, it supports both Java and XML bean configurations.
You can easily create multiple beans out of the same class using Java configuration.
XML config like:
<bean id="first" class="com.foo.MyClass" />
<bean id="second" class="com.foo.MyClass" />
translates into:
#Configuration
class MyConfiguration {
#Bean
MyClass first() {
return new MyClass();
}
#Bean
MyClass second() {
return new MyClass();
}
}
with Maciej Walkowiak's answer, it is also recomended to write it like this:
#Configuration
class MyConfiguration {
#Bean
#Qualifier("first")
MyClass first() {
return new MyClass();
}
#Bean
#Qualifier("second")
MyClass second() {
return new MyClass();
}
}
then later when you autowire you can use:
#Autowired
#Qualifier("second")
private MyClass myClass;

Resources