Занятие №7
Spring Boot
Кернер
Денис
Давайте напишем программу!
Приложение - "Шутки", которое может
- грузить шутки про Чака Норриса
- сохранять их в базу
Как много нужно сделать?
- Указать все нужные зависимости с версиями
- Настроить подключение к базе данных
- Создать все нужные бины
- Настроить библиотеку логирования
- Разработать интерфейс
А может это сделает кто-нибудь другой?
Spring Boot
Появление микросервисов потребовало ускорить создание проектов на Spring
Так появился Spring Boot 1.0 в апреле 2014
Схема обмена данными
Схема работы с приложением
Шаги
- Настройка проекта - скачать архив
- Добавить JokeRetriever
- Добавить тест
- Добавить Shell
- Добавить JokeDataService
- Добавить логирование
- Добавить настройки по файлам
- Добавить PostConstruct
- Показать консоль h2
Настройка проекта Spring Initializr
Spring Starters (группы библиотек)
Добавить получение шуток
- Создаем интерфейс JokeRetriever
- Создаем имплементацию с помощью RestTemplate
- Скачиваем результат как строку
- Потом создаем DTO
- Маппинг автоматически делает спринг!
Интерфейс - получение шуток
public interface JokeRetriever {
String getJoke();
}
Используем RestTemplate
@Service
public class RestJokeRetriever implements
JokeRetriever {
private final RestTemplate restTemplate;
public RestJokeRetriever(RestTemplate restTemplate)
{ this.restTemplate = restTemplate; }
@Override
public String getJoke() {
String url = "http://api.icndb.com/jokes/random";
ResponseEntity<String> response =
restTemplate.getForEntity(url, String.class);
return response.getBody();
}
}
Добавить бин RestTemplate (httpClient)
@SpringBootApplication
@Configuration
public class JokesApplication {
// ...
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Добавить тест на получение шутки
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JokesApplication.class)
public class RestJokeRetrieverTest {
@Autowired
JokeRetriever jokeRetriever;
@Test
public void joke() {
String jokeText = jokeRetriever.getJoke()
.value.getJoke();
Assert.assertFalse(jokeText.isEmpty());
Assert.assertTrue(jokeText.contains(
"Chuck Norris"));
}
}
My name is shell. Spring shell.
Давайте общаться с пользователем через интерфейс командной строки.
Проект Spring Shell - позволяет вам уйти от обработки ввода и работать только с командами.
Подключаем зависимость
dependencies {
// ...
implementation
'org.springframework.shell:spring-shell-starter:2.0.0.RELEASE'
// ...
}
Пишем свои команды
@ShellComponent
public class ShellCommands {
private final JokeRetriever jokeService;
private String lastJoke;
@ShellMethod("Get joke about Chuck Norris.")
public String joke() {
lastJoke = jokeService.getJoke().value.getJoke();
return lastJoke;
}
}
Результат первой фазы
shell:>joke
Chuck Norris hosting is 101% uptime guaranteed.
shell:>joke
Product Owners never ask Chuck Norris for more features.
They ask for mercy.
Все отлично? Запустим тесты
Warning
Тест не запускается. Из-за Shell. Как исправить?
@RunWith(SpringRunner.class)
@SpringBootTest(
classes = JokesApplication.class,
properties = {
InteractiveShellApplicationRunner
.SPRING_SHELL_INTERACTIVE_ENABLED + "=false",
ScriptShellApplicationRunner
.SPRING_SHELL_SCRIPT_ENABLED + "=false"
}
)
public class RestJokeRetrieverTest {
Что дальше?
А как хранить?
Как посмотреть все?
Добавить работу с данными
Добавим интерфейс JokeDataService.
Как реализовать?
- Подключить dependency
- Использовать JdbcTemplate
Все остальное сделает Spring Boot
Как он это делает?
- @SpringBootApplication
- @EnableAutoConfiguration
- @Import(EnableAutoConfigurationImportSelector.class)
- spring.factories
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
@Configuration
@ConditionalOnClass(ObjectMapper.class)
public class JacksonAutoConfiguration {
@Bean
public JsonComponentModule jsonComponentModule() {
return new JsonComponentModule();
}
Правила выбора
- Пользовательские бины важнее автоматики
- Если есть зависимость в classpath, то Spring Boot попробует автоматически создать бин
- Если ваш бин не создается, то вы можете запустить Spring в режиме debug и получить отчет об автоконфигурации
java -jar jokes.jar --debug
Либо указав в настройках запуска
Для Idea community edition
Пример отчета
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
CodecsAutoConfiguration.JacksonCodecConfiguration matched:
- @ConditionalOnClass found required class
'com.fasterxml.jackson.databind.ObjectMapper' (OnClassCondition)
Работа с данными
@Override
public void save(String jokeText) {
jdbcTemplate.update(
"insert into jokes (joke) values (?)"
, jokeText);
}
@Override
public List<String> getAll() {
return jdbcTemplate.query(
"select joke from jokes",
(rs, rowNum) -> rs.getString("joke"));
}
А как создать схему?
jdbcTemplate может создать таблицу.
Как нам запустить код создания таблицы до старта приложения?
@PostConstruct
public void onPostConstruct() {
jdbcTemplate.update(
"create table jokes (joke text);");
}
Добавляем хранение и команды в shell
Реализуем хранение
Добавляем команды в spring shell
Проверяем, что работает просмотр команд
Данные стираются при перезапуске
Как сохранить?
Давайте сделаем еще лучше!
- Добавим хранение в файле
- Давайте использовать консоль h2!
- http://localhost:8080/h2-console
- Добавим логирование
Хранение в файле
# путь к файлу
spring.datasource.url=jdbc:h2:file:./data/jokes
spring.datasource.driverClassName=org.h2.Driver
# логин
spring.datasource.username=sa
# пароль
spring.datasource.password=
# веб консоль
spring.h2.console.enabled=true
h2 консоль
Добавим логирование!
Нужно в классе с логированием объявить логгер
Нужно добавить настройки в приложении
logging.level.root=WARN
logging.level.io.github.bael.jokes=DEBUG
logging.file=jokes.log
private final Logger logger =
LoggerFactory.getLogger(RestJokeRetriever.class);
Пишем в лог!
logger.debug("Result: {}", response.getBody());
Итоги
Как сделать лучше?
- RestAPI (Spring Web)
- JPA и Spring Data (ORM)
- LiquiBase
- PostgreSql
- SpringBoot Actuator
Как вам Spring Boot?
А домашнее задание?
Домашнее задание
- Сделайте сервис который возвращает погоду по названию города. Командный интерфейс принимает название города как параметр.
- API URL: https://community-open-weather-map.p.rapidapi.com/weather?units=metric&mode=json&q=Красноярск"; (Ключ для апи вышлем в чат)
- Сохраняйте в базу результат строкой: дата + город + температура: 27.08.2019 Krasnoyarsk +25
Рекомендации формирования запроса
Конкретные заголовки мы скинем в чат
// используйте метод exchange RestTemplate.
// на входу ему нужна RequestEntity, которую можно создать
// указав в конструкторе HTTP заголовки, HTTP метод и URI
// Заголовки создайте так:
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add(HEADER_NAME, HEADER_VALUE);
// URI
String url = "http://example.com";
URI.create(url);
Работа с CLI - пример параметра команды
@ShellMethod("Get joke about Chuck Norris.")
public String joke(
@ShellOption(defaultValue = "NERDY")
String category) {
lastJoke = jokeService.getJoke(category).value.getJoke();
return lastJoke;
}
Спасибо за внимание!
JavaSIS#19 unit 7 spring boot
By Dennis Kerner
JavaSIS#19 unit 7 spring boot
- 400