T map(Object result);
+
+ /**
+ * 解析返回类型
+ *
+ * @param resultType 返回类型
+ * @return 结果
+ */
+ Type resolve(Type resultType);
+}
diff --git a/rest-proxy-core/pom.xml b/rest-proxy-core/pom.xml
new file mode 100644
index 0000000..1a947d8
--- /dev/null
+++ b/rest-proxy-core/pom.xml
@@ -0,0 +1,90 @@
+
+
+
+ group.flyfish
+ rest-proxy
+ 1.0.0
+
+ 4.0.0
+
+ rest-proxy-core
+
+
+
+ group.flyfish
+ rest-proxy-api
+ ${sdk.version}
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.springframework.boot
+ spring-boot
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ javax.servlet
+ javax.servlet-api
+
+
+ org.springframework
+ spring-web
+
+
+ org.reflections
+ reflections
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.apache.httpcomponents
+ httpmime
+
+
+ commons-io
+ commons-io
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ junit
+ junit
+ test
+
+
+
+
+
+
+ ${project.basedir}/src/test/resources
+
+ dev/*
+ prod/*
+
+
+
+ ${project.basedir}/src/test/resources/${config.dir}
+
+
+
+
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/annotation/EnableRestApiProxy.java b/rest-proxy-core/src/main/java/group/flyfish/rest/annotation/EnableRestApiProxy.java
new file mode 100644
index 0000000..7daea5f
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/annotation/EnableRestApiProxy.java
@@ -0,0 +1,25 @@
+package group.flyfish.rest.annotation;
+
+import group.flyfish.rest.configuration.RestClientConfiguration;
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.*;
+
+/**
+ * 启用restapi自动代理
+ *
+ * @author wangyu
+ */
+@Import(RestClientConfiguration.class)
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface EnableRestApiProxy {
+
+ /**
+ * 基本扫描路径
+ *
+ * @return 结果
+ */
+ String[] basePackages() default "group.flyfish.rest";
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/client/RestClient.java b/rest-proxy-core/src/main/java/group/flyfish/rest/client/RestClient.java
new file mode 100644
index 0000000..03ff79c
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/client/RestClient.java
@@ -0,0 +1,323 @@
+package group.flyfish.rest.client;
+
+import group.flyfish.rest.core.builder.RestClientBuilder;
+import group.flyfish.rest.core.exception.RestClientException;
+import group.flyfish.rest.core.produce.HttpClientProducer;
+import group.flyfish.rest.enums.ResponseType;
+import group.flyfish.rest.utils.JacksonUtil;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JavaType;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpEntity;
+import org.apache.http.StatusLine;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.springframework.lang.Nullable;
+import org.springframework.util.StreamUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.UnknownHostException;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
+
+import static group.flyfish.rest.constants.RestConstants.DEFAULT_EXECUTOR;
+
+/**
+ * Rest请求客户端
+ *
+ * @author Mr.Wang
+ *
+ * @apiNote 1. 全builder调用,用户系统内部相互通信
+ * 2. 支持异步回调
+ * 3. 多样性组合
+ * 4. 解耦实现
+ * 5. 支持上传文件、FormData,JSON、支持自定义无侵入扩展
+ * 6. 新增单例httpClient模式,复用连接让应用更高效
+ */
+@Slf4j
+public final class RestClient {
+
+ private static volatile CloseableHttpClient client;
+ private final HttpRequestBase request;
+ private boolean async = false;
+ private Consumer consumer;
+ private Consumer errorConsumer;
+ private ExecutorService executorService;
+ private ResponseType responseType = ResponseType.NORMAL;
+ private Class> resultClass;
+ private TypeReference> typeReference;
+ private JavaType resultType;
+
+ /**
+ * 内部构造方法,不对外公开
+ *
+ * @param request 请求信息
+ */
+ public RestClient(HttpRequestBase request) {
+ this.request = request;
+ }
+
+ /**
+ * 设置单例的客户端
+ *
+ * @param client 客户端
+ */
+ public static void setClient(CloseableHttpClient client) {
+ RestClient.client = client;
+ }
+
+ /**
+ * 新增一个构建器
+ *
+ * @return 结果
+ */
+ public static RestClientBuilder create() {
+ return new RestClientBuilder();
+ }
+
+ /**
+ * 设置请求失败时的回调
+ *
+ * @param errorConsumer 错误回调
+ * @return 结果
+ */
+ public RestClient onError(Consumer errorConsumer) {
+ this.errorConsumer = errorConsumer;
+ return this;
+ }
+
+ /**
+ * 错误处理
+ *
+ * @param e 异常
+ */
+ private void handleError(RestClientException e) {
+ if (null != errorConsumer) {
+ errorConsumer.accept(e);
+ } else {
+ throw e;
+ }
+ }
+
+ /**
+ * 设置响应类型
+ *
+ * @param responseType 响应类型
+ * @return 结果
+ */
+ public RestClient responseType(ResponseType responseType) {
+ this.responseType = responseType;
+ return this;
+ }
+
+ /**
+ * 标记线程池执行
+ *
+ * @return 结果
+ */
+ public RestClient async() {
+ this.async = true;
+ this.executorService = DEFAULT_EXECUTOR;
+ return this;
+ }
+
+ /**
+ * 标记指定线程池执行
+ *
+ * @param executorService 线程池
+ * @return 结果
+ */
+ public RestClient async(ExecutorService executorService) {
+ this.async = true;
+ this.executorService = executorService;
+ return this;
+ }
+
+ /**
+ * 异步执行,接收结果
+ *
+ * @param consumer 结果
+ */
+ public void execute(Consumer consumer) {
+ this.consumer = consumer;
+ if (this.async) {
+ if (this.executorService == null) {
+ handleError(new RestClientException("线程池未指定或为空!"));
+ }
+ this.executorService.submit(this::executeSafety);
+ } else {
+ executeSafety();
+ }
+ }
+
+ /**
+ * 静默执行,抛弃全部异常
+ */
+ public void executeSilent() {
+ executeSafety();
+ }
+
+ /**
+ * 执行请求,返回Map
+ *
+ * @return map
+ * @throws IOException 异常
+ */
+ public Map executeForMap() throws IOException {
+ this.responseType = ResponseType.JSON;
+ return innerExecute();
+ }
+
+ /**
+ * 执行请求,返回字符串
+ *
+ * @return 字符串
+ * @throws IOException 异常
+ */
+ public String executeForString() throws IOException {
+ this.responseType = ResponseType.TEXT;
+ return innerExecute();
+ }
+
+ /**
+ * 安全的执行
+ */
+ private void executeSafety() {
+ try {
+ execute();
+ } catch (IOException e) {
+ handleError(new RestClientException(e.getMessage(), e, null));
+ }
+ }
+
+ /**
+ * 执行并序列化,该方法会安全的返回对象或者空
+ *
+ * @param clazz 类
+ * @param 泛型
+ * @return 结果
+ */
+ @Nullable
+ public T execute(Class clazz) {
+ this.responseType = ResponseType.OBJECT;
+ this.resultClass = clazz;
+ try {
+ return innerExecute();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 执行并序列化,使用复杂的自动构造的类型
+ *
+ * @param type jackson的强化类型
+ * @param 泛型
+ * @return 结果
+ */
+ @Nullable
+ public T execute(JavaType type) {
+ this.responseType = ResponseType.OBJECT;
+ this.resultType = type;
+ try {
+ return innerExecute();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Nullable
+ public T execute(TypeReference typeReference) {
+ this.responseType = ResponseType.OBJECT;
+ this.typeReference = typeReference;
+ try {
+ return innerExecute();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 执行请求,返回响应实体,自行处理
+ *
+ * @return 响应实体
+ * @throws IOException 异常
+ */
+ public T execute() throws IOException {
+ return innerExecute();
+ }
+
+ /**
+ * 内部执行方法,预处理结果
+ *
+ * @param 泛型
+ * @return 结果
+ */
+ private T innerExecute() throws IOException {
+ if (null == client) {
+ client = HttpClientProducer.produce();
+ }
+ log.info("【Rest Invoke】{} {}", request.getMethod(), request.getURI());
+ try (CloseableHttpResponse response = client.execute(request)) {
+ StatusLine statusLine = response.getStatusLine();
+ if (200 == statusLine.getStatusCode()) {
+ HttpEntity entity = response.getEntity();
+ if (consumer != null) {
+ consumer.accept(entity);
+ }
+ return resolveResponse(entity);
+ } else {
+ int requestCode = statusLine.getStatusCode();
+ log.error(request.getURI() + "接口调用失败,code:" + requestCode);
+ handleError(new RestClientException("网络请求状态异常!代码:" + requestCode, null, statusLine));
+ }
+ } catch (UnknownHostException e) {
+ handleError(new RestClientException(e.getMessage(), e, null));
+ log.error("未知的请求地址!");
+ } finally {
+ request.releaseConnection();
+ }
+ return null;
+ }
+
+ /**
+ * 解析结果
+ *
+ * @param entity 响应体
+ * @param 泛型
+ * @return 结果
+ */
+ @SuppressWarnings("unchecked")
+ private T resolveResponse(HttpEntity entity) throws IOException {
+ switch (responseType) {
+ case TEXT:
+ return (T) EntityUtils.toString(entity);
+ case JSON:
+ return (T) JacksonUtil.json2Map(EntityUtils.toString(entity));
+ case BINARY:
+ try (InputStream in = entity.getContent()) {
+ return (T) StreamUtils.copyToByteArray(in);
+ }
+ case OBJECT:
+ if (null != this.resultClass) {
+ return (T) JacksonUtil.fromJson(EntityUtils.toString(entity), this.resultClass);
+ }
+ if (null != this.typeReference) {
+ return (T) JacksonUtil.fromJson(EntityUtils.toString(entity), this.typeReference);
+ }
+ if (null != this.resultType) {
+ return (T) JacksonUtil.fromJson(EntityUtils.toString(entity), this.resultType);
+ }
+ default:
+ return (T) entity;
+ }
+ }
+
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/configuration/RestClientConfiguration.java b/rest-proxy-core/src/main/java/group/flyfish/rest/configuration/RestClientConfiguration.java
new file mode 100644
index 0000000..da67a01
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/configuration/RestClientConfiguration.java
@@ -0,0 +1,77 @@
+package group.flyfish.rest.configuration;
+
+import group.flyfish.rest.annotation.EnableRestApiProxy;
+import group.flyfish.rest.mapping.RestResultMapping;
+import group.flyfish.rest.registry.RestApiRegistry;
+import group.flyfish.rest.registry.proxy.support.RestArgumentResolver;
+import group.flyfish.rest.registry.proxy.support.RestArgumentResolverComposite;
+import group.flyfish.rest.registry.proxy.support.resolvers.RestBodyArgumentResolver;
+import group.flyfish.rest.registry.proxy.support.resolvers.RestHeaderArgumentResolver;
+import group.flyfish.rest.registry.proxy.support.resolvers.RestParamArgumentResolver;
+import group.flyfish.rest.registry.proxy.support.resolvers.RestPathParamArgumentResolver;
+import group.flyfish.rest.utils.DataUtils;
+import group.flyfish.rest.utils.IocBeans;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.context.annotation.Lazy;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * rest请求相关配置
+ *
+ * @author wangyu
+ */
+@EnableConfigurationProperties(RestClientProperties.class)
+public class RestClientConfiguration {
+
+ /**
+ * 注册rest自动代理
+ *
+ * @return 结果
+ */
+ @Bean
+ @Lazy
+ public RestApiRegistry restApiRegistry(RestArgumentResolverComposite composite,
+ List mappings) {
+ // 先注册映射们
+ if (DataUtils.isNotEmpty(mappings)) {
+ mappings.forEach(mapping -> RestResultMapping.MAPPINGS.put(mapping.getClass(), mapping));
+ }
+ // 最后实例化
+ return new RestApiRegistry(composite);
+ }
+
+ /**
+ * IOC容器操作
+ *
+ * @return 结果
+ */
+ @Bean
+ @ConditionalOnMissingBean(name = "iocBeans")
+ public IocBeans iocBeans() {
+ return new IocBeans();
+ }
+
+ /**
+ * 一个很重要的bean,反向解析各种参数
+ *
+ * @return 结果
+ */
+ @Bean
+ public RestArgumentResolverComposite restArgumentResolverComposite() {
+ List resolvers = Arrays.asList(
+ new RestPathParamArgumentResolver(),
+ new RestBodyArgumentResolver(),
+ new RestHeaderArgumentResolver(),
+ new RestParamArgumentResolver()
+ );
+ return new RestArgumentResolverComposite(resolvers);
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/configuration/RestClientProperties.java b/rest-proxy-core/src/main/java/group/flyfish/rest/configuration/RestClientProperties.java
new file mode 100644
index 0000000..2fbedf8
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/configuration/RestClientProperties.java
@@ -0,0 +1,67 @@
+package group.flyfish.rest.configuration;
+
+import group.flyfish.rest.registry.RestApiRegistry;
+import group.flyfish.rest.utils.DataUtils;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import javax.annotation.Resource;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 客户端的配置
+ *
+ * @author wangyu
+ * 整合入spring boot,简化配置,提高业务复用性
+ */
+@Data
+@ConfigurationProperties(prefix = "rest.client", ignoreUnknownFields = false)
+@Slf4j
+public class RestClientProperties implements InitializingBean {
+
+ @Resource
+ private RestApiRegistry restApiRegistry;
+
+ /**
+ * 超时时间,默认30s
+ */
+ private Duration connectionTimeout = Duration.ofSeconds(30);
+
+ /**
+ * 基本url
+ */
+ private String baseUrl;
+
+ /**
+ * ssl无条件新人
+ */
+ private Boolean alwaysTrust = true;
+
+ /**
+ * 定义的内容字典,可以支持动态取值,使用#variable
+ */
+ private Map urls = new HashMap<>();
+
+ /**
+ * 获取字典url
+ *
+ * @param key 键
+ * @return 结果
+ */
+ public String getDictUrl(String key) {
+ if (DataUtils.isEmpty(urls)) {
+ return null;
+ }
+ return urls.get(key);
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ log.info("initialized rest client properties。 baseUrl is:{}", this.baseUrl);
+ restApiRegistry.initialize(this);
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/constants/RestConstants.java b/rest-proxy-core/src/main/java/group/flyfish/rest/constants/RestConstants.java
new file mode 100644
index 0000000..7161203
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/constants/RestConstants.java
@@ -0,0 +1,209 @@
+package group.flyfish.rest.constants;
+
+import group.flyfish.rest.core.ThreadPoolManager;
+import group.flyfish.rest.core.builder.TypedMapBuilder;
+import group.flyfish.rest.core.resolver.*;
+import group.flyfish.rest.enums.HttpMethod;
+import org.apache.http.client.config.RequestConfig;
+
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * rest客户端需要的常量都在这里
+ *
+ * @author wangyu
+ */
+public interface RestConstants {
+ /**
+ * 请求配置
+ */
+ RequestConfig REQUEST_CONFIG = RequestConfig.custom().setConnectTimeout(3000).build();
+ /**
+ * 请求解析器
+ */
+ Map RESOLVER_MAP = resolverBuilder()
+ .with(HttpMethod.GET, new HttpGetResolver())
+ .with(HttpMethod.POST, new HttpPostResolver())
+ .with(HttpMethod.PUT, new HttpPutResolver())
+ .with(HttpMethod.PATCH, new HttpPatchResolver())
+ .with(HttpMethod.DELETE, new HttpDeleteResolver())
+ .build();
+ /**
+ * 线程池
+ */
+ ExecutorService DEFAULT_EXECUTOR = ThreadPoolManager.defaultCachedThreadPool();
+ /**
+ * 使用的MIME,后缀映射
+ * 自定义的MIME TYPE,可以过滤
+ */
+ Map MIME_MAP = TypedMapBuilder.stringMapBuilder()
+ .with("ai", "application/postscript")
+ .with("aif", "audio/x-aiff")
+ .with("aifc", "audio/x-aiff")
+ .with("aiff", "audio/x-aiff")
+ .with("asc", "text/plain")
+ .with("au", "audio/basic")
+ .with("avi", "video/x-msvideo")
+ .with("bcpio", "application/x-bcpio")
+ .with("bin", "application/octet-stream")
+ .with("c", "text/plain")
+ .with("cc", "text/plain")
+ .with("ccad", "application/clariscad")
+ .with("cdf", "application/x-netcdf")
+ .with("class", "application/octet-stream")
+ .with("cpio", "application/x-cpio")
+ .with("cpt", "application/mac-compactpro")
+ .with("csh", "application/x-csh")
+ .with("css", "text/css")
+ .with("dcr", "application/x-director")
+ .with("dir", "application/x-director")
+ .with("dms", "application/octet-stream")
+ .with("doc", "application/msword")
+ .with("drw", "application/drafting")
+ .with("dvi", "application/x-dvi")
+ .with("dwg", "application/acad")
+ .with("dxf", "application/dxf")
+ .with("dxr", "application/x-director")
+ .with("eps", "application/postscript")
+ .with("etx", "text/x-setext")
+ .with("exe", "application/octet-stream")
+ .with("ez", "application/andrew-inset")
+ .with("f", "text/plain")
+ .with("f90", "text/plain")
+ .with("fli", "video/x-fli")
+ .with("gif", "image/gif")
+ .with("gtar", "application/x-gtar")
+ .with("gz", "application/x-gzip")
+ .with("h", "text/plain")
+ .with("hdf", "application/x-hdf")
+ .with("hh", "text/plain")
+ .with("hqx", "application/mac-binhex40")
+ .with("htm", "text/html")
+ .with("html", "text/html")
+ .with("ice", "x-conference/x-cooltalk")
+ .with("ief", "image/ief")
+ .with("iges", "model/iges")
+ .with("igs", "model/iges")
+ .with("ips", "application/x-ipscript")
+ .with("ipx", "application/x-ipix")
+ .with("jpe", "image/jpeg")
+ .with("jpeg", "image/jpeg")
+ .with("jpg", "image/jpeg")
+ .with("js", "application/x-javascript")
+ .with("kar", "audio/midi")
+ .with("latex", "application/x-latex")
+ .with("lha", "application/octet-stream")
+ .with("lsp", "application/x-lisp")
+ .with("lzh", "application/octet-stream")
+ .with("m", "text/plain")
+ .with("man", "application/x-troff-man")
+ .with("me", "application/x-troff-me")
+ .with("mesh", "model/mesh")
+ .with("mid", "audio/midi")
+ .with("midi", "audio/midi")
+ .with("mif", "application/vnd.mif")
+ .with("mime", "www/mime")
+ .with("mov", "video/quicktime")
+ .with("movie", "video/x-sgi-movie")
+ .with("mp2", "audio/mpeg")
+ .with("mp3", "audio/mpeg")
+ .with("mp4", "video/mpeg")
+ .with("mpe", "video/mpeg")
+ .with("mpeg", "video/mpeg")
+ .with("mpg", "video/mpeg")
+ .with("mpga", "audio/mpeg")
+ .with("ms", "application/x-troff-ms")
+ .with("msh", "model/mesh")
+ .with("nc", "application/x-netcdf")
+ .with("oda", "application/oda")
+ .with("pbm", "image/x-portable-bitmap")
+ .with("pdb", "chemical/x-pdb")
+ .with("pdf", "application/pdf")
+ .with("pgm", "image/x-portable-graymap")
+ .with("pgn", "application/x-chess-pgn")
+ .with("png", "image/png")
+ .with("pnm", "image/x-portable-anymap")
+ .with("pot", "application/mspowerpoint")
+ .with("ppm", "image/x-portable-pixmap")
+ .with("pps", "application/mspowerpoint")
+ .with("ppt", "application/mspowerpoint")
+ .with("ppz", "application/mspowerpoint")
+ .with("pre", "application/x-freelance")
+ .with("prt", "application/pro_eng")
+ .with("ps", "application/postscript")
+ .with("qt", "video/quicktime")
+ .with("ra", "audio/x-realaudio")
+ .with("ram", "audio/x-pn-realaudio")
+ .with("ras", "image/cmu-raster")
+ .with("rgb", "image/x-rgb")
+ .with("rm", "audio/x-pn-realaudio")
+ .with("roff", "application/x-troff")
+ .with("rpm", "audio/x-pn-realaudio-plugin")
+ .with("rtf", "text/rtf")
+ .with("rtx", "text/richtext")
+ .with("scm", "application/x-lotusscreencam")
+ .with("set", "application/set")
+ .with("sgm", "text/sgml")
+ .with("sgml", "text/sgml")
+ .with("sh", "application/x-sh")
+ .with("shar", "application/x-shar")
+ .with("silo", "model/mesh")
+ .with("sit", "application/x-stuffit")
+ .with("skd", "application/x-koan")
+ .with("skm", "application/x-koan")
+ .with("skp", "application/x-koan")
+ .with("skt", "application/x-koan")
+ .with("smi", "application/smil")
+ .with("smil", "application/smil")
+ .with("snd", "audio/basic")
+ .with("sol", "application/solids")
+ .with("spl", "application/x-futuresplash")
+ .with("src", "application/x-wais-source")
+ .with("step", "application/STEP")
+ .with("stl", "application/SLA")
+ .with("stp", "application/STEP")
+ .with("sv4cpio", "application/x-sv4cpio")
+ .with("sv4crc", "application/x-sv4crc")
+ .with("swf", "application/x-shockwave-flash")
+ .with("t", "application/x-troff")
+ .with("tar", "application/x-tar")
+ .with("tcl", "application/x-tcl")
+ .with("tex", "application/x-tex")
+ .with("texi", "application/x-texinfo")
+ .with("texinfo", "application/x-texinfo")
+ .with("tif", "image/tiff")
+ .with("tiff", "image/tiff")
+ .with("tr", "application/x-troff")
+ .with("tsi", "audio/TSP-audio")
+ .with("tsp", "application/dsptype")
+ .with("tsv", "text/tab-separated-values")
+ .with("txt", "text/plain")
+ .with("unv", "application/i-deas")
+ .with("ustar", "application/x-ustar")
+ .with("vcd", "application/x-cdlink")
+ .with("vda", "application/vda")
+ .with("viv", "video/vnd.vivo")
+ .with("vivo", "video/vnd.vivo")
+ .with("vrml", "model/vrml")
+ .with("wav", "audio/x-wav")
+ .with("wrl", "model/vrml")
+ .with("xbm", "image/x-xbitmap")
+ .with("xlc", "application/vnd.ms-excel")
+ .with("xll", "application/vnd.ms-excel")
+ .with("xlm", "application/vnd.ms-excel")
+ .with("xls", "application/vnd.ms-excel")
+ .with("xlw", "application/vnd.ms-excel")
+ .with("xml", "text/xml")
+ .with("xpm", "image/x-xpixmap")
+ .with("xwd", "image/x-xwindowdump")
+ .with("xyz", "chemical/x-pdb")
+ .with("zip", "application/zip ")
+ .with("apk", "application/vnd.android.package-archive")
+ .with("*", "application/octet-stream")
+ .build();
+
+ static TypedMapBuilder resolverBuilder() {
+ return TypedMapBuilder.builder();
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/ThreadPoolManager.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/ThreadPoolManager.java
new file mode 100644
index 0000000..4226685
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/ThreadPoolManager.java
@@ -0,0 +1,57 @@
+package group.flyfish.rest.core;
+
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * 线程池管理
+ *
+ * @author wangyu
+ * 用于管理Http异步执行池
+ */
+public class ThreadPoolManager {
+
+ public static ExecutorService defaultCachedThreadPool() {
+ return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
+ 60L, TimeUnit.SECONDS,
+ new SynchronousQueue<>(),
+ DefaultThreadFactory.createDefault());
+ }
+
+ /**
+ * 默认的线程工厂
+ */
+ private static class DefaultThreadFactory implements ThreadFactory {
+ private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
+ private final ThreadGroup group;
+ private final AtomicInteger threadNumber = new AtomicInteger(1);
+ private final String namePrefix;
+
+ private DefaultThreadFactory() {
+ SecurityManager s = System.getSecurityManager();
+ group = (s != null) ? s.getThreadGroup() :
+ Thread.currentThread().getThreadGroup();
+ namePrefix = "pool-" +
+ POOL_NUMBER.getAndIncrement() +
+ "-thread-";
+ }
+
+ private static DefaultThreadFactory createDefault() {
+ return new DefaultThreadFactory();
+ }
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(group, r,
+ namePrefix + threadNumber.getAndIncrement(),
+ 0);
+ if (t.isDaemon()) {
+ t.setDaemon(false);
+ }
+ if (t.getPriority() != Thread.NORM_PRIORITY) {
+ t.setPriority(Thread.NORM_PRIORITY);
+ }
+ return t;
+ }
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/builder/MapParamBuilder.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/builder/MapParamBuilder.java
new file mode 100644
index 0000000..55ba2e0
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/builder/MapParamBuilder.java
@@ -0,0 +1,166 @@
+package group.flyfish.rest.core.builder;
+
+import group.flyfish.rest.utils.DataUtils;
+import group.flyfish.rest.utils.JacksonUtil;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Map参数构造器
+ *
+ * @author Mr.Wang
+ * @apiNote 通过重载过滤已知类型的空值,并分别处理
+ */
+public final class MapParamBuilder {
+
+ private static final String PAGE_KEY = "pageUtil";
+
+ private static final String EMPTY_PATTERN = "{}";
+
+ private Map params;
+
+ private MapParamBuilder() {
+ this.params = new HashMap<>();
+ }
+
+ public static MapParamBuilder builder() {
+ return new MapParamBuilder();
+ }
+
+ public static MapParamBuilder of(Map initialParams) {
+ MapParamBuilder builder = new MapParamBuilder();
+ builder.withAll(initialParams);
+ return builder;
+ }
+
+ public static MapParamBuilder empty() {
+ MapParamBuilder builder = new MapParamBuilder();
+ builder.params = Collections.emptyMap();
+ return builder;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T any(Map map, String key) {
+ Object value = map.get(key);
+ return (T) value;
+ }
+
+ public MapParamBuilder with(String key, Object value) {
+ if (DataUtils.isNotBlank(key) && value != null) {
+ this.params.put(key, value);
+ }
+ return this;
+ }
+
+ public boolean has(String key) {
+ return this.params.containsKey(key);
+ }
+
+ public MapParamBuilder with(String key, Collection> value, Collection> defaultValue) {
+ if (DataUtils.isNotBlank(key) && DataUtils.isNotEmpty(value)) {
+ this.params.put(key, value);
+ } else {
+ this.params.put(key, defaultValue);
+ }
+ return this;
+ }
+
+ public MapParamBuilder with(String key, String value) {
+ if (DataUtils.isNotBlank(key) && DataUtils.isNotBlank(value)) {
+ this.params.put(key, value);
+ }
+ return this;
+ }
+
+ public MapParamBuilder with(String key, Integer value) {
+ // 过滤负值,无意义的值
+ if (DataUtils.isNotBlank(key) && value != null) {
+ this.params.put(key, value);
+ }
+ return this;
+ }
+
+ /**
+ * 交换键位对应的值
+ *
+ * @param oldKey 要被交换的key
+ * @param newKey 要交换的key
+ * @return 结果
+ */
+ public MapParamBuilder exchange(String oldKey, String newKey) {
+ if (this.params.containsKey(oldKey) && this.params.containsKey(newKey)) {
+ Object oldValue = this.params.get(oldKey);
+ Object newValue = this.params.get(newKey);
+ this.params.put(oldKey, newValue);
+ this.params.put(newKey, oldValue);
+ }
+ return this;
+ }
+
+ /**
+ * 替换key为新的key,值不变
+ *
+ * @param oldKey 旧的key
+ * @param newKey 新的key
+ * @return 结果
+ */
+ public MapParamBuilder replace(String oldKey, String newKey) {
+ Object value = this.params.get(oldKey);
+ if (null != value) {
+ this.params.remove(oldKey);
+ this.params.put(newKey, value);
+ }
+ return this;
+ }
+
+ public MapParamBuilder clear(String key) {
+ this.params.remove(key);
+ return this;
+ }
+
+ public MapParamBuilder with(String key, Long value) {
+ // 过滤负值,无意义的值
+ if (DataUtils.isNotBlank(key) && value != null) {
+ this.params.put(key, value);
+ }
+ return this;
+ }
+
+ public MapParamBuilder withAll(Map params) {
+ if (DataUtils.isNotEmpty(params)) {
+ params.forEach(this::with);
+ }
+ return this;
+ }
+
+ public MapParamBuilder withPage(Map params) {
+ if (params.containsKey(PAGE_KEY)) {
+ this.params.put("page", params.get(PAGE_KEY));
+ }
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T take(String key) {
+ return (T) this.params.get(key);
+ }
+
+ public boolean isEmpty() {
+ return DataUtils.isEmpty(params);
+ }
+
+ public Map build() {
+ return this.params;
+ }
+
+ @Override
+ public String toString() {
+ if (DataUtils.isEmpty(this.params)) {
+ return EMPTY_PATTERN;
+ }
+ return JacksonUtil.toJson(this.params).orElse(null);
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/builder/RestClientBuilder.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/builder/RestClientBuilder.java
new file mode 100644
index 0000000..11c8d0c
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/builder/RestClientBuilder.java
@@ -0,0 +1,199 @@
+package group.flyfish.rest.core.builder;
+
+import group.flyfish.rest.client.RestClient;
+import group.flyfish.rest.core.entity.Multipart;
+import group.flyfish.rest.enums.HttpMethod;
+import group.flyfish.rest.utils.DataUtils;
+import group.flyfish.rest.utils.JacksonUtil;
+import group.flyfish.rest.utils.RequestContext;
+import org.apache.http.client.methods.HttpRequestBase;
+
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static group.flyfish.rest.constants.RestConstants.REQUEST_CONFIG;
+import static group.flyfish.rest.constants.RestConstants.RESOLVER_MAP;
+
+/**
+ * 主要的builder,核心构建
+ *
+ * @author wangyu
+ */
+public class RestClientBuilder {
+
+ private String url;
+
+ private HttpMethod method = HttpMethod.GET;
+
+ private Map params;
+
+ private String body;
+
+ private Map headers;
+
+ private List multipartList;
+
+ private boolean multipart;
+
+ private boolean credential;
+
+ private String charset;
+
+ public String getUrl() {
+ return url;
+ }
+
+ public RestClientBuilder url(String url) {
+ this.url = url;
+ return this;
+ }
+
+ public HttpMethod getMethod() {
+ return method;
+ }
+
+ public RestClientBuilder method(HttpMethod method) {
+ this.method = method;
+ return this;
+ }
+
+ public RestClientBuilder get() {
+ this.method = HttpMethod.GET;
+ return this;
+ }
+
+ public RestClientBuilder post() {
+ this.method = HttpMethod.POST;
+ return this;
+ }
+
+ public RestClientBuilder multipart() {
+ this.multipart = true;
+ return this;
+ }
+
+ public boolean isMultipart() {
+ return multipart;
+ }
+
+ public Map getParams() {
+ if (null == params) {
+ params = new HashMap<>();
+ }
+ return params;
+ }
+
+ public RestClientBuilder queryParams(Map params) {
+ this.params = params;
+ return this;
+ }
+
+ public RestClientBuilder addParam(String key, Object value) {
+ if (null == this.params) {
+ this.params = new HashMap<>();
+ }
+ this.params.put(key, value);
+ return this;
+ }
+
+ public RestClientBuilder charset(String charset) {
+ this.charset = charset;
+ return this;
+ }
+
+ public RestClientBuilder withCredential() {
+ this.credential = true;
+ return this;
+ }
+
+ public Charset getCharset() {
+ return DataUtils.isBlank(charset) ? Charset.defaultCharset() : Charset.forName(charset);
+ }
+
+ public RestClientBuilder addMultipartBody(String name, String filename, Object data) {
+ if (null == this.multipartList) {
+ this.multipartList = new ArrayList<>();
+ }
+ this.multipartList.add(new Multipart(name, filename, data));
+ return this;
+ }
+
+ public List getMultipartList() {
+ if (null == multipartList) {
+ multipartList = new ArrayList<>();
+ }
+ return multipartList;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ public RestClientBuilder body(String body) {
+ this.body = body;
+ return this;
+ }
+
+ public RestClientBuilder body(Object body) {
+ this.body = JacksonUtil.toJson(body).orElse(null);
+ return this;
+ }
+
+ public Map getHeaders() {
+ if (null == headers) {
+ headers = new HashMap<>();
+ }
+ return headers;
+ }
+
+ public RestClientBuilder headers(Map headers) {
+ this.headers = headers;
+ return this;
+ }
+
+ public RestClientBuilder addHeader(String key, String value) {
+ if (null == this.headers) {
+ this.headers = new HashMap<>();
+ }
+ this.headers.put(key, value);
+ return this;
+ }
+
+ /**
+ * 匹配解析器
+ *
+ * @return 结果
+ */
+ private HttpRequestBase buildRequest() {
+ HttpRequestBase request = RESOLVER_MAP.getOrDefault(this.method, RESOLVER_MAP.get(HttpMethod.GET))
+ .resolve(this);
+ // 添加token凭证
+ if (credential) {
+ RequestContext.getCredential().ifPresent(value -> this.addHeader(
+ RequestContext.AUTHORIZATION_KEY, value)
+ );
+ }
+ // 添加头
+ getHeaders().forEach(request::addHeader);
+ // 设置公共设置
+ request.setConfig(REQUEST_CONFIG);
+ // 返回
+ return request;
+ }
+
+ /**
+ * 构建client
+ *
+ * @return 结果
+ */
+ public RestClient build() {
+ // 创建请求
+ HttpRequestBase request = buildRequest();
+ return new RestClient(request);
+ }
+
+
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/builder/TypedMapBuilder.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/builder/TypedMapBuilder.java
new file mode 100644
index 0000000..84cd495
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/builder/TypedMapBuilder.java
@@ -0,0 +1,54 @@
+package group.flyfish.rest.core.builder;
+
+import group.flyfish.rest.utils.JacksonUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 有具体泛型类型的Map构建器
+ * 提供基本的非空校验
+ *
+ * @author Mr.Wang
+ */
+public final class TypedMapBuilder {
+
+ private final Map params;
+
+ private TypedMapBuilder() {
+ this.params = new HashMap<>();
+ }
+
+ public static TypedMapBuilder builder() {
+ return new TypedMapBuilder<>();
+ }
+
+ public static TypedMapBuilder stringMapBuilder() {
+ return new TypedMapBuilder<>();
+ }
+
+ public static TypedMapBuilder stringObjectBuilder() {
+ return new TypedMapBuilder<>();
+ }
+
+ public TypedMapBuilder with(K key, V value) {
+ if (key != null && value != null) {
+ this.params.put(key, value);
+ }
+ return this;
+ }
+
+ public TypedMapBuilder withAll(Map values) {
+ values.forEach(this::with);
+ return this;
+ }
+
+ public Map build() {
+ return params;
+ }
+
+ @Override
+ public String toString() {
+ return JacksonUtil.toJson(this).orElse(null);
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/entity/Multipart.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/entity/Multipart.java
new file mode 100644
index 0000000..c713520
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/entity/Multipart.java
@@ -0,0 +1,22 @@
+package group.flyfish.rest.core.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 存储文件上传的part
+ *
+ * @author wangyu
+ */
+@Getter
+@Setter
+@AllArgsConstructor
+public class Multipart {
+
+ private String name;
+
+ private String filename;
+
+ private Object data;
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/exception/RestClientException.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/exception/RestClientException.java
new file mode 100644
index 0000000..9e1aa56
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/exception/RestClientException.java
@@ -0,0 +1,40 @@
+package group.flyfish.rest.core.exception;
+
+/**
+ * 异常类,用于包装异常
+ */
+public class RestClientException extends RuntimeException {
+
+ private static final long serialVersionUID = 4741281547788724661L;
+ private Exception nested;
+
+ private Object bind;
+
+ public RestClientException(String message, Exception nested) {
+ super(message);
+ this.nested = nested;
+ }
+
+ public RestClientException(String message, Exception nested, Object bind) {
+ super(message);
+ this.nested = nested;
+ this.bind = bind;
+ }
+
+ public RestClientException(String message) {
+ super(message);
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getBind() {
+ return (T) bind;
+ }
+
+ public void setBind(Object bind) {
+ this.bind = bind;
+ }
+
+ public Exception getNested() {
+ return nested;
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/produce/HttpClientProducer.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/produce/HttpClientProducer.java
new file mode 100644
index 0000000..2a0035c
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/produce/HttpClientProducer.java
@@ -0,0 +1,101 @@
+package group.flyfish.rest.core.produce;
+
+import group.flyfish.rest.configuration.RestClientProperties;
+import group.flyfish.rest.utils.DataUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+
+import javax.net.ssl.SSLContext;
+import java.io.NotActiveException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * 生产httpClient
+ *
+ * @author wangyu
+ */
+@Slf4j
+public final class HttpClientProducer {
+
+ // 使用非公平锁
+ private static final ReentrantLock lock = new ReentrantLock();
+ // 客户端实例,单例
+ private static volatile CloseableHttpClient client;
+ // 配置,配置没进来就不初始化
+ private static RestClientProperties properties;
+
+ /**
+ * 配置信息
+ *
+ * @param properties 属性
+ */
+ public static void configure(RestClientProperties properties) {
+ HttpClientProducer.properties = properties;
+ }
+
+ /**
+ * 获取属性
+ *
+ * @return 结果
+ */
+ public static RestClientProperties getProperties() {
+ return properties;
+ }
+
+ /**
+ * 生产客户端
+ *
+ * @return 结果
+ */
+ public static CloseableHttpClient produce() throws NotActiveException {
+ if (properties == null) {
+ log.warn("【rest客户端】未初始化rest客户端配置,生产者未就绪!将使用默认配置!!!");
+ properties = new RestClientProperties();
+ }
+ if (client == null) {
+ // 非公平锁,二次判定,定位volatile
+ lock.lock();
+ try {
+ if (client == null) {
+ client = getClient();
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+ return client;
+ }
+
+ /**
+ * 构建单例的httpClient
+ *
+ * @return 结果
+ */
+ private static CloseableHttpClient getClient() {
+ return DataUtils.isTrue(properties.getAlwaysTrust()) ? createSSLClient() : HttpClients.createDefault();
+ }
+
+ /**
+ * 不信任的证书请求客户端
+ *
+ * @return 结果
+ */
+ private static CloseableHttpClient createSSLClient() {
+ //信任所有
+ try {
+ SSLContext context = SSLContextBuilder.create().loadTrustMaterial(null, (arg0, arg1) -> true).build();
+ SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(context);
+ return HttpClients.custom().setSSLSocketFactory(factory).build();
+ } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpDeleteResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpDeleteResolver.java
new file mode 100644
index 0000000..5769543
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpDeleteResolver.java
@@ -0,0 +1,26 @@
+package group.flyfish.rest.core.resolver;
+
+import group.flyfish.rest.core.builder.RestClientBuilder;
+import group.flyfish.rest.core.resolver.support.AbstractParamResolver;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpRequestBase;
+
+/**
+ * 删除请求的解析器
+ *
+ * @author wangyu
+ */
+public class HttpDeleteResolver extends AbstractParamResolver implements HttpMethodResolver {
+
+ /**
+ * 解析请求
+ *
+ * @param builder 构建器
+ * @return 结果
+ */
+ @Override
+ public HttpRequestBase resolve(RestClientBuilder builder) {
+ resolveParams(builder);
+ return new HttpDelete(builder.getUrl());
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpGetResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpGetResolver.java
new file mode 100644
index 0000000..4b52feb
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpGetResolver.java
@@ -0,0 +1,18 @@
+package group.flyfish.rest.core.resolver;
+
+import group.flyfish.rest.core.builder.RestClientBuilder;
+import group.flyfish.rest.core.resolver.support.AbstractParamResolver;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpRequestBase;
+
+/**
+ * Get方法解析参数的解析器
+ */
+public class HttpGetResolver extends AbstractParamResolver implements HttpMethodResolver {
+
+ @Override
+ public HttpRequestBase resolve(RestClientBuilder builder) {
+ resolveParams(builder);
+ return new HttpGet(builder.getUrl());
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpMethodResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpMethodResolver.java
new file mode 100644
index 0000000..f62fad0
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpMethodResolver.java
@@ -0,0 +1,18 @@
+package group.flyfish.rest.core.resolver;
+
+import group.flyfish.rest.core.builder.RestClientBuilder;
+import org.apache.http.client.methods.HttpRequestBase;
+
+/**
+ * Http请求解析器
+ */
+public interface HttpMethodResolver {
+
+ /**
+ * 解析请求
+ *
+ * @param builder 构建器
+ * @return 结果
+ */
+ HttpRequestBase resolve(RestClientBuilder builder);
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpPatchResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpPatchResolver.java
new file mode 100644
index 0000000..7c61077
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpPatchResolver.java
@@ -0,0 +1,27 @@
+package group.flyfish.rest.core.resolver;
+
+import group.flyfish.rest.core.builder.RestClientBuilder;
+import group.flyfish.rest.core.resolver.support.AbstractBodyResolver;
+import org.apache.http.client.methods.HttpPatch;
+import org.apache.http.client.methods.HttpRequestBase;
+
+/**
+ * patch请求的解析器
+ *
+ * @author wangyu
+ */
+public class HttpPatchResolver extends AbstractBodyResolver implements HttpMethodResolver {
+
+ /**
+ * 解析请求
+ *
+ * @param builder 构建器
+ * @return 结果
+ */
+ @Override
+ public HttpRequestBase resolve(RestClientBuilder builder) {
+ HttpPatch httpPatch = new HttpPatch(builder.getUrl());
+ httpPatch.setEntity(buildEntity(builder));
+ return httpPatch;
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpPostResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpPostResolver.java
new file mode 100644
index 0000000..333b5b8
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpPostResolver.java
@@ -0,0 +1,20 @@
+package group.flyfish.rest.core.resolver;
+
+import group.flyfish.rest.core.builder.RestClientBuilder;
+import group.flyfish.rest.core.resolver.support.AbstractBodyResolver;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
+
+/**
+ * Post方法解析参数的解析器,包括上传
+ */
+public class HttpPostResolver extends AbstractBodyResolver implements HttpMethodResolver {
+
+ @Override
+ public HttpRequestBase resolve(RestClientBuilder builder) {
+ HttpPost post = new HttpPost(builder.getUrl());
+ post.setEntity(buildEntity(builder));
+ return post;
+ }
+
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpPutResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpPutResolver.java
new file mode 100644
index 0000000..6812105
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/HttpPutResolver.java
@@ -0,0 +1,27 @@
+package group.flyfish.rest.core.resolver;
+
+import group.flyfish.rest.core.builder.RestClientBuilder;
+import group.flyfish.rest.core.resolver.support.AbstractBodyResolver;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpRequestBase;
+
+/**
+ * put请求解析器
+ *
+ * @author wangyu
+ */
+public class HttpPutResolver extends AbstractBodyResolver implements HttpMethodResolver {
+
+ /**
+ * 解析请求
+ *
+ * @param builder 构建器
+ * @return 结果
+ */
+ @Override
+ public HttpRequestBase resolve(RestClientBuilder builder) {
+ HttpPut post = new HttpPut(builder.getUrl());
+ post.setEntity(buildEntity(builder));
+ return post;
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/support/AbstractBodyResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/support/AbstractBodyResolver.java
new file mode 100644
index 0000000..d223830
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/support/AbstractBodyResolver.java
@@ -0,0 +1,104 @@
+package group.flyfish.rest.core.resolver.support;
+
+import group.flyfish.rest.core.builder.RestClientBuilder;
+import group.flyfish.rest.utils.DataUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.message.BasicNameValuePair;
+
+import java.io.File;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static group.flyfish.rest.constants.RestConstants.MIME_MAP;
+
+/**
+ * 抽象的请求体解析
+ *
+ * @author wangyu
+ */
+public abstract class AbstractBodyResolver {
+
+ /**
+ * 构建entity
+ *
+ * @param builder 构建器
+ * @return 结果
+ */
+ protected HttpEntity buildEntity(RestClientBuilder builder) {
+ return builder.isMultipart() ? buildMultipart(builder)
+ : DataUtils.isNotBlank(builder.getBody()) ? buildJson(builder)
+ : buildFormData(builder);
+ }
+
+ /**
+ * 构建上传数据
+ *
+ * @param clientBuilder builder
+ * @return 结果
+ */
+ private HttpEntity buildMultipart(RestClientBuilder clientBuilder) {
+ MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+ builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
+ clientBuilder.getMultipartList().forEach(multipart -> {
+ Object data = multipart.getData();
+ String name = multipart.getName();
+ String filename = multipart.getFilename();
+ if (data instanceof byte[]) {
+ builder.addBinaryBody(name, (byte[]) data,
+ ContentType.create(MIME_MAP.getOrDefault(FilenameUtils.getExtension(filename),
+ ContentType.APPLICATION_OCTET_STREAM.getMimeType())), filename);
+ } else if (data instanceof File) {
+ builder.addBinaryBody(name, (File) data,
+ ContentType.create(MIME_MAP.getOrDefault(FilenameUtils.getExtension(filename),
+ ContentType.APPLICATION_OCTET_STREAM.getMimeType())), filename);
+ } else {
+ throw new IllegalArgumentException("上传时,输入的数据不被支持!");
+ }
+ });
+ return builder.build();
+ }
+
+ /**
+ * 构建JSON方式的POST
+ *
+ * @param clientBuilder builder
+ * @return 结果
+ */
+ private HttpEntity buildJson(RestClientBuilder clientBuilder) {
+ clientBuilder.addHeader("Content-Type", "application/json;charset=UTF-8");
+ Charset charset = clientBuilder.getCharset();
+ StringEntity entity = new StringEntity(clientBuilder.getBody(), charset);
+ entity.setContentEncoding(charset.toString());
+ entity.setContentType("application/json");
+ return entity;
+ }
+
+ /**
+ * 构建formdata
+ *
+ * @param clientBuilder builder
+ * @return 结果
+ */
+ private HttpEntity buildFormData(RestClientBuilder clientBuilder) {
+ // 设置参数
+ Map params = clientBuilder.getParams();
+ List list = params.keySet()
+ .stream()
+ .filter(key -> null != params.get(key))
+ .map(key -> new BasicNameValuePair(key, String.valueOf(params.get(key))))
+ .collect(Collectors.toList());
+ if (DataUtils.isNotEmpty(list)) {
+ return new UrlEncodedFormEntity(list, clientBuilder.getCharset());
+ }
+ return null;
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/support/AbstractParamResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/support/AbstractParamResolver.java
new file mode 100644
index 0000000..a6ea872
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/support/AbstractParamResolver.java
@@ -0,0 +1,29 @@
+package group.flyfish.rest.core.resolver.support;
+
+import group.flyfish.rest.core.builder.RestClientBuilder;
+import group.flyfish.rest.utils.DataUtils;
+
+import java.util.stream.Collectors;
+
+/**
+ * 抽象的参数解析逻辑
+ *
+ * @author wangyu
+ */
+public abstract class AbstractParamResolver {
+
+ /**
+ * 解析参数
+ *
+ * @param builder 构建器
+ */
+ protected void resolveParams(RestClientBuilder builder) {
+ if (DataUtils.isNotEmpty(builder.getParams())) {
+ String start = builder.getUrl().contains("?") ? "&" : "?";
+ String params = builder.getParams().entrySet().stream()
+ .map(entry -> entry.getKey() + "=" + (null == entry.getValue() ? "" : entry.getValue()))
+ .collect(Collectors.joining("&"));
+ builder.url(builder.getUrl() + start + params);
+ }
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/RestApiRegistry.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/RestApiRegistry.java
new file mode 100644
index 0000000..fd562e2
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/RestApiRegistry.java
@@ -0,0 +1,136 @@
+package group.flyfish.rest.registry;
+
+import group.flyfish.rest.annotation.EnableRestApiProxy;
+import group.flyfish.rest.annotation.RestService;
+import group.flyfish.rest.client.RestClient;
+import group.flyfish.rest.configuration.RestClientProperties;
+import group.flyfish.rest.core.produce.HttpClientProducer;
+import group.flyfish.rest.registry.proxy.RestProxyInvoker;
+import group.flyfish.rest.registry.proxy.support.RestArgumentResolverComposite;
+import group.flyfish.rest.utils.DataUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.reflections.Reflections;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.*;
+import org.springframework.util.Assert;
+
+import java.io.NotActiveException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * rest接口注册机
+ *
+ * @author wangyu
+ */
+@RequiredArgsConstructor
+@Slf4j
+public class RestApiRegistry implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {
+
+ private final RestArgumentResolverComposite composite;
+
+ // 包名
+ private final List packageNames = new ArrayList<>();
+
+ // 注册的执行器们,最终会使用配置进行初始化
+ private final List> invokers = new ArrayList<>();
+
+ // bean工厂
+ private ConfigurableListableBeanFactory beanFactory;
+
+ // rest请求参数
+ private RestClientProperties properties;
+
+ /**
+ * 动态注册bean
+ *
+ * @param registry 注册机
+ * @throws BeansException 异常
+ */
+ @Override
+ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
+ // 找基本包,找不到立马报错
+ beanFactory.getBeansWithAnnotation(EnableRestApiProxy.class)
+ .forEach((key, value) -> {
+ EnableRestApiProxy proxy = value.getClass().getAnnotation(EnableRestApiProxy.class);
+ for (String basePackage : proxy.basePackages()) {
+ if (DataUtils.isNotBlank(basePackage)) {
+ packageNames.add(basePackage);
+ }
+ }
+ });
+ // 不为空时查找
+ if (DataUtils.isNotEmpty(packageNames)) {
+ // 初始化反射
+ try {
+ Reflections reflections = new Reflections(packageNames.toArray());
+ // 得到Resource注解的类
+ Set> classSet = reflections.getTypesAnnotatedWith(RestService.class);
+ // 代理并生成子类,并注册到ioc容器
+ classSet.forEach(clazz -> registry.registerBeanDefinition(clazz.getName(),
+ BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> DataUtils.cast(RestProxyInvoker
+ .produce(clazz, composite, this::bindInvoker)))
+ .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE).getRawBeanDefinition())
+ );
+ } catch (IllegalStateException e) {
+ log.error("初始化Rest映射时出错", e);
+ }
+ return;
+ }
+ throw new BeanDefinitionValidationException("【RestApi】EnableRestApiProxy注解必须指定有效的basePackage!");
+ }
+
+ /**
+ * 初始化bean们
+ *
+ * @param properties 属性
+ * @throws NotActiveException 未活动异常
+ */
+ public void initialize(RestClientProperties properties) throws NotActiveException {
+ log.info("bean加载完成");
+ // 真正加载完成的回调
+ this.properties = properties;
+ // 配置参数
+ HttpClientProducer.configure(properties);
+ // 最后再配置,生成
+ RestClient.setClient(HttpClientProducer.produce());
+ // 初始化
+ this.invokers.forEach(invoker -> invoker.configure(properties));
+ }
+
+ /**
+ * 绑定执行器
+ *
+ * @param invoker 执行器
+ * @param 泛型
+ */
+ private void bindInvoker(RestProxyInvoker invoker) {
+ if (properties == null) {
+ this.invokers.add(invoker);
+ } else {
+ invoker.configure(properties);
+ }
+ }
+
+ @SneakyThrows
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ // do nothing
+ }
+
+ public void setBeanFactory(ConfigurableListableBeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ Assert.isTrue(beanFactory instanceof ConfigurableListableBeanFactory, "当前bean factory不被支持!");
+ this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/RestService.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/RestService.java
new file mode 100644
index 0000000..8a2ff0d
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/RestService.java
@@ -0,0 +1,12 @@
+package group.flyfish.rest.registry;
+
+/**
+ * 用于标记rest服务代理
+ *
+ * @author wangyu
+ * @deprecated 该类已经过时,请使用新版的注解声明
+ * @see group.flyfish.rest.annotation.RestService
+ */
+@Deprecated
+public interface RestService {
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/RestProxyInvoker.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/RestProxyInvoker.java
new file mode 100644
index 0000000..3ae9727
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/RestProxyInvoker.java
@@ -0,0 +1,232 @@
+package group.flyfish.rest.registry.proxy;
+
+import group.flyfish.rest.annotation.AutoMapping;
+import group.flyfish.rest.annotation.RestApi;
+import group.flyfish.rest.annotation.RestService;
+import group.flyfish.rest.client.RestClient;
+import group.flyfish.rest.configuration.RestClientProperties;
+import group.flyfish.rest.core.builder.RestClientBuilder;
+import group.flyfish.rest.mapping.RestResultMapping;
+import group.flyfish.rest.registry.proxy.support.ArgumentResolveContext;
+import group.flyfish.rest.registry.proxy.support.RestArgumentResolverComposite;
+import group.flyfish.rest.registry.proxy.support.UrlCompiler;
+import group.flyfish.rest.registry.wrapper.DefaultRestResultMapping;
+import group.flyfish.rest.utils.DataUtils;
+import group.flyfish.rest.utils.JacksonUtil;
+import com.fasterxml.jackson.databind.JavaType;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.util.ClassUtils;
+
+import java.beans.Transient;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+/**
+ * Rest代理执行器
+ *
+ * @author wangyu
+ */
+public class RestProxyInvoker implements InvocationHandler {
+
+ // 要代理的目标类
+ private final Class targetType;
+ // 解析集合
+ private final RestArgumentResolverComposite composite;
+ // 初始的基本路径
+ private String baseUrl;
+ // 基础url
+ private RestClientProperties properties;
+ // 结果映射
+ private RestResultMapping mapping;
+
+ /**
+ * 构造器
+ *
+ * @param targetType 目标类型
+ * @param composite 解析器
+ */
+ public RestProxyInvoker(Class targetType, RestArgumentResolverComposite composite) {
+ this.targetType = targetType;
+ this.composite = composite;
+ }
+
+ /**
+ * 生产一个实现类
+ *
+ * @param target 目标
+ * @param 泛型
+ * @return 结果
+ */
+ public static T produce(Class target, RestArgumentResolverComposite composite, Consumer> consumer) {
+ RestProxyInvoker invoker = new RestProxyInvoker<>(target, composite);
+ consumer.accept(invoker);
+ return DataUtils.cast(Proxy.newProxyInstance(target.getClassLoader(), new Class[]{target}, invoker));
+ }
+
+ /**
+ * 使用配置信息对接口进行配置
+ *
+ * @param properties 配置
+ */
+ public void configure(RestClientProperties properties) {
+ this.properties = properties;
+ this.baseUrl = this.findBaseUrl();
+ }
+
+
+ /**
+ * 执行rest请求的地方,这里很简单易懂
+ *
+ * @param proxy 代理对象
+ * @param method 代理方法
+ * @param args 参数
+ * @return 结果
+ * @throws Throwable 可能抛出的异常
+ */
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ // 处理基本方法被代理的情况
+ if (AopUtils.isEqualsMethod(method)) {
+ Object obj = args[0];
+ // The target does not implement the equals(Object) method itself.
+ return null != obj && ClassUtils.isAssignable(targetType, obj.getClass());
+ } else if (AopUtils.isHashCodeMethod(method)) {
+ // The target does not implement the hashCode() method itself.
+ return -1;
+ }
+ // 无视proxy,因为啥也没
+ RestApi restApi = AnnotationUtils.findAnnotation(method, RestApi.class);
+ if (null == restApi) {
+ throw new IllegalAccessException("【Rest调用】未声明rest配置的方法被调用!请检查代码!");
+ }
+ // 第一步就解析参数
+ ArgumentResolveContext context = composite.resolve(restApi, method, args);
+ // 构造和调用,这里的restClient不保存状态
+ RestClientBuilder builder = RestClient.create()
+ .url(determineUrl(restApi, context))
+ .method(restApi.method());
+ // 需要带cookie的带上
+ if (restApi.credentials()) {
+ builder.withCredential();
+ }
+ // 判断情况,赋值参数
+ if (context.hasBody()) {
+ builder.body(context.getBody());
+ }
+ // 赋值参数们
+ if (context.hasParams()) {
+ builder.queryParams(context.getParam());
+ }
+ // 赋值头
+ if (context.hasHeaders()) {
+ builder.headers(context.getHeaders());
+ }
+ // 赋值头部
+ RestClient client = builder.build();
+ // 是否对结果进行映射
+ boolean map = null != mapping && null == AnnotationUtils.findAnnotation(method, Transient.class);
+ // 执行请求
+ Object result = execute(client, method, map);
+ // 结果映射
+ return map ? mapping.map(result) : result;
+ }
+
+ /**
+ * 最终执行的方法
+ *
+ * @param client rest客户端实例
+ * @param method 原方法实例
+ * @param map 是否映射结果
+ * @return 结果
+ */
+ private Object execute(RestClient client, Method method, boolean map) throws IOException {
+ // 构建带泛型的返回值,自动判断是否是简单类型
+ JavaType constructed = JacksonUtil.getMapper().constructType(method.getGenericReturnType());
+ // 特殊处理映射
+ if (map) {
+ Type resolved = mapping.resolve(constructed);
+ // 返回java泛型
+ if (resolved instanceof JavaType) {
+ return client.execute((JavaType) resolved);
+ } else if (resolved instanceof Class) {
+ // 简单类型
+ return client.execute((Class>) resolved);
+ }
+ }
+ // 优先构建,对于map来说,只支持模糊map,否则可能会报错,直接返回
+ if (ClassUtils.isAssignable(Map.class, method.getReturnType())) {
+ return client.executeForMap();
+ }
+ // 不是map,直接返回构建结果类型
+ return client.execute(constructed);
+ }
+
+ /**
+ * 找到配置固化的基本url
+ */
+ private String findBaseUrl() {
+ // 注解的优先级高于全局基本路径
+ RestService restService = AnnotationUtils.findAnnotation(targetType, RestService.class);
+ // 当且仅当存在时进入
+ if (null != restService) {
+ // 注解的路径解析
+ String key = restService.value();
+ String baseUrl = restService.baseUrl();
+ // 更友好的处理解包
+ AutoMapping autoMapping = AnnotationUtils.findAnnotation(targetType, AutoMapping.class);
+ if (null != autoMapping) {
+ // 找到返回值处理器
+ Class> clazz = autoMapping.value();
+ // 处理返回值
+ if (Object.class.equals(clazz)) {
+ this.mapping = DefaultRestResultMapping.getInstance();
+ } else {
+ this.mapping = RestResultMapping.MAPPINGS.get(clazz);
+ }
+ }
+ // 当key不为空,解析字典路径
+ if (DataUtils.isNotBlank(key)) {
+ return properties.getDictUrl(key);
+ } else if (DataUtils.isNotBlank(baseUrl)) {
+ return baseUrl;
+ }
+ }
+ // 解包生效,以标准模式解包
+ return properties.getBaseUrl();
+ }
+
+ /**
+ * 决定基本url,优先级: 方法注解url > 方法注解baseUrl + uri > 全局配置 + uri
+ *
+ * @return 结果
+ */
+ private String determineUrl(RestApi restApi, ArgumentResolveContext context) {
+ String url;
+ // 解析url以支持PathVariable
+ if (DataUtils.isNotBlank(restApi.url())) {
+ url = restApi.url();
+ } else {
+ // 构建基础url,优先级从小到大依次找。同时尝试取字典值
+ Optional baseUrl = Stream.of(restApi.baseUrl(), this.baseUrl)
+ .filter(DataUtils::isNotBlank)
+ .findFirst()
+ .map(found -> found.startsWith("#") ? properties.getDictUrl(found.substring(1)) : found);
+ // 判定和赋值
+ if (baseUrl.isPresent() && DataUtils.isNotBlank(restApi.uri())) {
+ url = baseUrl.get() + restApi.uri();
+ } else {
+ throw new IllegalArgumentException("【Rest调用】未指定url或baseurl,无法调用远端服务器!");
+ }
+ }
+ // 尝试解析路径参数
+ return context.hasPathParams() ? UrlCompiler.compile(url, context.getPathParams()) : url;
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/ArgumentResolveContext.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/ArgumentResolveContext.java
new file mode 100644
index 0000000..f88d4e6
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/ArgumentResolveContext.java
@@ -0,0 +1,100 @@
+package group.flyfish.rest.registry.proxy.support;
+
+import group.flyfish.rest.annotation.RestApi;
+import group.flyfish.rest.utils.DataUtils;
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 参数解析上下文
+ *
+ * @author wangyu
+ */
+@Data
+@Builder
+public class ArgumentResolveContext {
+
+ // 注解
+ private RestApi annotation;
+
+ // 参数
+ private Map param;
+
+ // 路径参数
+ private Map pathParams;
+
+ // 请求头
+ private Map headers;
+
+ // 请求体
+ private Object body;
+
+ // 设置参数
+ public void setParam(String key, Object value) {
+ if (DataUtils.isEmpty(param)) {
+ param = new HashMap<>();
+ }
+ param.put(key, value);
+ }
+
+ // 设置请求体(分拣模式)
+ public void setBody(String key, Object value) {
+ if (null == body || !(body instanceof Map)) {
+ body = new HashMap<>();
+ }
+ Map map = DataUtils.cast(body);
+ map.put(key, value);
+ }
+
+ /**
+ * 设置头部
+ *
+ * @param headers 头
+ */
+ public void setHeaders(Map headers) {
+ if (DataUtils.isEmpty(this.headers)) {
+ this.headers = new HashMap<>();
+ }
+ this.headers.putAll(headers);
+ }
+
+ /**
+ * 设置单个头
+ *
+ * @param name 名称
+ * @param value 值
+ */
+ public void setHeader(String name, String value) {
+ if (DataUtils.isEmpty(this.headers)) {
+ this.headers = new HashMap<>();
+ }
+ this.headers.put(name, value);
+ }
+
+ // 设置路径参数
+ public void setPathParam(String key, Object value) {
+ if (DataUtils.isEmpty(pathParams)) {
+ pathParams = new HashMap<>();
+ }
+ pathParams.put(key, value);
+ }
+
+ public boolean hasPathParams() {
+ return DataUtils.isNotEmpty(pathParams);
+ }
+
+ public boolean hasBody() {
+ return null != body;
+ }
+
+ public boolean hasHeaders() {
+ return DataUtils.isNotEmpty(headers);
+ }
+
+ public boolean hasParams() {
+ return DataUtils.isNotEmpty(param);
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/RestArgumentResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/RestArgumentResolver.java
new file mode 100644
index 0000000..edc07b8
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/RestArgumentResolver.java
@@ -0,0 +1,28 @@
+package group.flyfish.rest.registry.proxy.support;
+
+import java.lang.reflect.Parameter;
+
+/**
+ * 参数解析器
+ *
+ * @author wangyu
+ */
+public interface RestArgumentResolver {
+
+ /**
+ * 是否支持
+ *
+ * @param parameter 参数
+ * @return 结果
+ */
+ boolean support(Parameter parameter);
+
+ /**
+ * 解析
+ *
+ * @param context 上下文,赋值
+ * @param parameter 参数
+ * @param value 值
+ */
+ void resolve(ArgumentResolveContext context, Parameter parameter, Object value);
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/RestArgumentResolverComposite.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/RestArgumentResolverComposite.java
new file mode 100644
index 0000000..59596fe
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/RestArgumentResolverComposite.java
@@ -0,0 +1,55 @@
+package group.flyfish.rest.registry.proxy.support;
+
+import group.flyfish.rest.annotation.RestApi;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.List;
+import java.util.stream.IntStream;
+
+/**
+ * 解析器集合
+ *
+ * @author wangyu
+ */
+public class RestArgumentResolverComposite {
+
+ // 解析器们
+ private final List resolvers;
+
+ public RestArgumentResolverComposite(List resolvers) {
+ this.resolvers = resolvers;
+ }
+
+ /**
+ * 执行解析
+ *
+ * @param annotation 注解
+ * @param method 方法
+ * @param args 参数
+ * @return 结果
+ */
+ public ArgumentResolveContext resolve(RestApi annotation, Method method, Object[] args) {
+ // 上下文
+ ArgumentResolveContext context = ArgumentResolveContext.builder().annotation(annotation).build();
+ // 参数元
+ Parameter[] parameters = method.getParameters();
+ // 循环处理
+ IntStream.range(0, parameters.length)
+ .forEach(index -> resolveInternal(context, parameters[index], args[index]));
+ return context;
+ }
+
+ /**
+ * 内部解析
+ *
+ * @param context 上下文
+ * @param parameter 参数
+ * @param value 值
+ */
+ private void resolveInternal(ArgumentResolveContext context, Parameter parameter, Object value) {
+ // 开始解析
+ resolvers.stream().filter(resolver -> resolver.support(parameter)).findFirst()
+ .ifPresent(resolver -> resolver.resolve(context, parameter, value));
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/UrlCompiler.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/UrlCompiler.java
new file mode 100644
index 0000000..26b60eb
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/UrlCompiler.java
@@ -0,0 +1,53 @@
+package group.flyfish.rest.registry.proxy.support;
+
+import group.flyfish.rest.core.builder.MapParamBuilder;
+import group.flyfish.rest.utils.DataUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * url编译器
+ *
+ * @author wangyu
+ */
+public class UrlCompiler {
+
+ // 匹配正则预编译
+ private static final Pattern PATTERN = Pattern.compile("\\{\\w+}");
+
+ /**
+ * 地址原路径编译
+ *
+ * @param source 源
+ * @return 结果
+ */
+ public static String compile(String source, Map params) {
+ Matcher matcher = PATTERN.matcher(source);
+ // 开始查找和替换
+ while (matcher.find()) {
+ String found = matcher.group();
+ String key = DataUtils.substringBetween(found, "{", "}");
+ if (params.containsKey(key)) {
+ source = source.replace(found, String.valueOf(params.get(key)));
+ }
+ }
+ return source;
+ }
+
+ /**
+ * 测试
+ *
+ * @param args args参数
+ */
+ public static void main(String[] args) {
+ String result = UrlCompiler.compile("http://www.baidu.com/love/{target}/{mobile}/{shit}", MapParamBuilder.builder()
+ .with("target", "nanami")
+ .with("mobile", 1223123)
+ .with("shit", new HashMap<>())
+ .build());
+ System.out.println(result);
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestBodyArgumentResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestBodyArgumentResolver.java
new file mode 100644
index 0000000..4bd20df
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestBodyArgumentResolver.java
@@ -0,0 +1,39 @@
+package group.flyfish.rest.registry.proxy.support.resolvers;
+
+import group.flyfish.rest.annotation.RestBody;
+import group.flyfish.rest.registry.proxy.support.ArgumentResolveContext;
+import group.flyfish.rest.registry.proxy.support.RestArgumentResolver;
+
+import java.lang.reflect.Parameter;
+
+/**
+ * 请求体参数解析
+ *
+ * @author wangyu
+ */
+public class RestBodyArgumentResolver implements RestArgumentResolver {
+
+ /**
+ * 是否支持
+ *
+ * @param parameter 参数
+ * @return 结果
+ */
+ @Override
+ public boolean support(Parameter parameter) {
+ return null != parameter.getAnnotation(RestBody.class);
+ }
+
+ /**
+ * 解析
+ *
+ * @param context 上下文,赋值
+ * @param parameter 参数
+ * @param value 值
+ */
+ @Override
+ public void resolve(ArgumentResolveContext context, Parameter parameter, Object value) {
+ // 无视合并body,这里的优先级最高
+ context.setBody(value);
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestHeaderArgumentResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestHeaderArgumentResolver.java
new file mode 100644
index 0000000..38ddd6a
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestHeaderArgumentResolver.java
@@ -0,0 +1,46 @@
+package group.flyfish.rest.registry.proxy.support.resolvers;
+
+import group.flyfish.rest.annotation.RestHeader;
+import group.flyfish.rest.registry.proxy.support.ArgumentResolveContext;
+import group.flyfish.rest.registry.proxy.support.RestArgumentResolver;
+import group.flyfish.rest.utils.DataUtils;
+
+import java.lang.reflect.Parameter;
+import java.util.Map;
+
+/**
+ * 请求头解析策略
+ *
+ * @author wangyu
+ */
+public class RestHeaderArgumentResolver implements RestArgumentResolver {
+
+ /**
+ * 是否支持
+ *
+ * @param parameter 参数
+ * @return 结果
+ */
+ @Override
+ public boolean support(Parameter parameter) {
+ return null != parameter.getAnnotation(RestHeader.class);
+ }
+
+ /**
+ * 解析
+ *
+ * @param context 上下文,赋值
+ * @param parameter 参数
+ * @param value 值
+ */
+ @Override
+ public void resolve(ArgumentResolveContext context, Parameter parameter, Object value) {
+ if (value instanceof Map) {
+ ((Map, ?>) value).forEach((k, v) -> context.setHeader((String) k, String.valueOf(v)));
+ } else {
+ RestHeader header = parameter.getAnnotation(RestHeader.class);
+ String name = DataUtils.isNotBlank(header.value()) ? header.value() : parameter.getName();
+ context.setHeader(name, null != value ? String.valueOf(value) : "");
+ }
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestParamArgumentResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestParamArgumentResolver.java
new file mode 100644
index 0000000..39faf12
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestParamArgumentResolver.java
@@ -0,0 +1,104 @@
+package group.flyfish.rest.registry.proxy.support.resolvers;
+
+import group.flyfish.rest.annotation.RestParam;
+import group.flyfish.rest.annotation.RestParams;
+import group.flyfish.rest.registry.proxy.support.ArgumentResolveContext;
+import group.flyfish.rest.registry.proxy.support.RestArgumentResolver;
+import group.flyfish.rest.utils.DataUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.util.ClassUtils;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Parameter;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * 参数解析
+ *
+ * @author wangyu
+ */
+@Slf4j
+public class RestParamArgumentResolver implements RestArgumentResolver {
+
+ /**
+ * 是否支持
+ *
+ * @param parameter 参数
+ * @return 结果
+ */
+ @Override
+ public boolean support(Parameter parameter) {
+ return true;
+ }
+
+ /**
+ * 解析
+ *
+ * @param context 上下文,赋值
+ * @param parameter 参数
+ * @param value 值
+ */
+ @Override
+ public void resolve(ArgumentResolveContext context, Parameter parameter, Object value) {
+ // 当参数包含@RestParams注解,使用BeanDescriptor处理
+ if (null != parameter.getAnnotation(RestParams.class) || ClassUtils.isAssignable(Map.class, parameter.getType())) {
+ resolveParams(context, parameter, value);
+ } else {
+ // 取得合法的名称
+ String name = Optional.ofNullable(parameter.getAnnotation(RestParam.class))
+ .map(RestParam::value)
+ .filter(DataUtils::isNotBlank)
+ .orElse(parameter.getName());
+ // 启用合并请求体,合并入
+ if (context.getAnnotation().mergedBody()) {
+ context.setBody(name, value);
+ } else {
+ context.setParam(name, value);
+ }
+ }
+ }
+
+ /**
+ * 解析多个参数
+ *
+ * @param parameter 参数
+ * @param value 值
+ */
+ private void resolveParams(ArgumentResolveContext context, Parameter parameter, Object value) {
+ // 参数注解存在,报出错误
+ if (null != parameter.getAnnotation(RestParam.class)) {
+ throw new IllegalArgumentException("无法将对象作为一个普通的参数!");
+ }
+ // 非空才处理
+ if (null != value) {
+ // 是map,直接解包赋值
+ if (ClassUtils.isAssignable(Map.class, parameter.getType())) {
+ Map values = DataUtils.cast(value);
+ values.forEach((k, v) -> {
+ if (null != v) {
+ context.setParam(k, String.valueOf(v));
+ }
+ });
+ } else {
+ // 对象,解析后混入
+ for (PropertyDescriptor propertyDescriptor : BeanUtils.getPropertyDescriptors(value.getClass())) {
+ if ("class".equalsIgnoreCase(propertyDescriptor.getName())) {
+ continue;
+ }
+ Object v = null;
+ try {
+ v = propertyDescriptor.getReadMethod().invoke(value);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ log.error("【Rest客户端】尝试解析参数时发生异常!获取bean的属性表失败!{}", e.getMessage(), e);
+ }
+ if (null != v) {
+ context.setParam(propertyDescriptor.getName(), String.valueOf(v));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestPathParamArgumentResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestPathParamArgumentResolver.java
new file mode 100644
index 0000000..945a8a0
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestPathParamArgumentResolver.java
@@ -0,0 +1,41 @@
+package group.flyfish.rest.registry.proxy.support.resolvers;
+
+import group.flyfish.rest.annotation.RestPathParam;
+import group.flyfish.rest.registry.proxy.support.ArgumentResolveContext;
+import group.flyfish.rest.registry.proxy.support.RestArgumentResolver;
+import group.flyfish.rest.utils.DataUtils;
+
+import java.lang.reflect.Parameter;
+
+/**
+ * 解析路径参数
+ *
+ * @author wangyu
+ */
+public class RestPathParamArgumentResolver implements RestArgumentResolver {
+
+ /**
+ * 是否支持
+ *
+ * @param parameter 参数
+ * @return 结果
+ */
+ @Override
+ public boolean support(Parameter parameter) {
+ return null != parameter.getAnnotation(RestPathParam.class);
+ }
+
+ /**
+ * 解析
+ *
+ * @param context 上下文,赋值
+ * @param parameter 参数
+ * @param value 值
+ */
+ @Override
+ public void resolve(ArgumentResolveContext context, Parameter parameter, Object value) {
+ RestPathParam annotation = parameter.getAnnotation(RestPathParam.class);
+ String name = DataUtils.isNotBlank(annotation.value()) ? annotation.value() : parameter.getName();
+ context.setPathParam(parameter.getName(), value);
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/wrapper/DefaultRestResultMapping.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/wrapper/DefaultRestResultMapping.java
new file mode 100644
index 0000000..48577a3
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/wrapper/DefaultRestResultMapping.java
@@ -0,0 +1,67 @@
+package group.flyfish.rest.registry.wrapper;
+
+import group.flyfish.rest.core.exception.RestClientException;
+import group.flyfish.rest.mapping.RestResultMapping;
+import group.flyfish.rest.utils.TypeResolveUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.client.utils.DateUtils;
+
+import java.lang.reflect.Type;
+import java.util.Date;
+
+/**
+ * 默认缺省的结果映射
+ *
+ * @author wangyu
+ */
+@Slf4j
+public class DefaultRestResultMapping implements RestResultMapping {
+
+ /**
+ * 获取内部类单例
+ *
+ * @return 结果
+ */
+ public static DefaultRestResultMapping getInstance() {
+ return SingletonHolder.INSTANCE;
+ }
+
+ /**
+ * 模糊的结果映射
+ *
+ * @param body 结果
+ * @return 映射后的结果
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public T map(Object body) throws RestClientException {
+ if (body instanceof RestResult) {
+ RestResult> result = (RestResult>) body;
+ if (result.isSuccess()) {
+ return (T) result.getResult();
+ }
+ log.error("【RestProxy】请求发生异常!状态码:{},时间:{},信息:{}", result.getCode(),
+ DateUtils.formatDate(new Date(result.getTimestamp()), "yyyy-MM-dd HH:mm:ss"), result.getMessage());
+ throw new RestClientException(result.getMessage());
+ }
+ return (T) body;
+ }
+
+ /**
+ * 解析返回类型
+ *
+ * @param resultType 返回类型
+ * @return 结果
+ */
+ @Override
+ public Type resolve(Type resultType) {
+ return TypeResolveUtils.wrap(resultType, RestResult.class);
+ }
+
+ /**
+ * 静态初始化器,由JVM来保证线程安全
+ */
+ private interface SingletonHolder {
+ DefaultRestResultMapping INSTANCE = new DefaultRestResultMapping();
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/wrapper/RestResult.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/wrapper/RestResult.java
new file mode 100644
index 0000000..0b4d052
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/wrapper/RestResult.java
@@ -0,0 +1,37 @@
+package group.flyfish.rest.registry.wrapper;
+
+import lombok.Data;
+
+/**
+ * 标准的解包结构,等同于全局
+ *
+ * @author wangyu
+ */
+@Data
+public class RestResult {
+
+ /**
+ * 成功标志
+ */
+ private boolean success = true;
+
+ /**
+ * 返回处理消息
+ */
+ private String message = "操作成功!";
+
+ /**
+ * 返回代码
+ */
+ private Integer code = 0;
+
+ /**
+ * 返回数据对象 data
+ */
+ private T result;
+
+ /**
+ * 时间戳
+ */
+ private long timestamp = System.currentTimeMillis();
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/utils/DataUtils.java b/rest-proxy-core/src/main/java/group/flyfish/rest/utils/DataUtils.java
new file mode 100644
index 0000000..0fe8b20
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/utils/DataUtils.java
@@ -0,0 +1,112 @@
+package group.flyfish.rest.utils;
+
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * 数据工具类,仅限于rest模块使用
+ *
+ * @author wangyu
+ */
+public final class DataUtils {
+
+ public static final int INDEX_NOT_FOUND = -1;
+
+ /**
+ * 判断字符串是否不为空
+ *
+ * @param target 目标字符串
+ * @return 结果
+ */
+ public static boolean isNotBlank(String target) {
+ return !StringUtils.isEmpty(target);
+ }
+
+ /**
+ * 强制转换和平滑过渡
+ *
+ * @param source 原对象
+ * @param 原类型
+ * @param 目标类型
+ * @return 结果
+ */
+ @SuppressWarnings("unchecked")
+ public static T cast(R source) {
+ return (T) source;
+ }
+
+ /**
+ * 判断集合是否不为空
+ *
+ * @param collection 集合
+ * @return 结果
+ */
+ public static boolean isNotEmpty(Collection> collection) {
+ return !CollectionUtils.isEmpty(collection);
+ }
+
+ /**
+ * 判断map是否不为空
+ *
+ * @param map 一个字典
+ * @return 结果
+ */
+ public static boolean isNotEmpty(Map, ?> map) {
+ return !CollectionUtils.isEmpty(map);
+ }
+
+ /**
+ * 判断map是否为囧
+ *
+ * @param map 一个字典
+ * @return 结果
+ */
+ public static boolean isEmpty(Map, ?> map) {
+ return CollectionUtils.isEmpty(map);
+ }
+
+ /**
+ * 是否为空
+ *
+ * @param target 目标
+ * @return 结果
+ */
+ public static boolean isBlank(String target) {
+ return StringUtils.isEmpty(target);
+ }
+
+ /**
+ * 查找两个字符串之间的内容
+ *
+ * @param str 字符串
+ * @param open 开始
+ * @param close 结尾
+ * @return 结果
+ */
+ public static String substringBetween(final String str, final String open, final String close) {
+ if (str == null || open == null || close == null) {
+ return null;
+ }
+ final int start = str.indexOf(open);
+ if (start != INDEX_NOT_FOUND) {
+ final int end = str.indexOf(close, start + open.length());
+ if (end != INDEX_NOT_FOUND) {
+ return str.substring(start + open.length(), end);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 判断是不是true
+ *
+ * @param value 值
+ * @return 结果
+ */
+ public static boolean isTrue(Boolean value) {
+ return Boolean.TRUE.equals(value);
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/utils/IocBeans.java b/rest-proxy-core/src/main/java/group/flyfish/rest/utils/IocBeans.java
new file mode 100644
index 0000000..68b160c
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/utils/IocBeans.java
@@ -0,0 +1,95 @@
+package group.flyfish.rest.utils;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.lang.Nullable;
+
+import java.util.Optional;
+import java.util.function.Supplier;
+
+public class IocBeans implements ApplicationContextAware {
+
+ private static IocBeans instance;
+
+ private ApplicationContext applicationContext;
+
+ public IocBeans() {
+ instance = this;
+ }
+
+ public static IocBeans sharedInstance() {
+ return instance;
+ }
+
+ public static IocBeans sharedInstance(ApplicationContext applicationContext) {
+ if (null != instance && null == instance.applicationContext) {
+ instance.applicationContext = applicationContext;
+ }
+ return instance;
+ }
+
+
+ /**
+ * 注册bean
+ *
+ * @param name bean的名称
+ * @param beanClass bean的类
+ * @param beanSupplier bean实例化
+ */
+ public static T registerBean(String name, Class beanClass, Supplier beanSupplier) {
+ return sharedInstance().register(name, beanClass, beanSupplier);
+ }
+
+ /**
+ * 注册bean
+ *
+ * @param name bean的名称
+ * @param beanClass bean的类
+ * @param beanSupplier bean实例化
+ */
+ @SuppressWarnings("unchecked")
+ public T register(String name, Class beanClass, Supplier beanSupplier) {
+ // 已经包含bean
+ if (applicationContext.containsBean(name)) {
+ Object wired = applicationContext.getBean(name);
+ if (wired.getClass().isAssignableFrom(beanClass)) {
+ return (T) wired;
+ } else {
+ throw new RuntimeException("注册RMI实例出错,Bean名称重复:" + name);
+ }
+ }
+ // 构建bean定义
+ BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(beanClass, beanSupplier)
+ .getRawBeanDefinition();
+ // 写入
+ return Optional.ofNullable(getBeanFactory())
+ .map(factory -> {
+ factory.registerBeanDefinition(name, beanDefinition);
+ return applicationContext.getBean(name, beanClass);
+ }).orElseThrow(() -> new RuntimeException("注册RMI实例出错,BeanFactory不存在!"));
+ }
+
+ /**
+ * 获取beanFactory
+ *
+ * @return 结果
+ */
+ @Nullable
+ public DefaultListableBeanFactory getBeanFactory() {
+ BeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
+ if (beanFactory == null) {
+ beanFactory = applicationContext.getParentBeanFactory();
+ }
+ return (DefaultListableBeanFactory) beanFactory;
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+}
diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/utils/JacksonUtil.java b/rest-proxy-core/src/main/java/group/flyfish/rest/utils/JacksonUtil.java
new file mode 100644
index 0000000..5e8c1e1
--- /dev/null
+++ b/rest-proxy-core/src/main/java/group/flyfish/rest/utils/JacksonUtil.java
@@ -0,0 +1,150 @@
+package group.flyfish.rest.utils;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Jackson序列化工具类
+ *
+ * @author Mr.Wang
+ */
+public final class JacksonUtil {
+
+ private static final ObjectMapper mapper = new ObjectMapper();
+
+ static {
+ // =========================================================================
+ // SerializationFeature for changing how JSON is written
+
+ // to enable standard indentation ("pretty-printing"):
+// mapper.enable(SerializationFeature.INDENT_OUTPUT);
+ // to allow serialization of "empty" POJOs (no properties to serialize)
+ // (without this setting, an exception is thrown in those cases)
+ mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
+ // to write java.util.Date, Calendar as number (timestamp):
+ mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+
+ // DeserializationFeature for changing how JSON is read as POJOs:
+
+ // to prevent exception when encountering unknown property:
+ mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+ // to allow coercion of JSON empty String ("") to null Object value:
+ mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
+
+ // =========================================================================
+ // JsonParser.Feature for configuring parsing settings:
+
+ // to allow C/C++ style comments in JSON (non-standard, disabled by default)
+ mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
+ // (note: with Jackson 2.5, there is also `mapper.enable(feature)` / `mapper.disable(feature)`)
+ mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
+ // to allow (non-standard) unquoted field names in JSON:
+ mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
+ // to allow use of apostrophes (single quotes), non standard
+ mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
+
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ // JsonGenerator.Feature for configuring low-level JSON generation:
+
+ // to force escaping of non-ASCII characters:
+ mapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true);
+ }
+
+ public static T fromJson(final String json) {
+ return readValue(json, new TypeReference() {
+ });
+ }
+
+ public static T fromJson(final String json, TypeReference reference) {
+ return readValue(json, reference);
+ }
+
+ public static T fromJson(final String json, Class clazz) {
+ if (null == json || "".equals(json)) {
+ return null;
+ }
+ try {
+ return mapper.readValue(json, clazz);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static T fromJson(final String json, JavaType type) {
+ if (null == json || "".equals(json)) {
+ return null;
+ }
+ try {
+ return mapper.readValue(json, type);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+// public static Map json2Map(final String json) {
+// return fromJson(json, Map.class);
+// }
+
+ public static Map json2Map(String json) {
+ return readValue(json, new TypeReference