Соколов
Никита
Чак Норрис настолько крут
Что про него напишут Java-приложение ...
"Шутки"
Грузит шутки про Чака Норриса |
Сохраняет их в базу данных |
Указать все нужные зависимости |
Создать нужные бины |
Разработать интерфейс |
Подключиться к базе данных |
Настроить библиотеку логирования |
Появление микросервисов потребовало ускорить создание проектов на Spring
Так появился Spring Boot 1.0
в апреле 2014
А для чего вообще нужен Spring ?
Меньше шаблонного инфраструктурного кода в сравнении с программой просто на Java
Меньше инфраструктурного кода Spring !
Концентрация на бизнес-логике, а не на конфигурации приложения!
Чак Норрис
настолько крут ...
Хочу шутку!
Случайная
Шутка
Случайная
Шутка
Случайная
Шутка
Получить шутку
Получить шутку
Чак одобряет!
Сохранить!
Сохранить последнюю шутку
Интерфейс
Сервис
API
Инициализация проекта |
Сервис получения шуток |
Интерфейс пользователя |
Сервис работы с данными |
Логирование |
Создать интерфейс JokeService |
Создать имплементацию с помощью RestTemplate |
Проверить! |
public interface JokeService {
String getJoke();
}
@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();
}
}
@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": [] } }
@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.
Давайте общаться с пользователем через интерфейс командной строки.
Проект 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.
Тест не запускается. Из-за 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.
Как реализовать?
Все остальное сделает Spring Boot!
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 и получить отчет об автоконфигурации
============================
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
Адрес консоли по умолчанию: 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 |
Конкретные заголовки мы скинем в чат
// используйте метод 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);
@ShellMethod("Get joke about Chuck Norris.")
public String joke(
@ShellOption(defaultValue = "NERDY")
String category) {
lastJoke = jokeService.getJoke(category);
return lastJoke;
}