retrofit-spring-boot-starter
Стартер для Retrofit на Spring Boot, поддерживает быструю интеграцию и расширение функций.
Разработка с открытым исходным кодом непроста, пожалуйста, поставьте мне звезду⭐️
<dependency>
<groupId>com.github.lianjiatech</groupId>
<artifactId>retrofit-spring-boot-starter</artifactId>
<version>3.1.3</version>
</dependency>
Интерфейсы должны быть отмечены аннотацией @RetrofitClient! Для связанных с HTTP аннотаций см. официальную документацию: Официальная документация Retrofit.
@RetrofitClient(baseUrl = "${test.baseUrl}")
public interface UserService {
/**
* Запрос имени пользователя по id
*/
@POST("getName")
String getName(@Query("id") Long id);
}
Обратите внимание: Путь запроса метода должен начинаться с
/
. ДляRetrofit
, еслиbaseUrl=http://localhost:8080/api/test/
и путь запроса метода равенperson
, то полный путь запроса метода будет:http://localhost: 8080/api/test/person
. Если путь запроса метода/person
, полный путь запроса метода:http://localhost:8080/person
.
Внедрите интерфейс в другие сервисы для использования:
@Service
public class BusinessService {
@Autowired
private UserService userService;
public void doBusiness() {
// вызов userService
}
}
По умолчанию Spring Boot
автоматически сканирует путь для регистрации RetrofitClient
. Вы также можете вручную указать путь сканирования, добавив @RetrofitScan
в класс конфигурации.
Все аннотации, связанные с запросами HTTP
, используют собственные аннотации Retrofit
. Ниже приводится краткое описание:
Классификация | Поддерживаемые аннотации |
---|---|
Метод запроса |
@GET @HEAD @POST @PUT @DELETE @OPTIONS @HTTP
|
Заголовок запроса |
@Header @HeaderMap @Headers
|
Параметр запроса |
@Query Конфигурация свойств
|
Компонент поддерживает множество настраиваемых свойств для работы с различными бизнес-сценариями. Конкретные поддерживаемые свойства конфигурации и их значения по умолчанию следующие:
retrofit:
global-converter-factories:
- com.github.lianjiatech.retrofit.spring.boot.core.BasicTypeConverterFactory
- retrofit2.converter.jackson.JacksonConverterFactory
global-call-adapter-factories:
global-log:
enable: true
log-level: info
log-strategy: basic
aggregate: true
global-retry:
enable: false
interval-ms: 100
max-retries: 2
retry-rules:
- response_status_not_2xx
- occur_io_exception
global-timeout:
read-timeout-ms: 10000
write-timeout-ms: 10000
connect-timeout-ms: 10000
call-timeout-ms: 0
degrade:
degrade-type: none
global-sentinel-degrade:
enable: false
# Threshold corresponding to each degrade policy. Average response time (ms), exceptions ratio (0-1), number of exceptions (1-N)
count: 1000
time-window: 5
# Degradation strategy (0: average response time; 1: ratio of exceptions; 2: number of exceptions)
grade: 0
global-resilience4j-degrade:
enable: false
# Get CircuitBreakerConfig from {@link CircuitBreakerConfigRegistry} based on this name as a global circuit breaker configuration
circuit-breaker-config-name: defaultCircuitBreakerConfig
auto-set-prototype-scope-for-path-math-interceptor: true
Дополнительные функции
Настройка времени ожидания Если вам нужно изменить только время ожидания OkHttpClient, вы можете сделать это через соответствующие поля @RetrofitClient или изменить глобальную конфигурацию времени ожидания.
Настройте OkHttpClient Если вам необходимо изменить другую конфигурацию OkHttpClient, вы можете это сделать, настроив OkHttpClient. Шаги следующие:
Стратегия ведения журнала: базовая
Агрегирование: да
Имя журнала: com.github.lianjiatech.retrofit.spring.boot.log.LoggingInterceptor
Значения четырёх стратегий печати журналов следующие:
Если требуется печатать журналы только для некоторых запросов, можно использовать аннотацию @Logging на соответствующем интерфейсе или методе.
Если необходимо изменить поведение печати журналов, можно наследовать класс LoggingInterceptor и настроить его как Spring bean.
Компонентная поддержка поддерживает глобальный повтор и декларативный повтор.
Глобальный повтор по умолчанию отключён, а элементы конфигурации по умолчанию выглядят следующим образом:
retrofit:
global-retry:
enable: false
interval-ms: 100
max-retries: 2
retry-rules:
- response_status_not_2xx
- occur_io_exception
Правило повтора поддерживает три конфигурации:
Если только часть запроса должна быть повторена, можно использовать аннотацию @Retry на соответствующем интерфейсе или методе.
Если необходимо изменить поведение повтора запросов, можно наследовать RetryInterceptor и настроить его как Spring bean.
Снижение нагрузки по умолчанию отключено и в настоящее время поддерживает реализации sentinel и resilience4j.
retrofit:
degrade:
# Тип снижения нагрузки. По умолчанию — none, что означает, что снижение нагрузки не включено
degrade-type: sentinel
Настройте degrade-type=sentinel для включения, а затем объявите аннотацию @SentinelDegrade на соответствующем интерфейсе или методе.
Не забудьте вручную импортировать зависимости Sentinel:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.6.3</version>
</dependency>
Кроме того, также поддерживается глобальное снижение нагрузки Sentinel:
retrofit:
degrade:
degrade-type: sentinel
global-sentinel-degrade:
enable: true
# Другая глобальная конфигурация Sentinel
Настройте degrade-type=resilience4j для включения. Затем объявите @Resilience4jDegrade на соответствующем интерфейсе или методе.
Не забудьте вручную импортировать зависимости Resilience4j:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-circuitbreaker</artifactId>
<version>1.7.1</version>
</dependency>
Также поддерживается глобальное снижение нагрузки Resilience4j:
retrofit:
degrade:
degrade-type: resilience4j
global-resilience4j-degrade:
enable: true
# Получить CircuitBreakerConfig из {@link CircuitBreakerConfigRegistry} на основе этого имени в качестве глобальной конфигурации выключателя
circuit-breaker-config-name: defaultCircuitBreakerConfig
Управление конфигурацией выключателя:
Реализуйте интерфейс CircuitBreakerConfigRegistrar и зарегистрируйте CircuitBreakerConfig.
@Component
public class CustomCircuitBreakerConfigRegistrar implements CircuitBreakerConfigRegistrar {
@Override
public void register(CircuitBreakerConfigRegistry registry) {
registry.register(Constants.DEFAULT_CIRCUIT_BREAKER_CONFIG, CircuitBreakerConfig.ofDefaults());
registry.register("testCircuitBreakerConfig", CircuitBreakerConfig.custom()
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED)
.failureRateThreshold(20)
.minimumNumberOfCalls(5) **Укажите CircuitBreakerConfig через circuitBreakerConfigName.**
Включите retrofit.degrade.global-resilience4j-degrade.circuit-breaker-config-name или @Resilience4jDegrade.circuitBreakerConfigName.
**Расширенное понижение уровня схемы.**
Если пользователю необходимо использовать другую реализацию понижения уровня схемы, наследуйте BaseRetrofitDegrade и настройте его с помощью Spring Bean.
**Настройте fallback или fallbackFactory (необязательно).**
Если @RetrofitClient не устанавливает fallback или fallbackFactory, при срабатывании прерывателя цепи будет напрямую генерироваться исключение RetrofitBlockException. Пользователи могут настроить возвращаемое значение метода при перегорании, установив fallback или fallbackFactory.
Обратите внимание: класс fallback должен быть классом реализации текущего интерфейса, а fallbackFactory должен быть FallbackFactory<T> — классом реализации универсального параметра текущего типа интерфейса. Кроме того, экземпляры fallback и fallbackFactory должны быть настроены как Spring Bean.
Основное различие между fallbackFactory и fallback заключается в том, что он может воспринимать аномальную причину (причину) каждого предохранителя. Приведённый пример:
```java
@Slf4j
@Service
public class HttpDegradeFallback implements HttpDegradeApi {
@Override
public Result<Integer> test() {
Result<Integer> fallback = new Result<>();
fallback.setCode(100)
.setMsg("fallback")
.setBody(1000000);
return fallback;
}
}
@Slf4j
@Service
public class HttpDegradeFallbackFactory implements FallbackFactory<HttpDegradeApi> {
@Override
public HttpDegradeApi create(Throwable cause) {
log.error("触发熔断了! ", cause.getMessage(), cause);
return new HttpDegradeApi() {
@Override
public Result<Integer> test() {
Result<Integer> fallback = new Result<>();
fallback.setCode(100)
.setMsg("fallback")
.setBody(1000000);
return fallback;
}
};
}
}
Декодер ошибок.
Когда в HTTP возникает ошибка запроса (включая исключение или данные ответа не соответствуют ожиданиям), декодер ошибок может декодировать информацию, связанную с HTTP, в пользовательское исключение. Вы можете использовать errorDecoder() в аннотации @RetrofitClient для указания декодера ошибок текущего интерфейса. Пользовательские декодеры ошибок должны реализовывать интерфейс ErrorDecoder:
HTTP-вызовы между микросервисами.
Наследуйте ServiceInstanceChooser.
Пользователи могут реализовать интерфейс ServiceInstanceChooser самостоятельно, завершить логику выбора экземпляров служб и настроить их как Spring Bean. Для приложения Spring Cloud это можно реализовать следующим образом:
@Service
public class SpringCloudServiceInstanceChooser implements ServiceInstanceChooser {
private LoadBalancerClient loadBalancerClient;
@Autowired
public SpringCloudServiceInstanceChooser(LoadBalancerClient loadBalancerClient) {
this.loadBalancerClient = loadBalancerClient;
}
/**
* Выбирает URI ServiceInstance из LoadBalancer для указанной службы.
*
* @param serviceId Идентификатор службы для поиска в LoadBalancer.
* @return Возвращает uri ServiceInstance
*/
@Override
public URI choose(String serviceId) {
ServiceInstance serviceInstance = loadBalancerClient.choose(serviceId);
Assert.notNull(serviceInstance, "can not found service instance! serviceId=" + serviceId);
return serviceInstance.getUri();
}
}
Укажите serviceId и path.
@RetrofitClient(serviceId = "user", path = "/api/user")
public interface ChooserOkHttpUserService {
/**
* 根据id查询用户信息
*/
@GET("getUser")
User getUser(@Query("id") Long id);
}
Глобальный перехватчик.
Если нам нужно выполнить унифицированную обработку перехвата для HTTP-запросов всей системы, мы можем реализовать глобальный перехватчик. GlobalInterceptor и настройка его как spring Bean.
@Component
public class MyGlobalInterceptor implements GlobalInterceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
// response的Header加上global
return response.newBuilder().header("global", "true").build();
}
}
Реализуйте интерфейс NetworkInterceptor и настройте его как spring Bean.
Retrofit может адаптировать объекты Call к типу возвращаемого значения методов интерфейса через CallAdapterFactory. Компонент расширяет некоторые реализации CallAdapterFactory:
Retrofit выберет соответствующий CallAdapterFactory для выполнения обработки адаптации в соответствии с типом возвращаемого значения метода**. В настоящее время поддерживаются следующие типы возвращаемого значения:**
CallAdapter можно расширить, расширив CallAdapter.Factory.
Компоненты поддерживают настройку глобальных фабрик адаптеров вызовов через retrofit.global-call-adapter-factories:
retrofit:
# The `CallAdaptorFactory` factory extended by the component has been built in, please do not repeat the configuration here
global-call-adapter-factories:
# ...
Для каждого интерфейса Java также можно указать CallAdapter.Factory, используемый текущим интерфейсом, через @RetrofitClient.callAdapterFactories.
Рекомендация: настроить CallAdapter.Factory как Spring Bean.
Retrofit использует Converter для преобразования объекта, аннотированного с помощью @Body, в тело запроса, а тела ответа — в объект Java. Вы можете выбрать следующий Converter:
Настройте глобальный Converter.Factory через retrofit.global-converter-factories, по умолчанию используется retrofit2.converter.jackson.JacksonConverterFactory.
Если вам нужно изменить конфигурацию Jackson, вы можете... Переопределение конфигурации компонента JacksonConverterFactory
для Retrofit
.
retrofit:
global-converter-factories:
- com.github.lianjiatech.retrofit.spring.boot.core.BasicTypeConverterFactory
- retrofit2.converter.jackson.JacksonConverterFactory
Для каждого интерфейса Java вы также можете указать Converter.Factory
, используемый текущим интерфейсом, с помощью @RetrofitClient.converterFactories
.
Рекомендация: Настройте
Converter.Factory
какSpring Bean
.
Метааннотация.
Аннотации, такие как @RetrofitClient
, @Retry
, @Logging
, @Resilience4jDegrade
, поддерживают метааннотации, наследование и @AliasFor
.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Inherited
@RetrofitClient(baseUrl = "${test.baseUrl}")
@Logging(logLevel = LogLevel.WARN)
@Retry(intervalMs = 200)
public @interface MyRetrofitClient {
@AliasFor(annotation = RetrofitClient.class, attribute = "converterFactories")
Class<? extends Converter.Factory>[] converterFactories() default {GsonConverterFactory.class};
@AliasFor(annotation = Logging.class, attribute = "logStrategy")
LogStrategy logStrategy() default LogStrategy.BODY;
}
Другие примеры.
Параметры формы.
@FormUrlEncoded
@POST("token/verify")
Object tokenVerify(@Field("source") String source,@Field("signature") String signature,@Field("token") String token);
@FormUrlEncoded
@POST("message")
CompletableFuture<Object> sendMessage(@FieldMap Map<String, Object> param);
Загрузка файла.
MultipartBody.Part
// 对文件名使用URLEncoder进行编码
public ResponseEntity importTerminology(MultipartFile file){
String fileName=URLEncoder.encode(Objects.requireNonNull(file.getOriginalFilename()),"utf-8");
okhttp3.RequestBody requestBody=okhttp3.RequestBody.create(MediaType.parse("multipart/form-data"),file.getBytes());
MultipartBody.Part part=MultipartBody.Part.createFormData("file",fileName,requestBody);
apiService.upload(part);
return ok().build();
}
@POST("upload")
@Multipart
Void upload(@Part MultipartBody.Part file);
Скачивание файла.
@RetrofitClient(baseUrl = "https://img.ljcdn.com/hc-picture/")
public interface DownloadApi {
@GET("{fileKey}")
Response<ResponseBody> download(@Path("fileKey") String fileKey);
}
@SpringBootTest(classes = RetrofitTestApplication.class)
@RunWith(SpringRunner.class)
public class DownloadTest {
@Autowired
DownloadApi downLoadApi;
@Test
public void download() throws Exception {
String fileKey = "6302d742-ebc8-4649-95cf-62ccf57a1add";
Response<ResponseBody> response = downLoadApi.download(fileKey);
ResponseBody responseBody = response.body();
InputStream is = responseBody.byteStream();
File tempDirectory = new File("temp");
if (!tempDirectory.exists()) {
tempDirectory.mkdir();
}
File file = new File(tempDirectory, UUID.randomUUID().toString());
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
byte[] b = new byte[1024];
int length;
while ((length = is.read(b)) > 0) {
fos.write(b, 0, length);
}
is.close();
fos.close();
}
}
Динамический URL.
Используйте аннотацию @url
, чтобы реализовать динамические URL. На этом этапе baseUrl
можно настроить с любым допустимым URL. Например: http://github.com/
. Во время выполнения будут отправляться запросы только на основе адреса @Url
.
Примечание: аннотация
@url
должна быть помещена в первую позицию параметра метода. Кроме того, в аннотациях, таких как@GET
и@POST
, нет необходимости определять путь конечной точки.
@GET
Map<String, Object> test3(@Url String url,@Query("name") String name);
DELETE
-запрос добавляет тело запроса.
@HTTP(method = "DELETE", path = "/user/delete", hasBody = true)
GET
-запрос добавляет. Сама по себе okhttp3
не поддерживает GET
-запрос для добавления тела запроса. Исходный код выглядит следующим образом:
[image](https://user-images.githubusercontent.com/30620547/108949806-0a9f7780-76a0-11eb-9eb4-326d5d546e98.png)
[image](https://user-images.githubusercontent.com/30620547/108949831-1ab75700-76a0-11eb-955c-95d324084580.png)
Автор приводит конкретные причины, вы можете обратиться к: [issue](https://github.com/square/okhttp/issues/3154).
Однако если вам действительно нужно это сделать, можно использовать: `@HTTP(method = "get", path = "/user/get", hasBody = true)`, используйте строчную букву `get`, чтобы обойти вышеуказанные ограничения.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )