#AllArgsConstructor not working with spring bean - java

I am having the class like below
#Controller
#RequestMapping(value = "/test")
#AllArgsConstructor
public class TestController {
#Qualifier("userDAO")
private final Test testDAO;
}
Below is my bean registration
<bean id="userDAO" class="com.test.dao.TestImpl"
p:sqlSessionFactory-ref="testSqlSessionFactory" />
when I run my app got error like below
No default constructor found; nested exception is java.lang.NoSuchMethodException bean configuration
Also I tried to add the lombok.config file in root and copied the Qualifier annotation, that's also not helped.
spring version is 3.2.15. lombok version 1.18.16
It's working fine with below
#Autowired
public TestController(#Qualifier("userDAO") final Test testDAO) {
this.testDAO = testDAO;
}
How to resolve this?

Adding only an #AllArgsConstructor isn't enough, as it will add the constructor but it doesn't add #Autowired. Because that is missing Spring will look for the default constructor.
To fix you can do 1 of 3 things
Upgrade to Spring 4.3 or higher, as that will automatically use the single constructor and autowire it
Instruct lombok to add #Autowired to the constructor.
Ditch lombok and just provide the constructor yourself.
The first should be pretty easy (include a newer version of Spring in your dependencies). The second requires some additional code.
#Controller
#RequestMapping(value = "/test")
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class TestController {
private final Test testDAO;
}
The #Qualifier won't work (and should be removed) as it should be on the constructor argument.
I would just ditch Lombok for this case and just add the constructor (option 3).
#Controller
#RequestMapping(value = "/test")
public class TestController {
private final Test testDAO;
#Autowired
public TestController(#Qualifier("userDAO") Test testDao) {
this.testDao=testDao;
}
}

Related

JAVA - Can I delete #Autowire directly?

after my teammates merged her changes (implements #EnableFeignClients for our SpringBootApplication and created some feign clients, properties and configs from the main branch), when boot the application it will pop up the The dependencies of some of the beans in the application context from a cycle: xxx
To resolve this issue I deleted some #Autowired from some involved controller/service classes and make them private and then the application can be boot successfully:
before my change:
#Autowired
MyUtil myUtil; //#Component
#Autowired
MyConfig myConfig; //#Component
#Autowired
MyApi myApi; //Interface class
public void myFunction(){
String id = myUtil.getId();
String name = myConfig.getNameById(id);
myApi.sendInfo(id, name);
}
after my change:
private MyUtil myUtil; //#Component
private MyConfig myConfig; //#Component
private MyApi myApi; //#Component
public void myFunction(){
String id = myUtil.getId();
String name = myConfig.getNameById(id);
myApi.sendInfo(id, name);
}
Spring version:
<groupID>org.springframework.boot</groupID>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.11</version>
and here are my concerns:
Will there be any impact after my changes (functional, performance, etc.)?
In my case (or in most scenarios in the future development), should i use #Autowired? When need to? When doesn't need to?
TL DR. You cannot delete #Autowired directly.
#Autowired is used by Spring in order to do dependency injection ( I know this is a fancy word)
#Autowired
ChickenService chickenService
#Autowired is almost equivalent to ChickenService chickenService = new ChickenService();
After your change,private MyConfig myConfig; //#Component myConfig will be null and therefore you introduced a bug.
Suppose that you have an EggService
#Service
public class ChickenService {
#Autowired
private EggService eggService;
}
The chicken holds a reference to the egg
#Service
public class EggService {
#Autowired
private ChickenService;
}
Now you have a chicken and egg problem.
https://www.baeldung.com/circular-dependencies-in-spring
#Autowired help the spring to find where it should inject classes.
The impact is that spring will not create objects of the classes where you removed the annotation.
Additional way to create objects in spring are with setter or constructor injection.
Example of constructor injection will be something like:
public YourClassName(MyUtil myUtil, MyConfig myConfig, MyApi myApi){
this.myUtil = myUtil;
this.myConfig = myConfig;
this.myApi = myApi;
}
I think also good idea as well to check why #Autowired not working. Can you provide full error? Does your class annotated with #Service or something

Test uses internal ContextConfiguration of other test

I created a new test in my Project. For this one I used #ContextConfiguration with an internal Configuration class in the same class as the Test. But now my other tests are failing because they are using the configuration of the new test.
How is this possible, I thought it is not possible to use a configuration inside of a test class from outside.
When I remove the internal configuration from the new test every other test works fine again.
#DataJpaTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#ContextConfiguration(classes = EventServiceTest.Config.class)
class EventServiceTest {
#Configuration
#Import({WorkingTimeConfig.class,
PartnerConfig.class,
ProjectConfig.class,
UserConfig.class,
AccountGroupConfig.class,
LanguageConfig.class,
CountryConfig.class,
EventConfig.class,
LanguageConfig.class})
static class Config {
#SuppressWarnings("unused")
#MockBean(reset = MockReset.BEFORE)
private UserAttendanceBoard userAttendanceBoard;
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Bean
public ImpersonateProperties impersonateProperties() {
return new ImpersonateProperties();
}
}
...
}
Now this Test is not working:
#Import(MailSenderAutoConfiguration.class)
#DataJpaTest
#Transactional
public class ServiceTimeEntryServiceTest {
private ServiceTimeService serviceTimeService;
private ServiceTimeEntryRepository repository;
#Autowired
public ServiceTimeEntryServiceTest(ServiceTimeService serviceTimeService, ServiceTimeEntryRepository repository) {
this.serviceTimeService = serviceTimeService;
this.repository = repository;
}
#Test
void getAllByAccountId() {...}
This error is thrown if I try to start my old tests:
org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'passwordEncoder' defined in class path resource [de/hlservices/timetracking/api/business/event/EventServiceTest$Config.class]: Cannot register bean definition
Thanks for your help :)
As Maciej Kowalski pointed out, this issue is probably related to a #ComponentScan annotation.
If you're using it, consider adding an excludeFilter to ensure you only get what you really want. You might want to exclude other configuration classes to be found by your #ComponentScan annotation:
#ComponentScan(excludeFilters = {
#ComponentScan.Filter(type = FilterType.ANNOTATION,
value = Configuration.class)
})
Btw: I really recommend using IntelliJ IDEA as IDE because of the awesome spring support.
You can lookup what beans/components are found by your scan just by clicking on the green icon left of your code (line:9) :
This makes debugging scanning issues way easier.
I had the same issue in my project and it was due to the fact that the #ComponentScan was picking up also that class due to the #Configuration annotation.
Everything worked fine when I removed that annotation and thus making the component scan to omit it. So you can have just like that:
#Import({WorkingTimeConfig.class,
PartnerConfig.class,
ProjectConfig.class,
UserConfig.class,
AccountGroupConfig.class,
LanguageConfig.class,
CountryConfig.class,
EventConfig.class,
LanguageConfig.class})
static class Config {
Removing #Configuration annotation did not prevent #ContextConfiguration(classes = EventServiceTest.Config.class) config to pick it up anyway.

NullPointerException when autowiring into non-spring managed class (POJO) - Spring Boot

I'm relatively new to Spring Boot and dependency injection overall, so please forgive any noob things going on here. I'm building an API and am having trouble when injecting dependencies into a POJO resource (DTO).
When I call the method in the POJO this.numComments = commentSvc.getAllForPhoto(this.getId()); I am getting a NullPointerException. However, when I do this from another spring-managed bean and pass the values into the constructor, it works fine.
After reading around, it looks like I need to do something with aspectJ and Load Time Weaving, but I'm not sure what that would look like in my code.
In essence, my approach looks something like this:
PhotoResource.java (POJO)
public class PhotoResource extends BaseRepresentable {
#Autowired
CommentService commentSvc;
private Long id;
private Integer numComments;
PhotoResource(PhotoEntity entity){
super(entity);
this.setId(entity.getId);
this.numComments = commentSvc.getAllForPhoto(this.getId());
}
}
CommentService.java
#Service
public class CommentService{
public List<CommentResource> getAllForPhoto(Long photoId) {
// code to get all comments for photo
}
}
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
Spring won't inject the dependency unless you ask the Spring container to manage the bean. In order for commentSvc to be injected into PhotoResource class, you need to annotate it with #Component or #Bean or #Service, e.g.:
#Component
public class PhotoResource extends BaseRepresentable {
#Autowired
CommentService commentSvc;
}
And make sure the package of this class is included into #ComponentScan packages.
Also, the following won't compile:
#Service
public class CommentService(){
You don't need paranthesis to declare a class, it should be:
#Service
public class CommentService{

MockBean annotation in Spring Boot test causes NoUniqueBeanDefinitionException

I am having trouble using the #MockBean annotation. The docs say MockBean can replace a bean within the context, but I am getting a NoUniqueBeanDefinitionException within my unit test. I can't see how to use the annotation. If I can mock the repo, then obviously there will be more than one bean definition.
I am following the examples found here: https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4
I have a mongo repository:
public interface MyMongoRepository extends MongoRepository<MyDTO, String>
{
MyDTO findById(String id);
}
And a Jersey resource:
#Component
#Path("/createMatch")
public class Create
{
#Context
UriInfo uriInfo;
#Autowired
private MyMongoRepository repository;
#POST
#Produces(MediaType.APPLICATION_JSON)
public Response createMatch(#Context HttpServletResponse response)
{
MyDTO match = new MyDTO();
match = repository.save(match);
URI matchUri = uriInfo.getBaseUriBuilder().path(String.format("/%s/details", match.getId())).build();
return Response.created(matchUri)
.entity(new MyResponseEntity(Response.Status.CREATED, match, "Match created: " + matchUri))
.build();
}
}
And a JUnit test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class TestMocks {
#Autowired
private TestRestTemplate restTemplate;
#MockBean
private MyMongoRepository mockRepo;
#Before
public void setup()
{
MockitoAnnotations.initMocks(this);
given(this.mockRepo.findById("1234")).willReturn(
new MyDTO());
}
#Test
public void test()
{
this.restTemplate.getForEntity("/1234/details", MyResponseEntity.class);
}
}
Error message:
Field repository in path.to.my.resources.Create required a single bean, but 2 were found:
- myMongoRepository: defined in null
- path.to.my.MyMongoRepository#0: defined by method 'createMock' in null
It's a bug: https://github.com/spring-projects/spring-boot/issues/6541
The fix is in spring-data 1.0.2-SNAPSHOT and 2.0.3-SNAPSHOT : https://github.com/arangodb/spring-data/issues/14#issuecomment-374141173
If you aren't using these version, you can work around it by declaring the mock with its name:
#MockBean(name="myMongoRepository")
private MyMongoRepository repository;
In response to your comment
From Spring's doc:
For convenience, tests that need to make REST calls to the started
server can additionally #Autowire a TestRestTemplate which will
resolve relative links to the running server.
Reading this, I think you need to declare #SpringBootTest with a web environment:
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
If your spring boot doesn't start the web environment, then what is the need for TestRestTemplate. Thus, I guess spring does not even make it available.
I had the same "issue" in spring-boot 2.3.9 but it's not a bug, it's problem with the configuration of beans.
At least, There are two ways to solve it:
Set name parameter in #MockBean annotation:
In the test, add a name to MockBean:
#MockBean(name="myRepository")
private MyRepository diffrentName;
and in the production codebase use myRepository as filed name :
#Autowired
private MyRepository myRepository;
The name of #MockBean must be the same as the name of the field.
Name a MockBean filed the same as a dependency in code.
In the test, use the correct name of MockBean filed:
#MockBean
private MyRepository customRepository;
and in the production codebase use customRepository as filed name :
#Autowired
private MyRepository customRepository;
in that way, You indicate which bean You want to use.
I hope this will be helpful for someone.
Just add below in POM.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

Why here we annotate class with #Autowire

I came across this code
#Singleton
#Controller
#Autowire(mode = AutowireMode.BY_NAME)
#Path("/")
public class RootResource {
}
I have seen #Autowire on fields,
It means autowiring by type, and class with this field will get bean with particular type.
But in above code I am not sure who is using this RootResource bean?
This is Spring-jersey Rest project.
What I understand is spring will create bean of RootResource and Some class will use this bean to set its property. (I cant see any explicit configuration for this bean)
My question is,
1) Who is this class?
2) Here Autowiring by name is done, Can I replace #Autowired with #Resource ?
The use of #Autowire in this case is to instruct the Spring container to inject dependencies into RootResource by using beans with names matching property names in RootResource.
This is similar to that autowire attribute of the bean element using XML configuration. Assuming RootResource has
#Singleton
#Controller
#Autowire(mode = AutowireMode.BY_NAME)
#Path("/")
public class RootResource{
private SomeService someService;
private AnotherService anotherService;
public void setSomeService(SomeService someService){
this.someService = someService;
}
public void setAnotherService(AnotherService anotherService){
this.anotherService = anotherService;
}
}
The container will attempt to find beans named someService and anotherService and will try to set the corresponding properties. Take note you don`t require any dependency inject annotations the property or field level.
You can use #Resource / #Autowired to achieve the same thing. However you have to annotate the fields or setters in this case. And also injection will fail if the dependency is not found in the container
#Singleton
#Controller
#Autowire(mode = AutowireMode.BY_NAME)
#Path("/")
public class RootResource{
private SomeService someService;
private AnotherService anotherService;
#Resource
public void setSomeService(SomeService someService){
this.someService = someService;
}
#Resource
public void setAnotherService(AnotherService anotherService){
this.anotherService = anotherService;
}
}
#Resource will use bean name and fall back to type matching whilst #Autowired always uses type matching
Also note that #Autowire and #Autowired have different behaviour. The RootResource bean does not need to be explictly configured in the application context. It will be auto detected by the component scanner as it has a stereotype annotation i.e #Controoler

Categories

Resources