@Autowired is not working, and we don't know why. The service and controller are in different modules and packages, but we assume this does not matter. We can manually instantiate the service, so the controller module is able to "see" the common module.
Running the application results in the following error:
Parameter 0 of constructor in com.xx.campaign_api.controller.MyController required a bean of type 'com.xx.campaign.common.service.MyService' that could not be found.
We have multi-module spring boot 3.4.2 project with following pom:
<project xxx
<groupId>org.springframework</groupId>
<artifactId>our-service</artifactId>
<version>0.1.0</version>
<packaging>pom</packaging>
<modules>
<module>campaign-common</module>
<module>campaign-api</module>
<module>campaign-schedule</module>
</modules>
</project>
in the api module we have a rest controller like this:
package com.xx.campaign_api.controller;
import com.xx.campaign.common.service.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
private MyService testService;
@Autowired
public MyController(MyService testService) { // THIS LINE IS FAILING
this.testService = testService;
}
@GetMapping("/test")
public String test() {
return testService.test();
}
}
The service looks like this:
package com.xxx.campaign.common.service;
import org.springframework.stereotype.Service;
@Service
public class MyServiceImpl implements MyService {
@Override
public String test() {
return "test3";
}
}
package com.xxx.campaign.common.service;
public interface MyService {
public String test();
}
The main class looks like this:
package com.xx.campaign_api;
@SpringBootApplication
public class CampaignApiApplication {
public static void main(String[] args) {
SpringApplication.run(CampaignApiApplication.class, args);
}
}
in the pom of the controller module, we have:
<dependency>
<groupId>xx</groupId>
<artifactId>campaign-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
I also tried this:
package com.xx.campaign_api.controller
@SpringBootApplication(scanBasePackages = "com.xx")
@RestController
public class GaController {
private MyService myService;
@Autowired
public GaController(MyService myService) { // THIS LINE IS FAILING
this.myService = myService;
}
But this app wont start with this error:
Web application could not be started as there was no org.springframework.boot.web.servlet.server.ServletWebServerFactory bean defined in the context.
The problem is your generic service and your API have different packages and don't really share a common super package (well they do in the form of com.xx
).
Next don't put multiple @SpringBootApplication
annotations in your code that only will result in errors. So remove the @SpringBootApplication
from the @RestController
class.
You can do 3 things to fix this.
@SpringBootApplication
annotated class (the one with the main
into com.xx
and start it from there.scanBasePackages
to your @SpringBootApplication
, while this work for detecting components if you have things like entities etc. in there it won't consider those.@SpringBootApplication
to a top-level shared package.@SpringBootApplication
annotated classpackage com.xx;
@SpringBootApplication
public class CampaignApiApplication {
public static void main(String[] args) {
SpringApplication.run(CampaignApiApplication.class, args);
}
}
While this will probably work, you might run the risk of scanning too much, depending of the name of the xx
.
scanBasePackages
to the @SpringBootApplication
annotationpackage com.xx.campaign_api;
@SpringBootApplication(scanBasePackages={
"com.xx.campaign", "com.xx.campaign_api"
})
public class CampaignApiApplication {
public static void main(String[] args) {
SpringApplication.run(CampaignApiApplication.class, args);
}
}
While this will work for detecting components like services, repositories etc. If you have things like entities or Spring Data repositories in those packages that won't work. To make that work you would have to add additional annotations like @EntityScan
and @EnableJpaRepositories
(or whatever persistence technology you use). Adding all those annotations is cumbersome and can lead to surprises as parts of auto configuration not being auto config anymore.
@SpringBootApplication
class.package com.xx.campaign;
@SpringBootApplication
public class CampaignApiApplication {
public static void main(String[] args) {
SpringApplication.run(CampaignApiApplication.class, args);
}
}
Now I would suggest to do this and rename your com.xx.campaign_api
package to com.xx.campaign.api
. This would make it a sub package of com.xx.campaign
. Now when placing the CampaignApiApplication
in the com.xx.campaign
package it will automatically detect everything in com.xx.campaign
and all of its sub-packages. Without adding any additional annotations.
This last one is also the recommended one from the Spring Boot developers and mentioned in the Spring Boot reference guide.