Как запустить @SpringBootApplication без контроллера

Как запустить код из класса с аннотацией @SpringBootApplication. Я хочу запустить свой код без обращения к контроллеру и получить информацию с терминала, а не с веб-браузера. Я пытался вызвать WeatherService в @SpringBootApplication, но у меня не получилось запустить приложение с описанием

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

WeatherClientApplication требует запуска Bean WeatherService. Для запуска Bean WeatherService требуется Bean RestTemplate в WeatherClientApplication. Вы должны автоматически подключить службу и позволить Spring обрабатывать внедрение зависимостей, а не объявлять требования bean-компонента в конструкторе, чтобы обойти эту проблему.

Compass 22.01.2019 20:36
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
1
2 720
3

Ответы 3

Вы можете сделать это, используя основной метод и используя ApplicationContext. В этом подходе вам не нужен CommandLineRunner.

public static void main(String[] args) {
    ApplicationContext context = SpringApplication.run(WeatherClientApplication.class, args);
 WeatherService service = (WeatherService)context.getBean("weatherService");
  service. getTemperatureByCityName("cityname");
}

почему бы и нет, у вас есть ApplicationContext, зачем вам что-то запускать? @Стефан Николл

Deadpool 22.01.2019 21:06

Потому что есть другие способы избежать ручной игры с контекстом?

Stephane Nicoll 23.01.2019 10:15

1) Вам нужно внедрить CommandLineRunner и определить точку входа вашего приложения в методе public void run(String... args), определенном в этом интерфейсе.

2) Как сказал Spring, у вас есть цикл: разорвите его инъекцией вне конструктора.

Такие как :

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

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

Как правило, внедрение конструктора должно быть предпочтительнее внедрения поля или сеттера, но в вашем случае это приемлемо.

Я бы тоже не рекомендовал этого делать. Внедрение поля происходит сразу после создания экземпляра класса, и для WeatherService требуется bean-компонент, который фактически предоставляет тот же класс. Это, вероятно, также создаст цикл.

Stephane Nicoll 22.01.2019 21:04

Думаете, это случайно? Spring не предназначен для проверки зависимости, необходимой перед внедрением поля, и создания экземпляров bean-компонентов, необходимых до ?

davidxxx 22.01.2019 21:07

Да, он предназначен для проверки таких вещей, и я на самом деле немного удивлен, что он работает. Тот факт, что это работает, не является причиной плохого дизайна: если бы вы сами подключали что-то, вы бы не проектировали свой код таким образом, так зачем вам это, потому что «вы можете»? В конце дня этот код пытается инициализировать поле чем-то, что требуется для вызова метода.

Stephane Nicoll 23.01.2019 10:18

Вы создаете цикл, вводя службу в сам @SpringBootApplication. Внедрение конструктора означает, что ничего не может произойти, пока класс не будет построен, но эта служба будет создана позже.

Не используйте внедрение полей на вашем @SpringBootApplication, так как оно представляет собой корневой контекст. Ваш CommandLineRunner вводит RestTemplate, но вы его не используете. Если вы замените его на WeatherService и удалите инъекцию конструктора, все должно работать нормально.

Кстати, я рад, что вы нашли приложение погоды полезным :)

Работает отлично. Спасибо

user8116296 22.01.2019 21:16

Другие вопросы по теме