diff --git a/README.md b/README.md
index d86265d..dc47271 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,7 @@ gitee项目地址:[https://gitee.com/lianjiatech/retrofit-spring-boot-starter]
- [x] [全局拦截器](#全局拦截器)
- [x] [调用适配器](#调用适配器)
- [x] [数据转换器](#数据转码器)
+- [x] [元注解](#元注解)
- [x] [其他功能示例](#其他功能示例)
## 快速使用
@@ -51,7 +52,7 @@ gitee项目地址:[https://gitee.com/lianjiatech/retrofit-spring-boot-starter]
com.github.lianjiatech
retrofit-spring-boot-starter
- 2.3.1
+ 2.3.2
```
@@ -64,7 +65,7 @@ gitee项目地址:[https://gitee.com/lianjiatech/retrofit-spring-boot-starter]
com.github.lianjiatech
retrofit-spring-boot-starter
- 2.3.1
+ 2.3.2
com.squareup.okhttp3
@@ -854,6 +855,29 @@ retrofit:
针对每个Java接口,还可以通过`@RetrofitClient`注解的`converterFactories()`指定当前接口采用的`Converter.Factory`,指定的转换器工厂实例依然优先从Spring容器获取。
+### 元注解
+
+`@RetrofitClient`、`@Retry`、`@Logging`、`@Resilience4jDegrade`等注解支持元注解、继承以及`@AliasFor`。 我们可以随意组合、调整相关注解:
+
+```java
+
+@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;
+}
+```
+
## 其他功能示例
### form参数接口调用
diff --git a/pom.xml b/pom.xml
index 79e827c..d440821 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.github.lianjiatech
retrofit-spring-boot-starter
- 2.3.1
+ 2.3.2
retrofit-spring-boot-starter
retrofit-spring-boot-starter
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitFactoryBean.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitFactoryBean.java
index 8970ee2..5f9c3f8 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitFactoryBean.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitFactoryBean.java
@@ -10,6 +10,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.BeansException;
@@ -17,6 +18,7 @@
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
+import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
@@ -87,7 +89,8 @@ public boolean isSingleton() {
}
private okhttp3.ConnectionPool parseConnectionPool() {
- RetrofitClient retrofitClient = retrofitInterface.getAnnotation(RetrofitClient.class);
+ RetrofitClient retrofitClient =
+ AnnotatedElementUtils.findMergedAnnotation(retrofitInterface, RetrofitClient.class);
String poolName = retrofitClient.poolName();
Map poolRegistry = retrofitConfigBean.getPoolRegistry();
Assert.notNull(poolRegistry, "poolRegistry does not exist! Please set retrofitConfigBean.poolRegistry!");
@@ -99,7 +102,8 @@ private okhttp3.ConnectionPool parseConnectionPool() {
private OkHttpClient createOkHttpClient() throws IllegalAccessException, InvocationTargetException {
OkHttpClient.Builder okHttpClientBuilder = createOkHttpClientBuilder();
- RetrofitClient retrofitClient = retrofitInterface.getAnnotation(RetrofitClient.class);
+ RetrofitClient retrofitClient =
+ AnnotatedElementUtils.findMergedAnnotation(retrofitInterface, RetrofitClient.class);
if (isEnableDegrade(retrofitInterface)) {
okHttpClientBuilder.addInterceptor(retrofitConfigBean.getRetrofitDegrade());
}
@@ -116,7 +120,8 @@ private OkHttpClient createOkHttpClient() throws IllegalAccessException, Invocat
}
private OkHttpClient.Builder createOkHttpClientBuilder() throws InvocationTargetException, IllegalAccessException {
- RetrofitClient retrofitClient = retrofitInterface.getAnnotation(RetrofitClient.class);
+ RetrofitClient retrofitClient =
+ AnnotatedElementUtils.findMergedAnnotation(retrofitInterface, RetrofitClient.class);
Method method = findOkHttpClientBuilderMethod();
if (method != null) {
return (OkHttpClient.Builder)method.invoke(null);
@@ -158,11 +163,11 @@ private Method findOkHttpClientBuilderMethod() {
@SuppressWarnings("unchecked")
private List findInterceptorByAnnotation() {
- Annotation[] classAnnotations = retrofitInterface.getAnnotations();
+ Annotation[] classAnnotations = AnnotationUtils.getAnnotations(retrofitInterface);
List interceptors = new ArrayList<>();
// 找出被@InterceptMark标记的注解。Find the annotation marked by @InterceptMark
List interceptAnnotations = new ArrayList<>();
- for (Annotation classAnnotation : classAnnotations) {
+ for (Annotation classAnnotation : Objects.requireNonNull(classAnnotations)) {
Class extends Annotation> annotationType = classAnnotation.annotationType();
if (annotationType.isAnnotationPresent(InterceptMark.class)) {
interceptAnnotations.add(classAnnotation);
@@ -204,9 +209,9 @@ private List findInterceptorByAnnotation() {
return interceptors;
}
- private Retrofit createRetrofit()
- throws InstantiationException, IllegalAccessException, InvocationTargetException {
- RetrofitClient retrofitClient = retrofitInterface.getAnnotation(RetrofitClient.class);
+ private Retrofit createRetrofit() throws IllegalAccessException, InvocationTargetException {
+ RetrofitClient retrofitClient =
+ AnnotatedElementUtils.findMergedAnnotation(retrofitInterface, RetrofitClient.class);
String baseUrl = RetrofitUtils.convertBaseUrl(retrofitClient, retrofitClient.baseUrl(), environment);
OkHttpClient client = createOkHttpClient();
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitScan.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitScan.java
index 93121d5..dd83be8 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitScan.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitScan.java
@@ -2,6 +2,7 @@
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -15,6 +16,7 @@
@Target(ElementType.TYPE)
@Documented
@Import(RetrofitClientScannerRegistrar.class)
+@Inherited
public @interface RetrofitScan {
/**
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/BaseRetrofitDegrade.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/BaseRetrofitDegrade.java
index 50794e1..ad8c634 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/BaseRetrofitDegrade.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/BaseRetrofitDegrade.java
@@ -6,6 +6,7 @@
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.context.EnvironmentAware;
+import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.env.Environment;
import com.github.lianjiatech.retrofit.spring.boot.core.RetrofitClient;
@@ -29,7 +30,8 @@ public String parseResourceName(Method method) {
if (resourceName != null) {
return resourceName;
}
- RetrofitClient retrofitClient = method.getDeclaringClass().getAnnotation(RetrofitClient.class);
+ RetrofitClient retrofitClient =
+ AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), RetrofitClient.class);
String baseUrl = RetrofitUtils.convertBaseUrl(retrofitClient, retrofitClient.baseUrl(), environment);
HttpMethodPath httpMethodPath = parseHttpMethodPath(method);
resourceName = formatResourceName(baseUrl, httpMethodPath);
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeProxy.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeProxy.java
index 7d19b53..9d130f1 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeProxy.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeProxy.java
@@ -5,6 +5,7 @@
import java.lang.reflect.Proxy;
import org.springframework.context.ApplicationContext;
+import org.springframework.core.annotation.AnnotatedElementUtils;
import com.github.lianjiatech.retrofit.spring.boot.core.RetrofitClient;
@@ -22,7 +23,8 @@ public class DegradeProxy implements InvocationHandler {
@SuppressWarnings("unchecked")
public static T create(Object source, Class retrofitInterface, ApplicationContext applicationContext) {
- RetrofitClient retrofitClient = retrofitInterface.getAnnotation(RetrofitClient.class);
+ RetrofitClient retrofitClient =
+ AnnotatedElementUtils.findMergedAnnotation(retrofitInterface, RetrofitClient.class);
Class> fallbackClass = retrofitClient.fallback();
Object fallback = null;
if (!void.class.isAssignableFrom(fallbackClass)) {
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/ResourceNameParser.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/ResourceNameParser.java
index a675e0d..172e4ff 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/ResourceNameParser.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/ResourceNameParser.java
@@ -30,38 +30,38 @@ public interface ResourceNameParser {
*/
default HttpMethodPath parseHttpMethodPath(Method method) {
- if (method.isAnnotationPresent(HTTP.class)) {
- HTTP http = method.getAnnotation(HTTP.class);
+ HTTP http = method.getAnnotation(HTTP.class);
+ if (http != null) {
return new HttpMethodPath(http.method(), http.path());
}
- if (method.isAnnotationPresent(GET.class)) {
- GET get = method.getAnnotation(GET.class);
+ GET get = method.getAnnotation(GET.class);
+ if (get != null) {
return new HttpMethodPath("GET", get.value());
}
- if (method.isAnnotationPresent(POST.class)) {
- POST post = method.getAnnotation(POST.class);
+ POST post = method.getAnnotation(POST.class);
+ if (post != null) {
return new HttpMethodPath("POST", post.value());
}
- if (method.isAnnotationPresent(PUT.class)) {
- PUT put = method.getAnnotation(PUT.class);
+ PUT put = method.getAnnotation(PUT.class);
+ if (put != null) {
return new HttpMethodPath("PUT", put.value());
}
- if (method.isAnnotationPresent(DELETE.class)) {
- DELETE delete = method.getAnnotation(DELETE.class);
+ DELETE delete = method.getAnnotation(DELETE.class);
+ if (delete != null) {
return new HttpMethodPath("DELETE", delete.value());
}
- if (method.isAnnotationPresent(HEAD.class)) {
- HEAD head = method.getAnnotation(HEAD.class);
+ HEAD head = method.getAnnotation(HEAD.class);
+ if (head != null) {
return new HttpMethodPath("HEAD", head.value());
}
- if (method.isAnnotationPresent(PATCH.class)) {
- PATCH patch = method.getAnnotation(PATCH.class);
+ PATCH patch = method.getAnnotation(PATCH.class);
+ if (patch != null) {
return new HttpMethodPath("PATCH", patch.value());
}
throw new UnsupportedOperationException("unsupported method!" + method);
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/resilience4j/Resilience4jRetrofitDegrade.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/resilience4j/Resilience4jRetrofitDegrade.java
index d4328eb..5b3d972 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/resilience4j/Resilience4jRetrofitDegrade.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/resilience4j/Resilience4jRetrofitDegrade.java
@@ -43,7 +43,8 @@ public void loadDegradeRules(Class> retrofitInterface) {
continue;
}
Resilience4jDegrade resilience4jDegrade =
- AnnotationExtendUtils.findAnnotationIncludeClass(method, Resilience4jDegrade.class);
+ AnnotationExtendUtils.findMergedAnnotation(method, method.getDeclaringClass(),
+ Resilience4jDegrade.class);
if (resilience4jDegrade == null) {
continue;
}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/sentinel/SentinelRetrofitDegrade.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/sentinel/SentinelRetrofitDegrade.java
index 440549b..aadbb43 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/sentinel/SentinelRetrofitDegrade.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/sentinel/SentinelRetrofitDegrade.java
@@ -42,7 +42,8 @@ public void loadDegradeRules(Class> retrofitInterface) {
}
// 获取熔断配置
SentinelDegrade sentinelDegrade =
- AnnotationExtendUtils.findAnnotationIncludeClass(method, SentinelDegrade.class);
+ AnnotationExtendUtils.findMergedAnnotation(method, method.getDeclaringClass(),
+ SentinelDegrade.class);
if (sentinelDegrade == null) {
continue;
}
@@ -61,7 +62,8 @@ public void loadDegradeRules(Class> retrofitInterface) {
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Method method = Objects.requireNonNull(request.tag(Invocation.class)).method();
- if (AnnotationExtendUtils.findAnnotationIncludeClass(method, SentinelDegrade.class) == null) {
+ if (AnnotationExtendUtils.findMergedAnnotation(method, method.getDeclaringClass(),
+ SentinelDegrade.class) == null) {
return chain.proceed(request);
}
String resourceName = parseResourceName(method);
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/interceptor/ErrorDecoderInterceptor.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/interceptor/ErrorDecoderInterceptor.java
index 0ba9ce7..d307b69 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/interceptor/ErrorDecoderInterceptor.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/interceptor/ErrorDecoderInterceptor.java
@@ -7,6 +7,7 @@
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.annotation.AnnotatedElementUtils;
import com.github.lianjiatech.retrofit.spring.boot.core.ErrorDecoder;
import com.github.lianjiatech.retrofit.spring.boot.core.RetrofitClient;
@@ -30,7 +31,8 @@ public class ErrorDecoderInterceptor implements Interceptor, ApplicationContextA
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Method method = Objects.requireNonNull(request.tag(Invocation.class)).method();
- RetrofitClient retrofitClient = method.getDeclaringClass().getAnnotation(RetrofitClient.class);
+ RetrofitClient retrofitClient =
+ AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), RetrofitClient.class);
ErrorDecoder errorDecoder =
AppContextUtils.getBeanOrNew(applicationContext, retrofitClient.errorDecoder());
boolean decoded = false;
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/interceptor/ServiceChooseInterceptor.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/interceptor/ServiceChooseInterceptor.java
index c056291..e1641f3 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/interceptor/ServiceChooseInterceptor.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/interceptor/ServiceChooseInterceptor.java
@@ -5,6 +5,7 @@
import java.net.URI;
import java.util.Objects;
+import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.StringUtils;
import com.github.lianjiatech.retrofit.spring.boot.core.RetrofitClient;
@@ -32,7 +33,8 @@ public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Method method = Objects.requireNonNull(request.tag(Invocation.class)).method();
Class> declaringClass = method.getDeclaringClass();
- RetrofitClient retrofitClient = declaringClass.getAnnotation(RetrofitClient.class);
+ RetrofitClient retrofitClient =
+ AnnotatedElementUtils.findMergedAnnotation(declaringClass, RetrofitClient.class);
String baseUrl = retrofitClient.baseUrl();
if (StringUtils.hasText(baseUrl)) {
return chain.proceed(request);
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/log/LoggingInterceptor.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/log/LoggingInterceptor.java
index 8207118..3ea3a03 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/log/LoggingInterceptor.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/log/LoggingInterceptor.java
@@ -4,6 +4,8 @@
import java.lang.reflect.Method;
import java.util.Objects;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+
import com.github.lianjiatech.retrofit.spring.boot.config.LogProperty;
import lombok.extern.slf4j.Slf4j;
@@ -30,8 +32,7 @@ public LoggingInterceptor(LogProperty logProperty) {
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Method method = Objects.requireNonNull(request.tag(Invocation.class)).method();
- // 获取重试配置
- Logging logging = method.getDeclaringClass().getAnnotation(Logging.class);
+ Logging logging = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Logging.class);
if (!needLog(logging)) {
return chain.proceed(request);
}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/retry/RetryInterceptor.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/retry/RetryInterceptor.java
index 33db520..e8474e3 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/retry/RetryInterceptor.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/retry/RetryInterceptor.java
@@ -33,7 +33,7 @@ public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Method method = Objects.requireNonNull(request.tag(Invocation.class)).method();
// 获取重试配置
- Retry retry = AnnotationExtendUtils.findAnnotationIncludeClass(method, Retry.class);
+ Retry retry = AnnotationExtendUtils.findMergedAnnotation(method, method.getDeclaringClass(), Retry.class);
if (!needRetry(retry)) {
return chain.proceed(request);
}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/util/AnnotationExtendUtils.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/util/AnnotationExtendUtils.java
index 78ed35c..2426c18 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/util/AnnotationExtendUtils.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/util/AnnotationExtendUtils.java
@@ -3,6 +3,8 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+
import lombok.experimental.UtilityClass;
/**
@@ -16,15 +18,17 @@ public class AnnotationExtendUtils {
* 查找方法及其类上的指定注解,优先返回方法上的。
* @param 注解泛型参数
* @param method 方法
+ * @param clazz 类型
* @param annotationType 注解类型
* @return 方法及其类上的指定注解。
*/
- public static A findAnnotationIncludeClass(Method method, Class annotationType) {
- A annotation = method.getAnnotation(annotationType);
+ public static A findMergedAnnotation(Method method, Class> clazz,
+ Class annotationType) {
+ A annotation = AnnotatedElementUtils.findMergedAnnotation(method, annotationType);
if (annotation != null) {
return annotation;
}
- return method.getDeclaringClass().getAnnotation(annotationType);
+ return AnnotatedElementUtils.findMergedAnnotation(clazz, annotationType);
}
/**
@@ -36,11 +40,11 @@ public static A findAnnotationIncludeClass(Method method,
*/
public static boolean isAnnotationPresentIncludeMethod(Class> clazz,
Class annotationType) {
- if (clazz.isAnnotationPresent(annotationType)) {
+ if (AnnotatedElementUtils.findMergedAnnotation(clazz, annotationType) != null) {
return true;
}
for (Method method : clazz.getMethods()) {
- if (method.isAnnotationPresent(annotationType)) {
+ if (AnnotatedElementUtils.findMergedAnnotation(method, annotationType) != null) {
return true;
}
}
diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/MySpringBootApplication.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/MySpringBootApplication.java
new file mode 100644
index 0000000..c9e82bb
--- /dev/null
+++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/MySpringBootApplication.java
@@ -0,0 +1,24 @@
+package com.github.lianjiatech.retrofit.spring.boot.test;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+import com.github.lianjiatech.retrofit.spring.boot.core.RetrofitScan;
+
+/**
+ * @author 陈添明
+ * @since 2022/5/7 8:48 下午
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@RetrofitScan("com.github.lianjiatech.retrofit.spring.boot")
+@SpringBootApplication
+public @interface MySpringBootApplication {}
diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/RetrofitTestApplication.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/RetrofitTestApplication.java
index 49edb78..cedc617 100644
--- a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/RetrofitTestApplication.java
+++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/RetrofitTestApplication.java
@@ -1,19 +1,15 @@
package com.github.lianjiatech.retrofit.spring.boot.test;
import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
-import com.github.lianjiatech.retrofit.spring.boot.core.RetrofitScan;
-
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.jaxb.JaxbConverterFactory;
/**
* @author 陈添明
*/
-@SpringBootApplication
-@RetrofitScan("com.github.lianjiatech.retrofit.spring.boot")
+@MySpringBootApplication
public class RetrofitTestApplication {
public static void main(String[] args) {
diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/ErrorDecoderTest.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/decoder/ErrorDecoderTest.java
similarity index 94%
rename from src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/ErrorDecoderTest.java
rename to src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/decoder/ErrorDecoderTest.java
index 8d160dd..bdb1ce9 100644
--- a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/ErrorDecoderTest.java
+++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/decoder/ErrorDecoderTest.java
@@ -1,4 +1,4 @@
-package com.github.lianjiatech.retrofit.spring.boot.test;
+package com.github.lianjiatech.retrofit.spring.boot.test.decoder;
import java.io.IOException;
@@ -14,9 +14,9 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.lianjiatech.retrofit.spring.boot.test.RetrofitTestApplication;
import com.github.lianjiatech.retrofit.spring.boot.test.entity.Person;
import com.github.lianjiatech.retrofit.spring.boot.test.entity.Result;
-import com.github.lianjiatech.retrofit.spring.boot.test.http.ErrorDecoderTestApi;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/ErrorDecoderTestApi.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/decoder/ErrorDecoderTestApi.java
similarity index 91%
rename from src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/ErrorDecoderTestApi.java
rename to src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/decoder/ErrorDecoderTestApi.java
index 0669193..c33283b 100644
--- a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/ErrorDecoderTestApi.java
+++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/decoder/ErrorDecoderTestApi.java
@@ -1,4 +1,4 @@
-package com.github.lianjiatech.retrofit.spring.boot.test.http;
+package com.github.lianjiatech.retrofit.spring.boot.test.decoder;
import com.github.lianjiatech.retrofit.spring.boot.core.RetrofitClient;
import com.github.lianjiatech.retrofit.spring.boot.log.LogStrategy;
diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/DegradeR4jApi.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/degrade/DegradeR4jApi.java
similarity index 97%
rename from src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/DegradeR4jApi.java
rename to src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/degrade/DegradeR4jApi.java
index 535f62e..4448bce 100644
--- a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/DegradeR4jApi.java
+++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/degrade/DegradeR4jApi.java
@@ -1,4 +1,4 @@
-package com.github.lianjiatech.retrofit.spring.boot.test.http;
+package com.github.lianjiatech.retrofit.spring.boot.test.degrade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/DegradeR4jTest.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/degrade/DegradeR4jTest.java
similarity index 95%
rename from src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/DegradeR4jTest.java
rename to src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/degrade/DegradeR4jTest.java
index d5ac9be..7b4f588 100644
--- a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/DegradeR4jTest.java
+++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/degrade/DegradeR4jTest.java
@@ -1,4 +1,4 @@
-package com.github.lianjiatech.retrofit.spring.boot.test;
+package com.github.lianjiatech.retrofit.spring.boot.test.degrade;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@@ -17,9 +17,9 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.lianjiatech.retrofit.spring.boot.test.RetrofitTestApplication;
import com.github.lianjiatech.retrofit.spring.boot.test.entity.Person;
import com.github.lianjiatech.retrofit.spring.boot.test.entity.Result;
-import com.github.lianjiatech.retrofit.spring.boot.test.http.DegradeR4jApi;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/DegradeSentinelApi.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/degrade/DegradeSentinelApi.java
similarity index 97%
rename from src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/DegradeSentinelApi.java
rename to src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/degrade/DegradeSentinelApi.java
index 144e2d0..7c5555e 100644
--- a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/DegradeSentinelApi.java
+++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/degrade/DegradeSentinelApi.java
@@ -1,4 +1,4 @@
-package com.github.lianjiatech.retrofit.spring.boot.test.http;
+package com.github.lianjiatech.retrofit.spring.boot.test.degrade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/DegradeSentinelTest.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/degrade/DegradeSentinelTest.java
similarity index 95%
rename from src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/DegradeSentinelTest.java
rename to src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/degrade/DegradeSentinelTest.java
index 025d74a..de5cb02 100644
--- a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/DegradeSentinelTest.java
+++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/degrade/DegradeSentinelTest.java
@@ -1,4 +1,4 @@
-package com.github.lianjiatech.retrofit.spring.boot.test;
+package com.github.lianjiatech.retrofit.spring.boot.test.degrade;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@@ -17,9 +17,9 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.lianjiatech.retrofit.spring.boot.test.RetrofitTestApplication;
import com.github.lianjiatech.retrofit.spring.boot.test.entity.Person;
import com.github.lianjiatech.retrofit.spring.boot.test.entity.Result;
-import com.github.lianjiatech.retrofit.spring.boot.test.http.DegradeSentinelApi;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/meta/MetaAnnotationTest.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/meta/MetaAnnotationTest.java
new file mode 100644
index 0000000..f530322
--- /dev/null
+++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/meta/MetaAnnotationTest.java
@@ -0,0 +1,78 @@
+package com.github.lianjiatech.retrofit.spring.boot.test.meta;
+
+import java.io.IOException;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.github.lianjiatech.retrofit.spring.boot.test.RetrofitTestApplication;
+import com.github.lianjiatech.retrofit.spring.boot.test.entity.Person;
+import com.github.lianjiatech.retrofit.spring.boot.test.entity.Result;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+
+/**
+ * @author 陈添明
+ */
+@SpringBootTest(classes = RetrofitTestApplication.class)
+@RunWith(SpringRunner.class)
+public class MetaAnnotationTest {
+
+ @Autowired
+ private MetaAnnotationTestApi metaAnnotationTestApi;
+
+ private MockWebServer server;
+
+ private static Gson gson = new GsonBuilder()
+ .create();
+
+ @Before
+ public void before() throws IOException {
+ System.out.println("=========开启MockWebServer===========");
+ server = new MockWebServer();
+ server.start(8080);
+
+ }
+
+ @After
+ public void after() throws IOException {
+ System.out.println("=========关闭MockWebServer===========");
+ server.close();
+ }
+
+ @Test
+ public void test() {
+
+ // mock
+ Person mockPerson = new Person().setId(1L)
+ .setName("test")
+ .setAge(10);
+ Result mockResult = new Result<>()
+ .setCode(0)
+ .setMsg("ok")
+ .setData(mockPerson);
+ MockResponse response = new MockResponse()
+ .setResponseCode(200)
+ .addHeader("Content-Type", "application/json; charset=utf-8")
+ .addHeader("Cache-Control", "no-cache")
+ .setBody(gson.toJson(mockResult));
+ server.enqueue(response);
+
+ // http check
+ Result person = metaAnnotationTestApi.getPerson(1L);
+ Person data = person.getData();
+ Assert.assertNotNull(data);
+ Assert.assertEquals("test", data.getName());
+ Assert.assertEquals(10, data.getAge().intValue());
+ }
+
+}
diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/meta/MetaAnnotationTestApi.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/meta/MetaAnnotationTestApi.java
new file mode 100644
index 0000000..dae9fa8
--- /dev/null
+++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/meta/MetaAnnotationTestApi.java
@@ -0,0 +1,24 @@
+package com.github.lianjiatech.retrofit.spring.boot.test.meta;
+
+import com.github.lianjiatech.retrofit.spring.boot.log.LogStrategy;
+import com.github.lianjiatech.retrofit.spring.boot.test.entity.Person;
+import com.github.lianjiatech.retrofit.spring.boot.test.entity.Result;
+
+import retrofit2.converter.gson.GsonConverterFactory;
+import retrofit2.http.GET;
+import retrofit2.http.Headers;
+import retrofit2.http.Query;
+
+/**
+ * @author 陈添明
+ */
+@MyRetrofitClient(converterFactories = GsonConverterFactory.class, logStrategy = LogStrategy.HEADERS)
+public interface MetaAnnotationTestApi {
+
+ @GET("person")
+ @Headers({
+ "X-Foo: Bar",
+ "X-Ping: Pong"
+ })
+ Result getPerson(@Query("id") Long id);
+}
diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/meta/MyRetrofitClient.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/meta/MyRetrofitClient.java
new file mode 100644
index 0000000..1ca9cc2
--- /dev/null
+++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/meta/MyRetrofitClient.java
@@ -0,0 +1,39 @@
+package com.github.lianjiatech.retrofit.spring.boot.test.meta;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.core.annotation.AliasFor;
+
+import com.github.lianjiatech.retrofit.spring.boot.core.RetrofitClient;
+import com.github.lianjiatech.retrofit.spring.boot.log.LogLevel;
+import com.github.lianjiatech.retrofit.spring.boot.log.LogStrategy;
+import com.github.lianjiatech.retrofit.spring.boot.log.Logging;
+import com.github.lianjiatech.retrofit.spring.boot.retry.Retry;
+
+import retrofit2.Converter;
+import retrofit2.converter.gson.GsonConverterFactory;
+
+/**
+ * @author 陈添明
+ * @since 2022/5/7 6:51 下午
+ */
+@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;
+}