I'm working on a Spring Boot 2.3 based application. I have a controller that currently has one autowired dependency:
public class ValidationController {
private ValidationService validationService;
#Autowired
public ValidationController(ValidationService validationService) {
this.validationService = validationService;
}
}
I have tests that run on this service and pass. I am attempting to add a second service to this controller:
public class ValidationController {
private ValidationService validationService;
private TokenService tokenService;
#Autowired
public ValidationController(ValidationService validationService, TokenService tokenService) {
this.validationService = validationService;
this.tokenService = tokenService;
}
}
When I do this, all of my ValidationControllerTest cases start failing. I found this message in the stack trace:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.auth.service.service.TokenService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
My TokenService interface is as follows:
public interface TokenService {
JsonWebToken getToken(User user, Account account);
}
The implementation is annotated with the #Service annotation:
#Service
public class TokenServiceImpl implements TokenService {
private final String url;
private final String username;
private final String password;
public TokenServiceImpl(
#Value("${service.url}") String url,
#Value("${service.username}") String username,
#Value("${service.password}") String password
) {
this.url = url;
this.username = username;
this.password = password;
}
#Override
public JsonWebToken getToken(User user, Account account) {
...
return jsonWebToken;
}
}
My ValidationService looks very similar to my TokenService as far as annotations, but for whatever reason, Spring Boot can find my ValidationService just fine but my TokenService, which lives in the same place, is nowhere to be found.
How do I make sure that Spring Boot can find my TokenService?
Thanks very much for the comments!
Can you double check that you bring up a spring application context in your tests?
It is my understanding that the #WebMvcTest annotation will load my application context, is that accurate? I have the following annotations on my ValidationControllerTest class:
#ExtendWith(SpringExtension.class)
#WebMvcTest(controllers = ValidationController.class)
Is this sufficient to load the application context in my tests?
Can you double check that this context does a component scan to the package where the TokenServiceImpl resides?
I am new to Spring Boot and still trying to grok component scanning. I have a #SpringBootApplication annotation on my main AuthServiceApplication class, which is in the com.example.auth.service package. I read about component scanning here:
If your other packages hierarchies are below your main app with the #SpringBootApplication annotation, you’re covered by implicit components scan.
My TokenServiceImpl class resides at com.example.auth.service.service.impl, which I think means that it is within the hierarchy of the main app, so it should be covered by the implicit component scan. Is that accurate?
Can you double check that ValidationService is not mocked (via #Mock annotation or something) somewhere in your test suite?
I do have the following in my ValidationServiceImplTest:
#MockBean
private TokenService tokenService;
It looks like I am not using it at the moment. I will remove this #MockBean and see if that does the trick.
Related
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
I am new at spring MVC framework and i am currently working in a web application that uses a session scoped bean to control some data flow.
I can access these beans in my application context using #Autowired annotation without any problem in the controllers. The problem comes when I use a class in service layer that does not have any request mapping (#RequestMapping, #GetMapping nor #PostMapping) annotation.
When I try to access the application context directly or using #Autowired or even the #Resource annotation the bean has a null value.
I have a configuration class as follow:
#Configuration
#EnableAspectJAutoProxy
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class, basePackages = "com.quantumx.nitididea.NITIDideaweb.repository")
public class AppConfig implements WebMvcConfigurer {
#Bean (name = "lastTemplate")
#SessionScope
public LastTemplate getlastTemplate() {
return new LastTemplate();
}
//Some extra code
}
The POJO class is defined as :
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I have a Test class that is annotated as service and does not have any request mapping annotated method:
//#Controller
#Service
public class Test {
// #Autowired
// private ApplicationContext context;
// #Autowired
#Resource(name = "lastTemplate")
public LastTemplate lastTemplate;
// #Autowired
// public void setLastTemplate(LastTemplate lastTemplate) {
// this.lastTemplate = lastTemplate;
// }
public Test() {
}
// #RequestMapping("/test")
public String testing() {
// TemplateForma last = (TemplateForma) context.getBean("lastInsertedTemplate");
// System.out.println(last);
System.out.println(lastTemplate);
// System.out.println(context.containsBean("lastTemplate"));
// System.out.println(context.getBean("lastTemplate"));
System.out.println("Testing complete");
return "Exit from testing method";
// return "/Messages/Success";
}
}
As you can see, there is a lot of commented code to show all the ways i have been trying to access my application context, using an Application context dependency, autowiring, declaring a resource and trying with a request mapping. The bean is null if no controller annotation and request mapping method is used and throws a java null pointer exception when I use the context getBean() methods.
Finally I just test my class in a controller that i have in my app:
#RequestMapping("/all")
public String showAll(Model model) {
Test test = new Test();
test.testing();
return "/Administrator/test";
}
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked. How can access my application context in a service class without mapping a request via controller?
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked
It should have worked in this case.
How can access my application context in a service class without mapping a request via controller?
Try one of these :-
#Autowired private ApplicationContext appContext;
OR
Implement ApplicationContextAware interface in the class where you want to access it.
Edit:
If you still want to access ApplicationContext from non spring managed class. Here is the link to article which shows how it can be achieved.
This page gives an example to get spring application context object with in non spring managed classes as well
What worked for me is that session scoped bean had to be removed in the application configuration declaration and moved to the POJO definition as follows:
#Component
#SessionScope
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I just call the bean using #Autowired annotation.
I'm trying to write a test in a Spring-Boot project. My problem is that I can't use my service that includes a constructor injection.
Depending on what I try I get errors like java.lang.IllegalStateException: Failed to load ApplicationContexts or NullPointerExceptions.
My first try was to change the constructor injection in my service to a field injection. But after reading this post I decided to change it back to the previous way.
Then I searched for examples but couldn't find something that was helpful.
Following are the relevant code snippets. If more code is needed I would provide it.
The service class with the constructor injection:
PlayerServiceImpl.java
#Service
public class PlayerServiceImpl implements PlayerService {
private PlayerRepository playerRepository;
private CompanyService companyService;
private CompanyResourceService companyResourceService;
#Autowired
public PlayerServiceImpl(PlayerRepository thePlayerRepository, CompanyService theCompanyService,
CompanyResourceService theCompanyResourceService) {
this.playerRepository = thePlayerRepository;
this.companyService = theCompanyService;
this.companyResourceService = theCompanyResourceService;
}
...
}
The test class im trying to create:
PlayerServiceImplIntegrationTest.java
#RunWith(SpringRunner.class)
#SpringBootTest
public class PlayerServiceImplIntegrationTest {
#TestConfiguration
static class PlayerServiceImplTestContextConfiguration {
private PlayerRepository playerRepository;
private CompanyService companyService;
private CompanyResourceService companyResourceService;
#Bean
public PlayerService playerService() {
return new PlayerServiceImpl(playerRepository, companyService, companyResourceService);
}
}
#Autowired
private PlayerService playerService;
#MockBean
private PlayerRepository playerRepository;
#Before
public void setUp() {
Player max = new Player("MaxMustang", "test123", "MaxMustang",
"max.mustang#test.com", new Date(System.currentTimeMillis()), 1, 0,
new BigDecimal("0.00"), new BigDecimal("0.00"), 0, 0);
Mockito.when(playerRepository.findByUserName(max.getUserName()))
.thenReturn(max);
}
#Test
public void whenFindById_thenReturnPlayer() {
String userName = "MaxMustang";
Player found = playerService.findByUserName(userName);
assertThat(found.getUserName()).isEqualTo(userName);
}
}
In my test, I'm trying to create a player object and receive it. It's just my first test in Spring Boot. And my main goal was to just get the test running.
And the original test is from Baeldung from "5. Mocking with #MockBean". But while experimenting around, I added or changed a few things.
If I missed a post pointing at the same problem I would be glad to be informed about that.
Also, I would appreciate it if someone can tell me if the arguments in the constructor of my service are too much or still in an "ok" range.
You have to make the configuration bean primary and also use constructor injection on that method:
#TestConfiguration
static class PlayerServiceImplTestContextConfiguration {
#Bean
#Primary
public PlayerService playerService(PlayerRepository playerRepository,
CompanyService companyService, CompanyResourceService companyResourceService) {
return new PlayerServiceImpl(playerRepository, companyService, companyResourceService);
}
}
Without primary you will have two beans of same type floating around and you dont use #Qualifier here. Also you cannot #Autowire beans in a configuration class thats why you need to use constructor injection.
My unit tests fail after I added #Service to the controller.
The project is Spring-boot v 2.0.1.RELEASE. I've spent many hours trying to find the answer but no luck. The test worked before I added #Service annotation and my service class has a repository in it.
Stack trace:
2018-04-24 12:57:12.487 WARN 940 --- [ main]
o.s.w.c.s.GenericWebApplicationContext : Exception encountered
during context initialization - cancelling refresh attempt:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'fleetController': Unsatisfied
dependency expressed through field 'service'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'uk.co.vw.lead.service.ContactUsService'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Controller:
#Slf4j
#RestController
#RequestMapping(value = VERSION, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public class FleetController {
public static final String VERSION = "1.0";
#Autowired
private ContactUsService service;
#InitBinder
public void initBinder(final WebDataBinder webDataBinder) {
webDataBinder.registerCustomEditor(NatureOfEnquiryEnum.class, new NatureOfEnquiryEnumConverter());
webDataBinder.registerCustomEditor(FleetSizeEnum.class, new FleetSizeEnumConverter());
}
#PostMapping(value = "/fleet/contact-us")
public ResponseEntity contactUs(#Valid ContactUsDTO formDTO) {
service.createForm(formDTO);
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
#PostMapping(value = "/fleet/request-demo")
public ResponseEntity requestDemo(#Valid RequestDemoDTO demoDTO) {
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
Service:
#Service
public class ContactUsServiceImpl implements ContactUsService {
#Autowired
private FleetRepository repository;
#Override
public void createForm(ContactUsDTO formDTO) {
ContactUsForm form = populateContactUsForm(formDTO);
repository.save(form);
}
}
Test class:
#RunWith(JUnitPlatform.class)
#WebMvcTest(FleetController.class)
#ExtendWith(SpringExtension.class)
public class FleetControllerTest {
private final String CONTACT_US_URL = "/fleet/contact-us";
#Autowired
private MockMvc mockMvc;
#MockBean
private FleetRepository repository;
#Autowired
private ContactUsService service;
#Test
public void contactUsSuccessTest() throws Exception {
this.mockMvc.perform( post("/" + VERSION + CONTACT_US_URL)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("firstname", "John")
.param("lastname", "Doe")
.param("company", "Skynet")
.param("emailAddress", "john.connor#sky.net")
.param("telephone", "020 8759 4294")
.param("natureOfEnquiry", "new")
.param("comments", "some comments")
.param("recaptchaResponse", "success"))
.andExpect(status().isNoContent());
}
#Test
public void contactUsMissingRequiredFieldsTest() throws Exception {
this.mockMvc.perform( post("/1.0/fleet/contact-us")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE))
.andExpect(status().isBadRequest());
}
}
Please help as I have no idea what is going on.
Test classed annotated with #WebMvcTest are tests that focus only on the Spring MVC components : controllers.
So the service field declared in your unit test could not be autowired:
#Autowired
private ContactUsService service;
So you should mock also this dependency :
#MockBean
private ContactUsService service;
Note also that as FleetController doesn't have any direct dependency on FleetRepository, mocking this bean is not required :
#MockBean
private FleetRepository repository;
It is even worse as it adds a mock in the context that could create side effect during your test.
You have to mock only the direct dependencies of the controller under test.
As alternative if you want to mock only some beans and not all which are not controllers, don't use #WebMvcTest and instead of use #SpringBootTest that will load the whole context.
Then declare in the test class, the class(es) you want to mock with #MockBean.
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