Spring : how this field in controller is injected? - java

I have a rest controller like this :
#Slf4j
#RestController
#RequestMapping(...)
public class MyController {
private MyService service;
public MyController(MyService service){
this.service = service;
}
And the service class is a component :
#Component
public class MyService{
...
}
And when I run the program, the service field is correctly injected. But how is it injected (there is no autowired annotation neither on filed ni on constructor) ?.
I am using SpringBoot 2.0.

From Spring 4.3 release. According to the documentation (https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-spring-beans-and-dependency-injection), if a bean has single constructor, #Autowired annotation can be omitted.
If a bean has one constructor, you can omit the #Autowired, as shown in the following example:
#Service
public class DatabaseAccountService implements AccountService {
private final RiskAssessor riskAssessor;
public DatabaseAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
}
// ...
}

Related

How to handle an error when a bean does not exist and could not inject in Spring-boot?

I have a standard rest-api spring-boot application.
Controller with injected service
#RestController
#RequestMapping("/foo")
#AllArgsConstructor
public class SomeController {
private final SomeService someService;
public void someMethod(){
someService.toDoSomething();
}
}
and service with injected other beans
#Setter
public class SomeService {
private AnotherVeryImportantBean anotherVeryImportantBean;
private RestTemplateBuilder restTemplate;
public void toDoSomething() {
anotherVeryImportantBean.someAction();
}
In my case, the bean AnotherVeryImportantBean is created in another dependency, which I connect to the spring-boot application. Whether to create a bean or not is decided by a variable in the application.yml file.
like this:
another.service.enabled: true
Of course I have config class for service
#Configuration
#RequiredArgsConstructor
public class SomeConfig {
private final AnotherVeryImportantBean anotherVeryImportantBean;
#Bean
public SomeService someService(RestTemplateBuilder restTemplate) {
SomeService foo = new SomeService();
foo.setAnotherVeryImportantBean(anotherVeryImportantBean);
foo.setRestTemplate(restTemplate);
return foo;
}
The problem is that this controller and service are not the only ones in the application. I would like the application not to crash completely if this particular controller and service are not formed. If the bean is not created for some reason, I just don't use that functionality (this controller).
At this point, the application crashes because AnotherService cannot be injected into someService (In case where, for some reason, it was not created).
I tried adding an annotation to config class
#ConditionalOnBean(AnotherVeryImportantBean.class)
like this:
#ConditionalOnBean(AnotherVeryImportantBean.class)
#Configuration
#RequiredArgsConstructor
public class SomeConfig {
private AnotherVeryImportantBean anotherVeryImportantBean;
#Bean
public SomeService someService(RestTemplateBuilder restTemplate) {
SomeService foo = new SomeService();
foo.setAnotherVeryImportantBean(anotherVeryImportantBean);
foo.setRestTemplate(restTemplate);
return foo;
}
But the problem is that conditional in SomeConfig checks if the bean is in the container before it is created.
How can I handle an error when a bean cannot inject another dependency into itself?

How to mock a service and all the autowired fields in the service?

I am looking to Mock my service and all the autowired fields in it. Below is the service:
#Service
public class MyServiceImpl implements MyService {
#Autowired
#Qualifier("propvalues")
Map<String,String> propvalues;
...
...
}
Below is the Configuration class
#Configuration
public class MyValuesConfig {
#Bean(name = "propvalues")
#ConfigurationProperties(prefix = "mysvcvalues")
private Map<String,String> propvalues;
}
The configuration class reads from application-myvalues.yml
This is my Test class MyServiceImplTest
#ExtendWith(MockitoExtension.class)
public class MyServiceImplTest {
#Mock
private MyService myService;
...
...
}
Now I find that the autowired fields in the mock service is null. Ideally I would want to see the values loaded from the configuration in the Map in the mock service.
Instead of #Mock user #SpyBean
#SpyBean inject object like an actual bean but you can mock any of its parts separately.
Or you can use spy to initialize the object and mock the other parts that you want.
Something like:
Mokito.spy(myService).when(....).thenReturn(...)

How can I use a JpaRepository, with #Autowired, inside a service class?

I've already read these questions and none of them worked:
Spring boot MVC - Unable to Autowire Repository in the service class
Why can't #Autowired a JPA repository - Spring boot + JPA
JpaRepository getting Null at service class
And also this one: https://www.baeldung.com/spring-autowired-field-null
Unfortunately, none of them worked.
What I have is:
Service interface:
#Service
public interface DayTradeService {
public List<DayTrade> getDayTrades(List<NotaDeCorretagem> corretagens);
}
Service Implementation:
public class DayTradeServiceImpl implements DayTradeService {
#Autowired
private DayTradeRepository dayTradeRepository;
#Override
public List<DayTrade> getDayTrades(List<NotaDeCorretagem> corretagens) {
// Several lines of code and some of them is trying to use dayTradeRepository.
}
}
My DayTradeRepository:
#Repository
public interface DayTradeRepository extends JpaRepository<DayTrade, Integer> {}
Inside my DayTradeController (annotated with #Controller), I can use a dayTradeRepository with #Autowired. But inside a service class, I cannot use. I get this message:
Cannot invoke "meca.irpf.Repositories.DayTradeRepository.getDayTrades()" because "this.dayTradeRepository" is null"
How can I make it possible?
EDIT after I accepted Nikita's answer:
I didn't post the Controller code, but it didn't have the #Autowired for the service class DayTradeServiceImpl. That was the point I was missing. After Nikita pointing that, I could solve the problem.
You not need create new object. You have to call like this:
#Controller
#RequestMapping("/test")
public class TestController {
#Autowired
private DayTradeServiceImpl dayTradeService;
#GetMapping(value = "/get")
public void getTrades() {
dayTradeService.getDayTrades(...);
}
}
And set annotation #Service for DayTradeServiceImpl.
#Service
public class DayTradeServiceImpl implements DayTradeService {
#Autowired
private DayTradeRepository dayTradeRepository;
#Override
public List<DayTrade> getDayTrades(List<NotaDeCorretagem> corretagens) {
// Several lines of code and some of them is trying to use dayTradeRepository.
}
}
Spring framework use inversion of control, which has container for beans. For detect beans use annotation like: #Service, #Component, #Repository.

Spring Boot application.properties custom variable in a non-controller class

How come application.properties will work in a RestController, but not in a service class?
//application.properties
test=test
Works Perfect!
#RestController
public class invitecontroller {
#Autowired inviteconfig inviteconfig;
#PostMapping("/v1/invite")
public void invite(#RequestBody XXX XXX) {
System.out.println(inviteconfig);
}
}
Returns "Null"
#Service
public class inviteservice {
#Autowired inviteconfig inviteconfig;
public void invite() {
System.out.println(inviteconfig);
}
}
#Configuration
#Data
public class inviteconfig {
private String test;
}
The inviteservice class is not configured for Spring IoC (Inversion of Control) as a bean, so Spring will not handle the inviteservice class lifecycle. In this case, #Autowired is useless.
To fix this try to add #Component annotation to invitesevice, to declare it as a component:
#Component
public class inviteservice {
#Autowired inviteconfig inviteconfig;
public void invite() {
System.out.println(inviteconfig);
}
}
In the case of the controller, with #RestController, Spring will recognize your class as a Spring component.
Finally, don't forget to inject inviteservice using Spring IoC (using #Autowired annotation, or other means)
inviteservice class should be annotated with #Component or #Service
#Component
public class inviteservice {
...

How to instantiate a Class/Bean in Spring passing parameter in constructor

I need to Autowire service interface in my controller, passing parameter logcode in not default service constructor
#Controller
public class FooController {
private Foo foo;
#Autowired
private FooService fooService //(I like passe parameter here);
}
Here's my Service:
I need to Autowire service interface in my controller, passing parameter logcode in not default service constructor
#Service
public class FooServiceImpl implements FooService {
#Autowired
private FooDAO fooDAO;
public FooServiceImpl(String pLogCode)
{
}
#Transactional
public void addFoo(Foo foo) {
fooDAO.addFoo(foo);
}
}
Passing arguments when autowiring interfaces it's only possible in XML config. Simmilar question is here:
Spring autowire interface

Categories