I'm encountering an issue with spring and kotlin :
Here it is :
I have a class MyConfig defined as such
#Configuration
class MyConfig
#Bean
fun restTemplate(builder: RestTemplateBuilder): RestTemplate =
builder.build()
On the other side I have an other class MyService defined as such
#Component
class MyService constructor(private val restTemplate: RestTemplate) {
fun test() {
// Use restTemplate
}
}
But all I get is the following message :
Description:
Field restTemplate in my.package.MyService required a bean of type 'org.springframework.web.client.RestTemplate' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.web.client.RestTemplate' in your configuration.
This issue only occurs with beans defined in #Configuration class (annotated with #Bean) but not with bean declared as #Component or #Service.
I have no such issue with the same kind of architecture in pure-Java.
I use spring boot 2.0.1
kotlin 1.2.40
on java 8
So, have I missed something?
Your restTemplate method isn't inside your MyConfig class - you've declared an empty class followed by a top level function. You're missing the curly braces that make the function a method inside the class:
#Configuration
class MyConfig {
#Bean
fun restTemplate(builder: RestTemplateBuilder): RestTemplate =
builder.build()
}
Related
I have a class annotated with a spring bean #Repository("clientDatasource") called ClientServiceDatasource which implements an interface called Datasource. I also have a mock implementation of this interface also annotated with a spring bean #Repository("mockDatasource") called MockClientServiceDatasource. I also have a class annotated with the spring bean #Service called ClientService and in in its constructor, I pass in a datasource. I do it like so:
#Service
class ClientService (#Qualifier("clientDatasource") private val dataSource: Datasource){}
As you can see that the service will default to the clientDatasource, because of the #Qualifier when the application is running.
However when I run my tests I annotate my test class with #SpringTest . In my understanding this means that it boots up the entire application as if it were normal. So I want to somehow overide that #Qualifier bean thats being used in the client service in my test so that the Client Service would then use the mockedDatasource class.
I'm fairly new to kotlin and spring. So I looked around and found ways to write a testConfig class to configure beans like so :
#TestConfiguration
class TestConfig {
#Bean
#Qualifier("clientDatasource")
fun mockDatasource(): Datasource {
return MockClientServiceDatasource()
}
}
and then using it in the test like so:
#SpringTest
#Import(TestConfig::class)
class ClientServiceTest {
...
}
I also asked chatGPT and it gave me this:
#SpringBootTest
class ClientServiceTest {
#Autowired
lateinit var context: ApplicationContext
#Test
fun testWithMockedDatasource() {
// Override the clientDatasource bean definition with the mockDatasource bean
val mockDatasource = context.getBean("mockDatasource", Datasource::class.java)
val mockClientDatasourceDefinition = BeanDefinitionBuilder.genericBeanDefinition(MockClientServiceDatasource::class.java)
.addConstructorArgValue(mockDatasource)
.beanDefinition
context.registerBeanDefinition("clientDatasource", mockClientDatasourceDefinition)
// Now the ClientService should use the mockDatasource when it's constructed
val clientService = context.getBean(ClientService::class.java)
// ... do assertions and other test logic here ...
}
}
But some of the methods don't work, I guess chatGPT knowledge is outdated.
I also looked through spring docs, but couldn't find anything useful.
Okay, So I took a look at the code previously with the TestConfig class. And I realised by adding the:
#Primary
annotation to the method inside my TestConfig class, it basically forces that to be the primary repository bean. Like so:
#TestConfiguration
class TestConfiguration {
#Bean
#Primary
#Qualifier("clientDatasource")
fun mockDatasource(): Datasource {
return MockClientDataSource()
}
}
and in the test I only imported the test and it just worked. I didn't have to autowire anything
This is my test class:
#SpringBootTest
#AutoConfigureMockMvc
#Import(TestConfiguration::class)
internal class ServiceControllerTest{
#Suppress("SpringJavaInjectionPointsAutowiringInspection")
#Autowired
lateinit var mockMvc: MockMvc
#Test
fun `should return all clients` () {
// when/then
mockMvc.get("/clients")
.andDo { print() }
.andExpect {
status { isOk() }
content { contentType(MediaType.APPLICATION_JSON) }
jsonPath("$[0].first_name") {value("John")}
}
}
}
I have a #ConfigurationProperties class that is no longer binding to a YML property source that gets resolved via Spring Cloud Config after upgrading to Hoxton.SR7. This code works fine using Hoxton.SR4 with the latest Spring Boot 2.2.9.RELEASE. Now, my properties are not bound and I'm receiving NPEs when I try to reference them. Following is a snapshot of my code:
#Configuration
public class MyConfiguration {
#Bean
public MyPropertiesBean myPropertiesBean() {
return new MyPropertiesBean();
}
}
#ConfigurationProperties(prefix = "com.acme.properties")
#Validated
public class MyPropertiesBean {
...
}
In src/main/resources/META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.acme.MyConfiguration
Any ideas why my #ConfigurationProperties class doesn't bind after upgrading Spring Cloud to Hoxton.SR7?
You're mixing two ways of binding properties: class and method.
Using a method and #Bean annotation:
#Configuration
public class MyConfiguration {
#Bean
#ConfigurationProperties(prefix = "com.acme.properties")
#Validated
public MyPropertiesBean myPropertiesBean() {
return new MyPropertiesBean();
}
}
This will create MyPropertiesBean and store it inside the application context for you to inject.
Class level bean declaration also creates a bean for you:
#Configuration
#ConfigurationProperties(prefix = "com.acme.properties")
#Validated
public class MyPropertiesBean {
...
}
This will also store a bean.
Although, you should be getting a runtime error when you try to inject MyPropertiesBean as now in your case there's two beans of the same type and Spring cannot resolve with only the type.
I have the following code:
This is my first class that will to call the
#Component
class DefaultBridge #Autowired constructor(
private val clientConfig: Config,
private val client: world.example.Client
) : Bridge {
override fun doEpicStuff(): String {
val request = createRequest(...)
val response = client.makeCall(request)
return response.responseBody
}
}
The Client being used (world.example.Client) then has the following piece of code:
public class Client {
#Autowired
private RestTemplate restTemplate;
public Response makeCall(Request request) {
restTemplate.exchange(....)
}
}
When running the code. I get the following Error:
NoSuchBeanDefinitionException: No qualifying bean of type
'org.springframework.web.client.RestTemplate' available: expected at
least 1 bean which qualifies as autowire candidate. Dependency
annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Where should I declare the bean when the constructor is autowired?
why won't spring automatically create the bean?
I need help understanding the problem so please don't just post the solution.
With Spring dependency injection you can inject a instance of a bean.
In Spring it's called Inversion of Control (IoC) principle. IoC is also known as dependency injection (DI).
You can define depencenies for your bean with member variable or constuctor variables. There are differnt possibilities, for example #Autowire as annotation for member variables like your usage.
The container then injects those dependencies when it creates your bean.
Attention, however, it is necessary that the spring container knows how the dependency to be injected. There are many ways to teach the container this. The simplest variant is to provide a #Bean producer method in a #Configuration class. Spring container "scans" all #Configurations at the start of the container and "registers" the #Bean producer.
Nobody has done a producer for RestTemplate. Not Spring self and also no other lib. With many other beans you use, this has already been done, not with RestTemplate. You have to provide a #Bean producer for RestTemplate.
Do this in a new #Configuration or in one of your existing #Configuration.
A #Configuration indicates that a class declares one or more #Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime, for example:
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate() {
// New instance or more complex config
return new RestTemplate();
}
}
Define RestTemplate as a bean in your configuration class.
Do we have a spring annotation that provides an option to initialize a bean (not a component) if not available through default constructor while autowiring?
If yes, that will be awesome. I am tired of initializing beans in some configuration class using default constructor and it occupies space.
I am doing this currently
#Bean
public Test test() {
return new Test();
}
Expecting:
Sometime like:
#Autowire(initMethodType=constructor)
private Test test:
If no, was there no real need of such annotation or any technical limitation?
You have to use #Bean annotation inside an #Configuration class.
Check the following link
https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch02s02.html
#Configuration
public class AppConfig {
#Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
You could annotate your class with #Component, like this:
#Component
public class Test {
...
}
Whereever you need that class, you could then simply autowire it:
#Autowired
private Test test;
You would just have to make sure, that you use #ComponentScan to pick up that bean. You can find more information on that here.
I use Spring Boot MVC application.
I have a #Configuration class that initializes a bean into ApplicationContext using #Bean.
I have a #Controller class into which I am trying to autowire the bean using #Autowired annotation.
Result: #Autowired field is null.
DEBUG: I tried to debug to see the order of execution. I was expecting to see that class annotated with #Configuration will run first to initialize bean into application context. However, controller class got instantiated first. Then #Bean method of configuration class got called next. Due to this bean is instantiated after controller and that is why Controller is not getting bean autowired.
Question: How to have #Configuration #Bean method execute prior to the controller class instantiation?
code for configuration class:
#Configuration
public class RootConfiguration2 {
#Autowired
private IService<ActBinding> bindingService;
#Bean
public Map<String, String> getBingindNameToRoutingKeyMap() throws Exception {
ListOperation<ActBinding> operation = ListOperation.from("key", "name", "exchangeId");
operation.sort("key", SortOrder.Ascending);
Iterable<ActBinding> result = bindingService.list(operation).getResult();
Map<String, String> bindingNameToRoutingKey = new HashMap<>();
result.forEach(x -> bindingNameToRoutingKey.put(x.getName(), x.getKey()));
return Collections.unmodifiableMap(bindingNameToRoutingKey);
}
}
I found two workarounds. Both solutions worked:
1. Use #Resource instead of #Autowired to inject the bean into controller.
2. Use #PostConstruct on the method annotated with #Bean in Configuration class.
Note: You dont have to do both of the changes. Any one of them should work.