1

How to run code from class with @SpringBootApplication annotation. I want to run my code without calling to controller and get info from terminal not web browser. I tried to call weatherService in @SpringBootApplication but I've got a application failed start with description

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  weatherClientApplication
↑     ↓
|  weatherService defined in file [C:\Users\xxx\IdeaProjects\weatherclient\target\classes\com\xxx\restapiclient\service\WeatherService.class]
└─────┘
@SpringBootApplication
public class WeatherClientApplication {

    private WeatherService weatherService;

    public WeatherClientApplication(WeatherService weatherService) {
        this.weatherService = weatherService;
    }

    private static final Logger log = LoggerFactory.getLogger(WeatherClientApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(WeatherClientApplication.class, args);
    }

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder){
        return builder.build();
    }


    @Bean
    public CommandLineRunner run(RestTemplate restTemplate) throws Exception {
        return args -> {
            log.info(weatherService.getTemperatureByCityName("Krakow"));
        };
    }

}
@Service
public class WeatherService {

    private RestTemplate restTemplate;

    public WeatherService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public String getTemperatureByCityName(String cityName) {
        String url = "http://api.openweathermap.org/data/2.5/weather?q=" + cityName + "&APPID=" + API_KEY + "&units=metric";
        Quote quote = restTemplate.getForObject(url, Quote.class);
        return String.valueOf(quote.getMain().getTemp());
    }
}
2
  • WeatherClientApplication requires Bean WeatherService to start. Bean WeatherService requires Bean RestTemplate within WeatherClientApplication to start. You should be autowiring the service and letting Spring handle the dependency injection rather than declaring bean requirements in the constructor to get around this issue. Commented Jan 22, 2019 at 19:36
  • If anyone comes here like me when searching info about CommandLineRunner and circular dependencies, just know that I've figured it's a bad idea to make your @ Configuration or @ SpringBootApplication class directly implement CommandLineRunner . Either create a @ bean from another class implementing CommandLineRunner or create an internal class, but DO NOT let your ApplicationContext class implement CommandLineRunner. Commented Mar 11, 2022 at 14:09

3 Answers 3

2

You can do this by using main method and by using ApplicationContext, In this approach you don't need any CommandLineRunner

public static void main(String[] args) {
    ApplicationContext context = SpringApplication.run(WeatherClientApplication.class, args);
 WeatherService service = (WeatherService)context.getBean("weatherService");
  service. getTemperatureByCityName("cityname");
}
Sign up to request clarification or add additional context in comments.

2 Comments

why not, you have ApplicationContext in hand, why do you need something to run ? @StephaneNicoll
Because there are other ways that avoid you having to play manually with the context?
0

1) What you want is implementing  CommandLineRunner and define the entry point of your application in the public void run(String... args) method defined in this interface.

2) As said by Spring you have a cycle : break it with a injection outside the constructor.

Such as :

@SpringBootApplication
public class WeatherClientApplication implements CommandLineRunner{
    @Autowired
    private WeatherService weatherService;

    //...
    @Override
    public void run(String... args) {    
       log.info(weatherService.getTemperatureByCityName("Krakow"));    
    }        
    //...
}

Generally constructor injection should be favored over field or setter injection but in your case, that is acceptable.

3 Comments

I wouldn't recommend doing that either. Field injection happens right after the class has been instantiated and the WeatherService requires a bean that the same class actually provides. This is likely to create a cycle as well.
Do you think that it is random ? Spring is not designed to check the dependency required before field injection and instantiating the beans required before ?
Yes it is designed to check that sort of things and I am actually a bit surprised it works. The fact it works is no reason for poor design: If you were wiring things yourself, you wouldn't design your code like this so why would you because "you can"? At the end of the day this code tries to initialize a field with something that a method invocation requires
0

You are creating a cycle as you are injecting a service in the @SpringBootApplication itself. Constructor injection means that nothing can really happen until the class is built but that service is going to be created later on.

Don't use field injection on your @SpringBootApplication as it represents the root context. Your CommandLineRunner injects a RestTemplate but you are not using it. If you replace that by the WeatherService and remove the constructor injection, things should work just fine.

I am glad you find the weather application useful by the way :)

1 Comment

Works fine. Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.