简体   繁体   中英

How to run @SpringBootApplication without controller

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());
    }
}

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");
}

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.

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 :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM