Занятие №7
Spring Boot
Соколов
Никита
Давайте напишем программу!
Чак Норрис настолько крут
Что про него напишут Java-приложение ...
"Шутки"
Грузит шутки про Чака Норриса |
Сохраняет их в базу данных |
Как много нужно сделать?
Указать все нужные зависимости |
Создать нужные бины |
Разработать интерфейс |
Подключиться к базе данных |
Настроить библиотеку логирования |
А может это сделает кто-нибудь другой?
Spring Boot
Появление микросервисов потребовало ускорить создание проектов на Spring
Так появился Spring Boot 1.0
в апреле 2014
В чем отличие от Spring ( Core ) ?
А для чего вообще нужен Spring ?
Меньше шаблонного инфраструктурного кода в сравнении с программой просто на Java
В чем отличие Spring Boot от Spring ?
Меньше инфраструктурного кода Spring !
Концентрация на бизнес-логике, а не на конфигурации приложения!
Схема работы с приложением
Чак Норрис
настолько крут ...
Хочу шутку!
Случайная
Шутка
Случайная
Шутка
Случайная
Шутка
Получить шутку
Получить шутку
Чак одобряет!
Сохранить!
Сохранить последнюю шутку
Интерфейс
Сервис
API
Шаги
Инициализация проекта |
Сервис получения шуток |
Интерфейс пользователя |
Сервис работы с данными |
Логирование |
Инициализация проекта
Spring Starters (группы библиотек)
Добавить сервис получения шуток
Создать интерфейс JokeService |
Создать имплементацию с помощью RestTemplate |
Проверить! |
Интерфейс - получение шуток
public interface JokeService {
String getJoke();
}
Использовать RestTemplate
@Service
public class JokeServiceRestImpl implements JokeService {
private final RestTemplate restTemplate;
public JokeServiceRestImpl(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
public class JokesApplication {
// ...
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Добавить тест на получение шутки
@SpringBootTest
public class JokeServiceRestImplTest {
@Autowired
JokeService jokeService;
@Test
public void joke() {
String jokeText = jokeService.getJoke();
Assert.assertFalse(jokeText.isEmpty());
boolean jokeContainsChuckName =
jokeText.contains("Chuck Norris");
Assert.assertTrue(jokeContainsChuckName);
}
}
Где смеяться ?
> { "type": "success", "value": { "id": 31, "joke": "Chuck Norris has two speeds: Walk and Kill.", "categories": [] } }
Добавить транспортный объект
public class JokeDTO {
private ValueDTO value;
public ValueDTO getValue() {
return value;
}
}
public class ValueDTO {
private String joke;
public String getJoke() {
return joke;
}
}
> { "type": "success", "value": { "id": 31, "joke": "Chuck Norris has two speeds: Walk and Kill.", "categories": [] } }
Использовать DTO
@Service
public class JokeServiceRestImpl implements JokeService {
...
@Override
public String getJoke() {
ResponseEntity<Joke> responseEntity =
restTemplate.getForEntity(URL, Joke.class);
return responseEntity.getBody().getValue().getJoke();
}
}
> Chuck Norris crossed the road. No one has ever dared question his motives.
My name is shell. Spring shell.
Давайте общаться с пользователем через интерфейс командной строки.
Проект Spring Shell - позволяет вам уйти от обработки ввода и работать только с командами.
!
Подключить зависимость
dependencies {
// ...
compile
group: 'org.springframework.shell',
name: 'spring-shell-starter',
// ...
}
Добавить свои команды
@ShellComponent
public class ShellCommands {
private final JokeService jokeService;
private String lastJoke;
@ShellMethod("Get joke about Chuck Norris.")
public String joke() {
lastJoke = jokeService.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();
}
Правила выбора
- Пользовательские бины важнее автоматики
java -jar jokes.jar --debug
Если ваш бин не создается, то вы можете запустить Spring в режиме debug и получить отчет об автоконфигурации
- Если есть зависимость в classpath, то Spring Boot попробует автоматически создать бин
Либо указав в настройках запуска
Для Idea community edition
Пример отчета
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
CodecsAutoConfiguration.JacksonCodecConfiguration matched:
- @ConditionalOnClass found required class
'com.fasterxml.jackson.databind.ObjectMapper'
(OnClassCondition)
Работа с данными
Создать интерфейс JokeDataService |
Создать имплементацию с помощью JdbcTemplate |
Добавить команды в shell |
Работа с данными
@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 IF NOT EXISTS jokes (joke text)");
}
Как запустить код создания таблицы до старта приложения?
Появилось хранение в БД |
Есть команды в shell |
Все работает |
Данные стираются при перезапуске |
Как сохранить?
Давайте сделаем еще лучше!
Добавить хранение в файле |
Воспользоваться консолью h2 |
Добавить логирование |
Хранение в файле
# путь к файлу
spring.datasource.url=jdbc:h2:file:./data/jokes
spring.datasource.driverClassName=org.h2.Driver
# логин
spring.datasource.username=sa
# пароль
spring.datasource.password=sa
# веб консоль
spring.h2.console.enabled=true
h2 консоль
Адрес консоли по умолчанию: http://localhost:8080/h2-console
Добавить логирование!
Объявить логгер в классе
Добавить настройки в приложении
logging.level.root = WARN
logging.level.pro.sisit.jokes = DEBUG
logging.file = jokes.log
public class RestJokeRetriever {
/// ...
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);
return lastJoke;
}
Спасибо за внимание!
JavaSIS#20 unit 7 spring boot
By nikitamugen
JavaSIS#20 unit 7 spring boot
- 319