Якщо ви коли-небудь намагалися зробити HTTP виклик у проекті Spring Boot, то знаєте, що це не так просто, як здається. Звісно, вам потрібен тільки HTTP клієнт, але коли ви починаєте розглядати варіанти — RestClient, WebClient, RestTemplate… і всі ці ручні налаштування — все це може швидко стати дуже складним.
Якщо ви втомилися від великої кількості шаблонного коду або хочете знайти більш елегантне рішення, Spring Cloud OpenFeign може бути саме тим, що вам потрібно. У цій статті ми розглянемо, що таке OpenFeign, чому його варто використовувати та як почати з практичними прикладами.
Походження: від Feign до OpenFeign
Мені подобається приділяти час, щоб розуміти речі детально, бо навіть маленькі деталі мають значення. Тому ми почнемо з розгляду походження OpenFeign, щоб краще зрозуміти, як і чому це працює. Якщо ви прийшли лише за практичними прикладами (що абсолютно нормально), можете сміливо пропустити до розділу Практичний посібник. Інакше сідайте зручніше, і почнемо.
Що таке Feign?
Feign — це декларативна бібліотека REST-клієнтів, розроблена компанією Netflix як частина її екосистеми відкритих мікросервісів. Назва Feign, що означає "вдавати" або "імітувати", відображає її призначення: ви визначаєте інтерфейси Java з анотаціями HTTP-методів, а Feign генерує реалізації клієнтів під час виконання.
Ось як виглядає клієнт Feign:
public interface BookClient {
@RequestLine("GET /{isbn}")
BookResource findByIsbn(@Param("isbn") String isbn);
@RequestLine("GET")
List findAll();
@RequestLine("POST")
@Headers("Content-Type: application/json")
void create(Book book);
}
Feign використовує наведений інтерфейс як шаблон для автоматичного створення робочого клієнта без необхідності писати код, пов’язаний з HTTP.
Наприклад, метод findByIsbn
отримує інформацію про конкретну книгу за її ISBN:
@RequestLine(“GET /{isbn}”)
означає GET запит за шляхом типу/123456
, де 123456 — це ISBN.@Param(“isbn”)
зв’язує параметр isbn з{isbn}
в URL.BookResource
— це об’єкт, до якого буде перетворено відповідь від сервера.
І так далі, ви зрозуміли ідею…
Без використання Feign (і використовуючи RestClient, наприклад) ваш код міг би виглядати так:
@Service
public class BookService {
private final RestClient restClient;
public BookService() {
this.restClient = RestClient.builder()
.baseUrl("http://book-api.com")
.uriBuilderFactory(new DefaultUriBuilderFactory("http://book-api.com"))
.build();
}
public BookResource findByIsbn(String isbn) {
return restClient.get()
.uri("/{isbn}", isbn)
.retrieve()
.body(BookResource.class);
}
public List findAll() {
return restClient.get()
.uri("/")
.retrieve()
.body(new ParameterizedTypeReference<List<BookResource>>() {});
}
public void create(Book book) {
restClient.post()
.uri("/")
.contentType(MediaType.APPLICATION_JSON)
.body(book)
.retrieve()
.toBodilessEntity();
}
}
Feign, зменшуючи кількість шаблонного коду, дозволяє зосередитись на логіці програми, поки бібліотека "імітує" HTTP API клієнтів за допомогою декларативних інтерфейсів.
Перехід до Spring Cloud OpenFeign
Коли Netflix представив Feign, Spring Cloud інтегрував його як Spring Cloud Netflix Feign, що спростило його використання в додатках Spring Boot.
З часом Netflix припинив підтримку кількох проектів, включаючи Feign. Тоді спільнота взяла на себе ініціативу і створила OpenFeign, а Spring Cloud перейшов на Spring Cloud OpenFeign, щоб підтримувати проект, який розвивається спільнотою.
Для більш глибокого розуміння того, як Feign еволюціонував у OpenFeign, перегляньте вступний посібник по Netflix Feign і цю статтю порівняння Netflix Feign vs. OpenFeign.
OpenFeign](https://www.baeldung.com/netflix-feign-vs-openfeign) — це чудові ресурси.
Практичний посібник з OpenFeign
Spring Cloud OpenFeign базується на OpenFeign і інтегрується з екосистемою Spring. Це значно полегшує використання Feign у додатках Spring Boot завдяки таким можливостям, як впровадження залежностей, підтримка анотацій MVC, інтеграція з балансувальником навантаження, автоконфігурація, покращена підтримка тестування і кастомні налаштування.
Давайте розглянемо використання OpenFeign на практиці та подивимося, як це допомагає писати більш чистий код у відповідних сценаріях.
Спочатку додайте залежність OpenFeign до вашого pom.xml
:
org.springframework.cloud
spring-cloud-starter-openfeign
Додайте анотацію @EnableFeignClients
до вашого головного класу програми:
@EnableFeignClients
@SpringBootApplication
public class SpringCloudOpenfeignDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudOpenfeignDemoApplication.class, args);
}
}
Додайте інформацію про ресурс, який ви хочете використовувати, у файл application.yml
. У цьому випадку я використовую JSONPlaceholder — дуже зручний безкоштовний фейковий API, ідеальний для таких цілей:
spring:
cloud:
openfeign:
client:
config:
post-service:
url: https://jsonplaceholder.typicode.com/posts
Далі створіть декларативний інтерфейс та анотуйте його за допомогою @FeignClient(name = "post-service")
.
Зверніть увагу, що ім'я, визначене в конфігураційних властивостях вище, повинно відповідати імені, зазначеному в анотації.
@FeignClient(name = "post-service")
public interface PostClient {
@GetMapping
List getPosts();
@GetMapping("/{id}")
Post getPostById(@PathVariable Long id);
@GetMapping("/{id}/comments")
List getCommentsByPostId(@PathVariable Long id);
@PostMapping
Post createPost(@RequestBody Post post);
@PutMapping("/{id}")
Post updatePost(@PathVariable Long id, @RequestBody Post post);
@DeleteMapping("/{id}")
void deletePost(@PathVariable Long id);
}
Тепер інжектуйте інтерфейс PostClient
у ваш клас сервісу та викликайте його методи:
@Service
public class PostService {
private final PostClient postClient;
public PostService(PostClient postClient) {
this.postClient = postClient;
}
public List getAllPosts() {
return postClient.getPosts();
}
public Post getPostById(Long id) {
return postClient.getPostById(id);
}
public List getCommentsByPostId(Long postId) {
return postClient.getCommentsByPostId(postId);
}
public Post createPost(Post post) {
return postClient.createPost(post);
}
public Post updatePost(Long id, Post post) {
return postClient.updatePost(id, post);
}
public void deletePost(Long id) {
postClient.deletePost(id);
}
}
І наостанок створіть PostController
:
@RestController
@RequestMapping("/posts")
public class PostController {
private final PostService postService;
public PostController(PostService postService) {
this.postService = postService;
}
@GetMapping
public List getAllPosts() {
return postService.getAllPosts();
}
@GetMapping("/{id}")
public Post getPost(@PathVariable Long id) {
return postService.getPostById(id);
}
@GetMapping("/{id}/comments")
public List getComments(@PathVariable Long id) {
return postService.getCommentsByPostId(id);
}
@PostMapping
public Post createPost(@RequestBody Post post) {
return postService.createPost(post);
}
@PutMapping("/{id}")
public Post updatePost(@PathVariable Long id, @RequestBody Post post) {
return postService.updatePost(id, post);
}
@DeleteMapping("/{id}")
public void deletePost(@PathVariable Long id) {
postService.deletePost(id);
}
}
Тепер протестуємо результати:
Тестування методів контролера по черзі, демонструючи роботу клієнта Feign
Під капотом, що відбувається, це те, що ми робимо виклики до наших власних кінцевих точок контролера на localhost
. Однак за лаштунками наша реалізація перенаправляє ці виклики до https://jsonplaceholder.typicode.com/posts сервісу, використовуючи клієнт Feign для обробки комунікації.
Розширена конфігурація
Щоб зробити розробку ще простішою, OpenFeign підтримує розширені конфігурації.
🟢 Потрібно перехопити запит з аутентифікацією? Не проблема. Реалізуйте RequestInterceptor
від Feign:
@Configuration
public class AuthInterceptorConfiguration implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
String auth = "PUBLIC_KEY" + ":" + "SECRET_KEY";
byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes(StandardCharsets.UTF_8));
String authHeader = "Basic " + new String(encodedAuth);
requestTemplate.header("Authorization", authHeader);
}
}
І визначте його в application.yaml
:
spring:
cloud:
openfeign:
client:
config:
post-service:
url: https://jsonplaceholder.typicode.com/posts
requestInterceptors:
- com.anita.springcloudopenfeigndemo.AuthInterceptorConfiguration
🟢 Потрібно налаштувати рівні логування? Легко.
Просто додайте бажану властивість рівня логера (доступні опції — full, basic, headers та none) у application.yaml
:
spring:
cloud:
openfeign:
client:
config:
post-service:
url: https://jsonplaceholder.typicode.com/posts
logger-level: full
🟢 Централізований декодер помилок? Звісно:
@Component
public class CustomErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
// Логіка обробки помилок тут
return new RuntimeException("Сталася помилка: " + response.status());
}
}
🟢 Логіка відновлення на випадок помилок? Перевірено. Додайте залежність для Circuitbreaker Resilience4j у ваш pom.xml
:
org.springframework.cloud
spring-cloud-starter-circuitbreaker-resilience4j
3.2.0
Додайте реалізацію fallback для вашого клієнта Feign:
@Component
public class PostClientFallback implements PostClient {
@Override
public List getPosts() {
return List.of();
}
@Override
public Post getPostById(Long id) {
return new Post(
RandomGenerator.getDefault().nextLong(),
RandomGenerator.getDefault().nextLong(),
"fallback-title",
"fallback-body"
);
}
// Інші методи
}
І оголосіть це в вашій дефініції Feign клієнта таким чином:
@FeignClient(name = "post-service", fallback = PostClientFallback.class)
public interface PostClient {
// Інші методи
}
Якщо щось піде не так із сервісом, який ви використовуєте, реалізація fallback візьме на себе роботу. Просто та зручно.
Навмисне змінено URL сервісу, щоб викликати помилку, демонструючи використання fallback реалізації в таких випадках
Коли використовувати Spring Cloud OpenFeign?
Щоб відповісти на питання коли, важливо порівняти можливості доступних рішень.
🟡 Feign вирізняється своєю простотою та безшовною інтеграцією з Spring, особливо для декларативних REST викликів.
🟡 WebClient є потужним вибором для реактивних та асинхронних сценаріїв, ідеально підходить для роботи з потоками даних.
🟡 RestClient пропонує сучасний та гнучкий підхід для синхронних задач, сумісний з новими функціями Spring.
🟡 RestTemplate, хоч і дещо застарілий, залишаєтьcя надійним для простих синхронних задач.
🟡 Додатково, можна розглянути Apache HttpClient або OkHttp для складніших випадків, що вимагають точного контролю або оптимізації продуктивності.
Кожен інструмент має свої переваги, тому вибір залежить від архітектури вашого додатку, складності та конкретних вимог до випадку.
Метою цього блогу було поділитися своїм захопленням від того, як Spring Cloud OpenFeign може стати елегантним та потужним рішенням для здійснення HTTP викликів. Минуло не так багато часу від того, як я його відкрив, але він швидко став одним із моїх улюблених інструментів для написання чистого та більш підтримуваного коду.
Якщо у вас був подібний досвід або ви маєте іншу точку зору, буду радий почитати ваші думки в коментарях!
При цьому важливо пам'ятати, що інструменти повинні відповідати проблемі, а не навпаки. Завжди ретельно оцінюйте свої варіанти перед прийняттям рішення.
Якщо вам цікаво дізнатися більше про OpenFeign, я настійно рекомендую ознайомитися з офіційною документацією. А якщо ви хочете зрозуміти, як він вписується в ширший світ мікросервісів, ось огляд на GeeksforGeeks, який також стане чудовим стартом.
Усі приклади коду, показані в цьому посібнику, доступні на GitHub.
І, як завжди, не пропустіть наступні блоги! ✨
Перекладено з: Still Haven’t Tried Spring Cloud OpenFeign? You’re Missing Out