Spring: Properly use of lombock Builder - spring

I faced with problem while using of lombock #Builder.
In SpringBoot application I create the following component:
#Getter
#Builder
#Component
#AllArgsConstructor
public class ContentDTO {
private UUID uuid;
private ContentAction contentAction;
private String payload;
}
But when I run the application< I receive:
Error creating bean with name 'contentDTO': Unsatisfied dependency expressed through constructor parameter 0
Caused by:
No qualifying bean of type 'java.util.UUID' available: expected at least 1 bean which qualifies as autowire candidate
"Finger to the sky", I changed lombock-builder to custom builder, like this:
#Getter
#Component
public class ContentDTO {
private ContentDTO() {
}
// fields
public static Builder newBuilder() {
return new ContentDTO().new Builder();
}
public class Builder{
private Builder() {
private constructor
}
public ContentDTO build() {
return ContentDTO.this;
}
}
}
And problem is gone.
Its nice, but I clearly dont understand, what was problem!
Why in this case lombock-builder prevented the autowiring of beans?
And how to use lombock-builder properly in Spring ApplicationContext?

the use of the builder requires a default constructor. When you added the #AllArgsConstructor annotation the problem appears. therefore, you must also add the #NoArgsConstructor annotation. That should be the solution for your code.

Well ContentDTO has the #Component annotation therefore Spring tried to pick up and register an instance of ContentDTO in order to do so It tried to create an instance using all args constructor generated by Lombock since it was the only available constructor.
It failed due to it couldn't find registered beans with the given types expected by the ContentDTO constructor.
Adding #NoArgsConstructor or a default constructor without args like you did will work, the Builder is not related.

Related

Can't inject repository in springboot

This is my repository:
#Repository
public interface GroupRepository extends JpaRepository<Group, Integer> {}
This is how I tried to use it in my service class:
#Service
public class GroupService {
private final GroupRepository groupRepository;
#Autowired
public GroupService(GroupRepository groupRepository) {
this.groupRepository = groupRepository;
}
Got this error :org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'groupController': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'groupService' defined in file [/Users/nathanxuan/Files/Project/ratingApp/target/classes/com/xjy/service/GroupService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'groupRepository' defined in com.xjy.mapper.GroupRepository defined in #EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class com.xjy.pojo.Group
Does anyone know why this is happening? When I commented out the repository class everything works fine (I have another mapper class and that one also works).
-------edit-----------
This is the structure of my app:
I think the problem is with the model class Group, it needs the Entity annotation since you are using JPA. Try out this:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Group {
....
}
Note: #Data #AllArgsConstructor #NoArgsConstructor comes from Lombok, so if you're not using it, just create regular Getter and Setter
Try removing #Repository from your repository. By extending from JpaRepository should be enough.
Unless you really need a setter injection defining your repository in the service like this should also be fine and more concise.
#Autowired
private GroupRepository groupRepository;

How to use unit of work in rest controller properly?

public interface CourseRepo extends CrudRepository<Course, Long> {
}
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class UnitOfWork {
CourseRepo courses;
StudentRepository students;
StudyProgramRepository studyPrograms;
StudySchemeRepo studySchemes;
FeeStructureRepository feeStructures;
}
#RestController
public class TestController {
#Autowired
UnitOfWork uow;
#GetMapping("/addcr")
public String addCourse() {
Course cr = new Course();
cr.setTitle("DingDong course");
uow.getCourses().save(cr);
return "course Added..!!" ;
}
APPLICATION FAILED TO START
***************************
Description:
Field uow in com.srs.TestController required a bean of type 'com.srs.uow.UnitOfWork' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.srs.uow.UnitOfWork' in your configuration.
if i remove autowired and add a bean
#RestController
public class TestController {
#Bean
public UnitOfWork uow() {
return new UnitOfWork();
}
#GetMapping("/addcr")
public String addCourse() {
Course cr = new Course();
cr.setTitle("DingDong course");
uow().getCourses().save(cr);
return "course Added..!!" ;
}
java.lang.NullPointerException: Cannot invoke "com.srs.jpa.CourseRepo.save(Object)"
because the return value of "com.srs.uow.UnitOfWork.getCourses()" is null
i tried both autowired and in this case how can i use autowired or bean properly ?
Your class need to be annotated with #Component to be used with DI provider by #Autowired annotation
For the same reason each repository of your class need to be annotated with #Autowired
The Error Message gives the answer.
Field uow in com.srs.TestController required a bean of type 'com.srs.uow.UnitOfWork' that could not be found.
spring is searching for a bean from type UnitOfWork. You have to add this class to the application context from spring boot. To accomplish this you have to annotate the class UnitOfWork with #bean or #Data if you use lombok.
After this the spring application can find the Class UnitOfWork and auto wire it.
Since UnitOfWork (a somewhat misleading name in the JPA context) autowires data repositories, it has to be a Spring Bean itself.
The easiest and most common way is to annotate the class with one of the annotations #Service, #Component or #Bean, depending on the semantic of the class. There are also other ways, like the #Bean on method-level as you used.
To use the fully initialized bean you need to autowire it where you want to use it, not calling the create method. E.g. calling uow() as in your sample, bypasses the Spring Bean mechanism and creates a new instance, which hasn't been fully initialized (thus the NullPointerException).
Usually, the beans are autowired as fields, sometimes they are autowired in mehtod parameters (especially when working with #Bean on method-level in the same class).
E.g.
#Component
#Getter
#RequiredArgsConstructor
public class UnitOfWork {
private final CourseRepo courses;
private final StudentRepository students;
private final StudyProgramRepository studyPrograms;
private final StudySchemeRepo studySchemes;
private final FeeStructureRepository feeStructures;
}

Spring: Qualifying different types with the same name

I have two classes that I want to autowire using spring
#Component
public class Restaurant {
#Autowired
#Qualifier("HighClass")
private CoffeeMaker coffeeMaker;
}
and:
public class CappuccinoMaker implements CoffeeMaker{
#Autowired
#Qualifier("HighClass")
int numOfSpoons;
}
Then injecting:
#Bean(name="HighClass")
#Scope("prototype")
public CoffeeMaker HighClassCoffeeMakerGenerator() {
return new CappuccinoMaker();
}
#Bean(name="HighClass")
public int getNumOfSpoons() {
return 3;
}
I'd like to qualify both the int and the CoffeeMaker with "HighClass". In Guice it is possible to annotate different types with the same annotation and inject them correctly.
It seems like in spring this is not allowed. When I try injecting the fields I get an error that the required bean was not found. Did I miss anything?
Bean name in Spring is unique. Use #Profile in order to pick a bean from several competing variants.

"I need required a bean of type" Error : Spring-boot

I get an error when I try to execute SpringBoot. because I need a " bean ", I don't understand why I get this, I have all annotations
17-09-2018 12:24:53.905 [restartedMain] WARN o.s.b.c.e.AnnotationConfigEmbeddedWebApplicationContext.refresh - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'parameterController': Unsatisfied dependency expressed through field 'pgService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'parameterServiceImp': Unsatisfied dependency expressed through field 'pgRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'es.my.repository.ParameterRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
With more error :
APPLICATION FAILED TO START
Description:
Field pgRepository in es.service.ParameterServiceImp required a bean of type 'es.repository.ParameterRepository' that could not be found.
Action:
Consider defining a bean of type 'es.repository.ParameterRepository' in your configuration.
I have in my controller -> with #Autowired
#RestController
#RequestMapping(value = { "/param" })
#CrossOrigin
public class ParameterController {
#Autowired
ParameterService pgService;
#RequestMapping(method = RequestMethod.GET, value = "/get", produces =
MediaType.APPLICATION_JSON_VALUE)
public List<Parameter> getAllParameters() {
List<Parameter> list = pgService.selectAll();
return list;
}
In my service -> I don't use annotations
public interface ParameterService {
public List<Parameter> selectAll();
}
Imple-> I use Service and Autowired
#Service
public class ParameterServiceImp implements ParameterService {
#Autowired
ParameterRepository pgRepository;
public List<Parameter> selectAll() {
return pgRepository.findAll());
}
}
Repository -> Here , I have querys.
public interface ParameterRepository extends CrudRepository<Parameter, String> {
}
Model ->
My POJO
#Entity
#Table(name = "Parameter")
public class Parameter {
#Id
#NotNull
#Column(name = "ID")
private String id;
#NotNull
#Column(name = "name")
private String name;
// getters setters and construct
}
I have #Entity , #Service , #Autowired but I get an error
If you use #SpringBootApplication with no basePackage specified, it will default to the current package. Just like you add #ComponentScan and #EnableJpaRepositories with no base package.
If you have set a different package to #SpringBootApplication make sure you also add #EnableJpaRepositories with proper basePackage. Repositories won't be recognized only by #ComponentScan(or declaring them as beans by any other ways explicitly or implicitly).
Try adding the next annotation.
#EnableJpaRepositories(basePackages = {"<repository-package-here>"})
is a.some.package package for your #SpingbootApplication?
other wise you need to add component scan annotation for your base package that is a.some.package

Injecting 2 bean with same class name

I have a app 'app_test' which consists a class TestClass with #Service anotation. I have a library class 'lib_test' with bean in XML file with id=''TestClass'. Both are in different package.
I m injecting #Service bean as follows
Import com.app.TestClass
Class TestController
{
Private final TestClass testClass;
#Inject
TestController (TestClass testClass)
{
This.testClass =testClass;
}
}
It should inject by type since they are in different package. But the controller is giving qualified bean not found.
I can resolve it by giving #Qualifier and giving name to #Service. But y is it needed? Since both are in different package it should autowire by type right? Or m missing some concept?
Although they are in different packages if they are of the same type Spring does not know which to use
I'd suggest marking any service class with #Primary.
package com.app.TestClass
#Primary
#Repository
public class TestClass implements XXX
This way it will be selected as the default autowire candididate, with no need to autowire-candidate on the other bean.
Also, rather than using #Autowired #Qualifier, I find it more elegant to use #Resource for picking specific beans.
I've always found this a strange limitation of Spring's standard bean naming convention. It does not include the package part of the class in the name leading to duplicates in large projects when classes have the same name.
This is why I always configure Spring projects with a different BeanNameGenerator:
public class CustomAnnotationConfigWebApplicationContext extends AnnotationConfigWebApplicationContext {
private BeanNameGenerator qualifiedAnnotationBeanNameGenerator = new QualifiedNameAnnotationBeanNameGenerator();
#Override
protected BeanNameGenerator getBeanNameGenerator() {
return this.qualifiedAnnotationBeanNameGenerator;
}
}
And the generator:
public class QualifiedNameAnnotationBeanNameGenerator extends AnnotationBeanNameGenerator {
#Override
protected String buildDefaultBeanName(BeanDefinition definition) {
String qualifiedName = definition.getBeanClassName();
return Introspector.decapitalize(qualifiedName);
}
}
With this setup, common class names that are in different packages are automatically recognized as being different and the correct one gets injected.

Resources