BeanDefinitionParsingException when trying to use JPA with Kotlin and Spring boot - spring

I get this exception when i try to use springboot + JPA + kotlin + maven
org.springframework.beans.factory.parsing.BeanDefinitionParsingException:
Configuration problem: #Bean method 'init' must not be private or final;
change the method's modifiers to continue
Offending resource: com.wirecard.kotlin.jpa.Application
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:70)
at org.springframework.context.annotation.BeanMethod.validate(BeanMethod.java:50)
at org.springframework.context.annotation.ConfigurationClass.validate(ConfigurationClass.java:219)
at org.springframework.context.annotation.ConfigurationClassParser.validate(ConfigurationClassParser.java:528)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:307)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:239)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:254)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:94)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:606)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:462)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:957)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:946)
at com.wirecard.kotlin.jpa.ApplicationKt.main(Application.kt:50)
I followed the example in this link but it is built using Gradle
Springboot application for kotlin and JPA
The code consists of 3 classes
Class Customer
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
#Entity
class Customer(
val firstName: String,
val lastName: String,
#Id #GeneratedValue(strategy = GenerationType.AUTO)
val id: Long = -1) {
override fun toString(): String {
return "Customer(id=$id, firstName='$firstName', lastName='$lastName')"
}
}
Class CustomerController
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
#RestController
class CustomerController(val repository:CustomerRepository) {
#GetMapping("/")
fun findAll() = repository.findAll()
#GetMapping("/{lastName}")
fun findByLastName(#PathVariable lastName:String)
= repository.findByLastName(lastName)
}
Interface CustomerRepository
import org.springframework.data.repository.CrudRepository
interface CustomerRepository : CrudRepository<Customer, Long> {
fun findByLastName(lastName: String): Iterable<Customer>
}

From the log it looks like it's actually the 4th class, Application, that you're having a problem with. It says that the init method in that class shouldn't be private or final, your problem here would be the latter - functions and classes in Kotlin are final by default.
You can make the method non-final by marking it with the open keyword.
#Bean
open fun init(repository: CustomerRepository) = CommandLineRunner { ... }
You'll also have to mark your Application class with the open keyword.
#SpringBootApplication
open class Application { ... }
The reason these open keywords are not in the example is because it uses the kotlin-spring plugin (see in the build script), which opens up the classes and functions that Spring requires to be non-final.
You should be able to use the kotlin-spring plugin with Maven as well, if you don't want to manually make your classes open. The instructions for doing so are right above the Gradle instructions, at the end of the chapter about the all-open plugin. You just have to include the code that's there, and replace <plugin>all-open</plugin> with <plugin>spring</plugin>, as the comment there describes.

Related

Spring injects a bean other than what is specified in #Configuration

Recently I've made an error while wiring beans in Spring that caused a behaviour that I'm unable to replicate. Instead of a property sourced with #Value getting injected into Stuff (see the complete demo code below) a value of another bean of type String defined in #Configuration was used when the application was deployed.
What I find puzzling is that everything works as expected when running locally (including the unit test), the output is foo not kaboom, and that this 'bean swap' happened at all when deployed rather than 'no qualifying bean' error.
The commented out line shows the fix which I think makes the configuration similar to what is in the manual.
What is the problem with my set-up? What would make the code as shown (i.e. without the fix) use kaboom String rather than foo property?
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
#SpringBootApplication
open class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
#Configuration
open class Config {
// ...beans of types other than String in original code...
#Bean
open fun beanBomb(): String {
return "kaboom"
}
#Bean
// fix:
// #Value("\${stuff}")
open fun beanStuff(stuff: String): Stuff {
return Stuff(stuff)
}
}
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
#Component
class Stuff(#Value("\${stuff}") val stuff: String)
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import javax.annotation.PostConstruct
#Component
class Init {
#Autowired
private lateinit var stuff: Stuff
#PostConstruct
fun init() {
println("stuff: " + stuff.stuff)
}
}
// application.properties
stuff=foo
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
#TestPropertySource(properties = {"stuff=testFoo"})
class DemoApplicationTests {
#SpyBean
private Stuff stuff;
#Test
void test() {
assertEquals("testFoo", stuff.getStuff());
}
}
Also, is the #Value annotation in Stuff necessary once the fix has been applied? If I uncomment the fix, remove #Value from Stuff and add the following annotation to the test class the test passes:
#ContextConfiguration(classes = {Config.class})
but when I run the app it prints kaboom...
You can check the order in which the bean is being created. if the bean is created before than in my view the Spring IoC container inject the value by type i.e. kaboom and since the Bean of any type is singleton by default, the instance of Stuff won't come into effect even though it is annotated with #component.
In your test you're loading the configuration manually where the bean of Stuff defined in Config is being injected not the Stuff annotated with #component.
The problem is the annotation needs to go on the parameter not the function.
In your way Spring is looking for a bean that meets the Type of String and there is a bean of Type String produced by the function beanBomb(). If you move the annotation like this it should remove the ambiguity.
#Bean
open fun beanStuff(#Value("\${stuff}") stuff: String): Stuff {
return Stuff(stuff)
}
I would add tho, that it's a bit unusual to have a bean of Type String, but I suppose if you don't want to use property/yaml files it would allow you to change a String based on profile.

Autowire Bean and application.yml file in another jar file

I am experimenting with spring boot multi module projects for my understanding.
My Over All Goal is :
1.Build Spring boot project as independent jar and utilise it on another project.
2.Autowire Bean properties inside jar as per new project. Make it independent.
Things I have done so far.
Project providerModule1
Declare a Service(MyService).
#Component
public class MyService {
#Autowired
ServiceProperties serviceProperties;
public String getInfoFromProperties() {
return serviceProperties.toString();
}
}
Create a bean called ServiceProperties that will be used in to MyService.
package com.demo.multimodule.providerModule1.util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import lombok.Data;
#Component
#Data
public class ServiceProperties {
#Value("${default.userName}")
private String name;
#Value("${default.email}")
private String email;
#Value("${default.age}")
private String age;
}
Load the bean ServiceProperties by reading propeties from yml file.
default:
userName: userName1
email: default#email.com
age: 18
Build the Project ProviderModule1 using maven plugin
Project ParentProjectApplication
5. Load the maven dependency.
<dependency>
<groupId>com.demo.multimodule</groupId>
<artifactId>providerModule1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<classifier>app-to-import</classifier>
</dependency>
Autowire the MyService from project providerModule1
package com.multimodule.demo.parentProject.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.demo.multimodule.providerModule1.service.MyService;
#Service
public class HomeService {
#Autowired
private MyService myService;
public String getHomeInfo() {
return "Home Info from Home service : "+myService.getInfoFromProperties();
}
}
Initially myservice was not getting loaded .Hence I added this step to main application class:
#SpringBootApplication(scanBasePackages= {"com.demo.multimodule.providerModule1.service",
"com.demo.multimodule.providerModule1.util"})
public class ParentProjectApplication {
}
Question 1 : Is this the only way using which I can autowire bean from a jar. If there is another way let me know.
I executed the Project ParentProjectApplication and it seem to work as expected.
Question 2 : Is it possible to autowire new yml property from Project ParentProjectApplication and make ServiceProperties bean of project ProviderModule1 utilise it.
you can try spring.factory to create beans.
refer the link below
https://docs.spring.io/spring-boot/docs/2.0.0.M3/reference/html/boot-features-developing-auto-configuration.html

Spring boot cannot find beans

i have a Spring Boot project which has some external packages i need to import as Beans in the main application.
So i have my main application in com.package.app package and some classes (among which some repositories) in com.package.commons package.
In order to take these beans i have my main class annotated as follows:
#SpringBootApplication
#ComponentScan({ "com.package.commons" ,"com.package.app"})
#EnableScheduling
#EnableAsync
public class EmanagerApplication extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(EmanagerApplication.class, args);
}
}
But when i launch the application it may occur (not always but very ofter) that the start up fails with these kind of error:
Description:
Field repository in com.package.commons.service.BrandService required a bean of type 'com.package.commons.persistence.repository.BrandRepository' that could not be found.
Action:
Consider defining a bean of type 'com.package.commons.persistence.repository.BrandRepository' in your configuration.
My BrandRepository is annotated with #Repository and the service class with #Service
The really strange thing is that if i keep launching the app at the end it stars... but there is no reason for it...
If you're using JPA, you'll also need the #EnableJpaRepositories annotation.
Also consider to use #EnableTransactionManagement to enable declarative transaction handling.
E.g. use something like the following in the same package or a parent package where you have your JPA entities and JPA repositories (untested):
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.jta.JtaTransactionManager;
#Configuration
#EntityScan
#EnableJpaRepositories
#EnableTransactionManagement
public class HibernateConfig extends JpaBaseConfiguration {
public HibernateConfig(DataSource dataSource, JpaProperties properties, ObjectProvider<JtaTransactionManager> jtaTransactionManager,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
super(dataSource, properties, jtaTransactionManager, transactionManagerCustomizers);
}
#Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
#Override
protected Map<String, Object> getVendorProperties() {
return new HashMap<>();
}
}
And don't forget to annotate your #Service classes also with #Transactional.
If you confirm that the Application which with the startup method of this application is good, and confirm the #ComponentScan is good also. And the configuration file yaml or properties of JPA also good.
How about trying extends JPA Repository like this:
public class xxxResponsitory extends JpaRepository<T, E>{
...
}
Cause JpaRepository has already annotated with #Repository annotation, T means the type of Primary Key, I always use Integer or Long, autoboxing type. E means the main type of this repository.
Make an example:
Now we have an Entity type named User, the Primary key type of User is Long, I would write the repository like this:
public class UserRepository extends JpaRepository<Long, User>{
...
}
Don't need annotated anything, then, In the service class, #Autowried UserRepository, everything is good to run. But make sure the things that I talk at the start of my answer.
Hope this can help you.

Spring data repository not found at compile time

I am trying to use Spring data and repositories in a Spring Boot application, but I have an error when compiling the project.
Here is my Entity :
package fr.investstore.model;
import javax.persistence.Id;
...
#Entity
public class CrowdOperation {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Long id;
#Enumerated(EnumType.STRING)
public RepaymentType repaymentType;
...
}
And the corresponding Repository:
package fr.investstore.repositories;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import fr.investstore.model.CrowdOperation;
public interface CrowdOperationRepository extends CrudRepository<CrowdOperation, Long> {
}
I use it in a WS controller, generating a repository through the Autowired annotation:
package fr.investstore.ws;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
...
#Controller
#EnableAutoConfiguration
public class SampleController {
#Autowired
private CrowdOperationRepository crowdOperationRepository;
#RequestMapping(path = "/", method = RequestMethod.GET)
#ResponseBody
public String getOperations(#RequestParam(required=true, defaultValue="Stranger") String name) {
crowdOperationRepository.save(new CrowdOperation());
return "Hello " + name;
}
}
And the code of the application:
package fr.investstore;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import fr.investstore.ws.SampleController;
#SpringBootApplication
public class InvestStoreApplication {
public static void main(String[] args) {
SpringApplication.run(SampleController.class, args);
}
}
But when compiling the project I get:
APPLICATION FAILED TO START
Description: Field crowdOperationRepository in
fr.investstore.ws.SampleController required a bean of type
'fr.investstore.repositories.CrowdOperationRepository' that could not
be found.
Action: Consider defining a bean of type
'fr.investstore.repositories.CrowdOperationRepository' in your
configuration.
Woudn't Spring automatically generate a bean for the repository through the interface?
How can I resolve this?
EDIT: I also tried to put the Repository annotation (from org.springframework.stereotype.Repository) onto CrowdOperationRepository, but I got the same error
While creating a spring-boot application, we need to keep some point in our mind like
Always keep main class (class with `#SpringBootApplication annotation) on the top level package and other classes should lie under sub-packages.
Always mark your bean classes with proper annotation e.g. all repositories should be marked by #Repository annotation, all service implementation classes should be marked with #Service, other component classes should be marked by #Component, class which defines our beans should be marked as #Configuration
Enable the feature which you are using e.g. #EnableJpaRepositories, #EnableTransactionManagement, #EnableJpaAuditing, these annotations also provides functionality which let us define which package spring needs to scan.
So in your case, you need to mark InvestStoreApplication class with #EnableJpaRepositories annotation and CrowdOperationRepository with #Repository.
you have to tell your spring boot application to load JPA repositories.
copy this one to your application class
it will auto-scan your JPA repository and load it in your spring container even if you do not define your interface with #Repository it will wire that bean in your dependent class.
#EnableJpaRepositories(basePackages = { "fr.investstore.repositories" })
Thank to #JBNizet for his comment, that made it working.
I create this answer since he did not:
Replace SpringApplication.run(SampleController.class, args); with SpringApplication.run(InvestStoreApplication.class, args);. And remove the useless #EnableAutoConfiguration on your controller.
Annotating your entity class as shown as spring hint below to allow spring get a valid repository bean
Spring Data JPA - Could not safely identify store assignment for repository candidate interface com.xxxxx.xxxxRepository.
If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: javax.persistence.Entity, javax.persistence.MappedSuperclass (preferred),
or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository.
2022-05-06 12:32:12.623 [ restartedMain] INFO [.RepositoryConfigurationDelegate:201 ] - Finished Spring Data repository scanning in 3 ms. Found 0 JPA repository interfaces.

Spring Data JPA repositories not being auto-created with CommandLineRunner

I am trying to write a simple Spring Data JPA app for Spring Boot in Groovy. I followed the getting started guide and did some basic transformation to make it work with Groovy and the Spring Boot CLI.
I am running the code with the Spring Boot CLI (v1.1.8):
spring run app.groovy
This results in the error:
NoSuchBeanDefinitionException: No qualifying bean of type [hello.CustomerRepository] is defined
Does anyone have an idea why the Repository is not getting created automatically? I feel like I must be missing something simple. Here is the app.groovy file containing all of the code:
package hello
#Grab("spring-boot-starter-data-jpa")
#Grab("h2")
import java.util.List
import javax.persistence.*
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.context.annotation.Configuration
import org.springframework.data.repository.CrudRepository
#Entity
class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
long id
String name
Customer() {}
Customer(String name) {
this.name = name
}
}
interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByName(String name)
}
#Configuration
#EnableAutoConfiguration
class Application implements CommandLineRunner {
#Autowired
ConfigurableApplicationContext context
void run(String[] args) {
CustomerRepository repository = context.getBean(CustomerRepository.class)
repository.save(new Customer("Jack", "Bauer"))
}
}
A Groovy CLI app can only scan for JPA repositories if you give it actual classes (i.e. not the .groovy scripts). You can build a jar file and run that, and it should work:
$ spring jar app.jar app.groovy
$ java -jar app.jar

Resources