/**
* 约束定义 Api 需要的基本信息
* T: 原始参数类型
* R: 远程调用参数类型
* E: 远程结果需要转换的类型
*
* E doRequest((R)T);
*
*
*/
public interface ApiDefinition<T, R, E> {
/**
* 服务唯一标识,与配置文件的服务 ID 对应 {@link RouteServiceProperties.ServiceConfig#getId()}
*/
String getServiceId();
/**
* Api名称,与标准Api定义 {@link RouteApiMapping#name()} @RouteApiMapping 注解的 name 对应
*/
String getApiName();
/**
* 请求路径
*/
String getPath();
/**
* 客户端类型
*/
TemplateType getTemplateType();
/**
* 请求入参转换器
* 转换器入参:@ApiParam 标识的参数;
* 转换器出参:服务提供方接口需要的参数;
*
* @see ApiParam
*/
public abstract RequestConvert<T, R> convertReq();
/**
* 响应结果转换器
* 转换器入参:参数1-服务 api 返回的报文; 参数2- @RouteApiMapping 标记方法定义返回值的泛型类型;
* 转换器出参:@RouteApiMapping 标记方法所定义返回值的泛型数据;
*
* @see RouteApiMapping
*/
public abstract ResponseParse<E> parseResp();
/**
* 请求入参的数据 ID (用于定位调用日志)
*/
public abstract String getRequestDataId(T request);
/**
* 请求授权类型
*/
AuthType authType();
}
beanDefinition的定义
public abstract class TosHttpAbstractApiDefinition<T, R, E> implements ApiDefinition<T, R, E> {
/**
* http请求方法
*/
public abstract Method getMethod();
/**
* 构建 http 请求头
*/
public Map<String, String> buildHeader() {
return new HashMap<>();
}
/**
* 构建请求的 form 表单数据
*/
public abstract Map<String, Object> buildForm(R r);
/**
* 构建请求的请求体数据(注:请求体不为空时会清除 form 表单数据)
*/
public abstract String buildBody(R r);
/**
* 重建 httpRequest
*/
public HttpRequest rebuildRequest(HttpRequest httpRequest) {
return httpRequest;
}
@Override
public AuthType authType() {
return AuthType.YLG;
}
@Override
public final TemplateType getTemplateType() {
return TemplateType.HTTP;
}
}
http的方式
package com.nuzar.fcms.common.api.definition.ch.accept;
import cn.hutool.http.Method;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.nuzar.fcms.common.api.model.request.platform.accept.PlansReq;
import com.nuzar.fcms.framwork.route.CHResponse;
import com.nuzar.fcms.framwork.route.Result;
import com.nuzar.fcms.framwork.route.TosHttpAbstractApiDefinition;
import com.nuzar.fcms.framwork.route.convert.RequestConvert;
import com.nuzar.fcms.framwork.route.convert.ResponseParse;
import com.nuzar.fcms.framwork.route.enums.AuthType;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 巢湖计划取消接口定义
*
* @author longlh
* @since 2023/6/26 15 14:10
*/
@Component
public class CHBulkCancelPlanDef extends TosHttpAbstractApiDefinition<PlansReq, Map<String,Object>, Void> {
@Override
public String getServiceId() {
return "CH";
}
@Override
public String getApiName() {
return "cancelPlan";
}
@Override
public String getPath() {
return "/api/BasGKPT/CancelBulkPlan";
}
@Override
public Method getMethod() {
return Method.POST;
}
@Override
public Map<String, Object> buildForm(Map<String, Object> objectMap) {
return null;
}
@Override
public AuthType authType() {
return AuthType.CH;
}
@Override
public String buildBody(Map<String,Object> request) {
return JSONUtil.toJsonStr(request);
}
@Override
public RequestConvert<PlansReq, Map<String,Object>> convertReq() {
return request -> {
Map<String,Object> httpRequest = Maps.newHashMap();
httpRequest.put("planPlatId",request.getPlanno());
return httpRequest;
};
}
@Override
public ResponseParse<Void> parseResp() {
return (response, returnDataType) -> {
CHResponse tosResponse = JSON.parseObject(response, CHResponse.class);
if (!tosResponse.isSuccess()) {
return Result.fail(tosResponse.getMessage());
}
return Result.success();
};
}
@Override
public String getRequestDataId(PlansReq request) {
return request.getPlanno().toString();
}
}
具体实例
package com.nuzar.fcms.framwork.route.auth;
import java.time.LocalDateTime;
public class Auth {
/**
* token值
*/
private String credentials;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 过期时间
*/
private LocalDateTime expireAt;
/**
* 临近过期时间
*/
private LocalDateTime nearExpireAt;
public Auth(String credentials, LocalDateTime createTime, LocalDateTime expireAt, LocalDateTime nearExpireAt) {
this.credentials = credentials;
this.createTime = createTime;
this.expireAt = expireAt;
this.nearExpireAt = nearExpireAt;
}
/**
* 是否已经过期
*/
public boolean expired() {
return expireAt == null ? false : expireAt.isBefore(LocalDateTime.now());
}
/**
* 是否临近过期
*/
public boolean nearExpired() {
return nearExpireAt == null ? false : nearExpireAt.isBefore(LocalDateTime.now());
}
public String getCredentials() {
return credentials;
}
}
授权
package com.nuzar.fcms.framwork.route.auth;
import com.nuzar.fcms.framwork.route.config.RouteServiceProperties;
import java.util.concurrent.Executor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
*
**/
public abstract class AuthHandleAbstract<T> {
/**
* 授权信息
*/
private Auth auth;
/**
* 异步刷新授权信息线程池
*/
private final Executor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy());
/**
* 是否正在刷新授权
*/
private final AtomicBoolean inRefresh = new AtomicBoolean(false);
/**
* 附加授权信息
*
* @param requestEntity 需要附加授权信息的请求模型
*/
public void attach(RouteServiceProperties.ServiceConfig serviceConfig, T requestEntity) {
if (auth == null || auth.expired()) {
//授权信息初始化或者已过期
refreshAuth(serviceConfig);
} else if (auth.nearExpired()) {
//即将过期,异步刷新
executor.execute(() -> refreshAuth(serviceConfig));
}
attachAuth(requestEntity, auth);
}
/**
* 刷新授权
*
* @param serviceConfig 请求授权的服务信息
*/
private void refreshAuth(RouteServiceProperties.ServiceConfig serviceConfig) {
if (inRefresh.compareAndSet(false, true)) {
try {
auth = getAuth(serviceConfig);
} finally {
inRefresh.set(false);
}
} else {
//已有线程在获取token,自旋等待 token 刷新
while (inRefresh.get()) {
}
}
}
/**
* 附加授权信息
*
* @param requestEntity 需要附加授权信息的请求模型
*/
protected abstract void attachAuth(T requestEntity, Auth auth);
/**
* 请求授权
*
* @param serviceConfig 请求授权的服务信息
* @return 授权
*/
protected abstract Auth getAuth(RouteServiceProperties.ServiceConfig serviceConfig);
}
授权抽象类
/**
* 巢湖码头请求授权处理器
*
**/
public class CHAuthHandle extends AuthHandleAbstract<Map<String, String>> {
/**
* 提前授权时间
*/
private final Duration advanceTime;
/**
* 获取授权请求路径
*/
private final String authPath = "/api/Login";
public CHAuthHandle(Duration advanceTime) {
this.advanceTime = advanceTime;
}
@Override
protected void attachAuth(Map<String, String> headers, Auth auth) {
headers.put(Header.AUTHORIZATION.getValue(), "Bearer " + auth.getCredentials());
}
/**
* 获取授权
*
* @param serviceConfig 请求授权的服务信息
*/
@Override
protected Auth getAuth(RouteServiceProperties.ServiceConfig serviceConfig) {
Assert.isTrue(StringUtils.hasText(serviceConfig.getUsername()) && StringUtils.hasText(serviceConfig.getPassword()), "服务配置的授权用户名或密码为空");
//获取token
String requestUrl = "http://" + serviceConfig.getHost() + ":" + serviceConfig.getPort() + authPath;
Map<String, Object> requestParam = new HashMap<>();
requestParam.put("BUE_UESRACCUONT", serviceConfig.getUsername());
requestParam.put("BUE_PASSWORD", serviceConfig.getPassword());
HttpResponse execute = HttpUtil.createPost(requestUrl)
.contentType(ContentType.JSON.getValue())
.body(JSONUtil.toJsonStr(requestParam))
.execute();
CHResponse chResponse = UpperCamelConvert.upperCamelStrToObj(execute.body(),CHResponse.class);
Assert.isTrue(chResponse.isSuccess(), () -> new RouteException(RouteExceptionEnum.AUTH_FAIL, chResponse.getMessage()));
return new Auth(chResponse.getMessage(), null, null, null);
}
}
@Configuration
@EnableConfigurationProperties(RouteServiceProperties.class)
@MapperScan("com.nuzar.fcms.framwork.route.log.mapper")
@ComponentScan("com.nuzar.fcms.framwork.route.log")
public class RouteAutoConfiguration {
/**
* 初始化服务配置与服务 api 定义
*
* @param routeServiceProperties 服务提供方信息
* @param apiDefinitionList api定义列表
*/
@Bean
public RouteServiceConfiguration routeServiceConfiguration(RouteServiceProperties routeServiceProperties, List<ApiDefinition> apiDefinitionList) {
return new RouteServiceConfiguration(routeServiceProperties.getServices(), apiDefinitionList);
}
/**
* 初始化 TOS 使用 SOAP 协议的 Template
*/
@Bean
public SoapTosTemplate soapTosTemplate() {
return new SoapTosTemplate();
}
/**
* 初始化 TOS 使用 HTTP 协议的 Template
*/
@Bean
public HttpTosTemplate httpTosTemplate() {return new HttpTosTemplate();}
/**
* 阳逻港 http 请求的授权处理器
*/
@Bean
public YLGAuthHandle httpAuthHandle() {
return new YLGAuthHandle(Duration.ofMinutes(10));
}
/**
* 巢河http 请求的授权处理器
*/
@Bean
public CHAuthHandle chHttpAuthHandle() {
return new CHAuthHandle(Duration.ofMinutes(10));
}
}
配置
package com.nuzar.fcms.framwork.route.config;
import com.nuzar.fcms.framwork.route.ApiDefinition;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.stream.Collectors;
/**
* <p>
* 服务配置与服务 api 关系映射的配置类
* </p>
*
*
*/
public class RouteServiceConfiguration implements InitializingBean {
/**
* 服务配置列表
*/
private final Set<RouteServiceProperties.ServiceConfig> serviceSet;
/**
* 服务 api 列表
*/
private final List<ApiDefinition> apiDefinitionList;
/**
* 服务配置与 api 的映射
*/
private Map<RouteServiceProperties.ServiceConfig, List<ApiDefinition>> serviceApiMapping = new HashMap<>();
public RouteServiceConfiguration(Set<RouteServiceProperties.ServiceConfig> serviceSet, List<ApiDefinition> apiDefinitionList) {
this.serviceSet = serviceSet;
this.apiDefinitionList = apiDefinitionList;
}
/**
* 初始化服务配置与服务 api 的映射关系
*/
@Override
public void afterPropertiesSet() {
this.serviceApiMapping = serviceSet.stream().collect(
Collectors.toMap(service -> service,
service -> apiDefinitionList.stream()
.filter(api -> Objects.equals(service.getId(), api.getServiceId()))
.collect(Collectors.toList())
)
);
}
/**
* 根据服务路由值获取指定 api 的服务配置与服务 api 定义
*
* @param route 服务路由值
* @param apiName api名称
* @return left:服务配置;right:服务api定义
*/
public Optional<Pair<RouteServiceProperties.ServiceConfig, ApiDefinition>> getApi(String route, String apiName) {
if (!StringUtils.hasText(route) || !StringUtils.hasText(apiName)) {
Optional.empty();
}
return getApiList(apiName).stream()
.filter(pair -> Objects.equals(pair.getLeft().getRoute(), route))
.findAny();
}
/**
* 获取所有服务指定 api 的服务配置与服务 api 定义列表
*
* @param apiName api名称
* @return left:服务配置;right:服务api定义
*/
public List<Pair<RouteServiceProperties.ServiceConfig, ApiDefinition>> getApiList(String apiName) {
if (!StringUtils.hasText(apiName)) {
return Collections.emptyList();
}
return serviceApiMapping.entrySet().stream()
.flatMap(entry -> entry.getValue().stream()
.map(api -> Pair.of(entry.getKey(), api))
)
.filter(pair -> Objects.equals(pair.getRight().getApiName(), apiName))
.collect(Collectors.toList());
}
/**
* 根据服务ID列表查询服务配置
*
* @param ids 服务ID列表
* @return 服务配置列表
*/
public Set<RouteServiceProperties.ServiceConfig> getServiceById(Set<String> ids) {
return serviceSet.stream()
.filter(s -> ids.contains(s.getId()))
.collect(Collectors.toSet());
}
}
package com.nuzar.fcms.framwork.route.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.util.HashSet;
import java.util.Set;
/**
* <p>
* 服务配置
* </p>
*
*
*/
@Data
@Validated
@ConfigurationProperties("route")
public class RouteServiceProperties {
/**
* 服务列表
*/
@NotEmpty
private Set<ServiceConfig> services = new HashSet<>();
/**
* 服务配置
*/
@Data
public static class ServiceConfig {
/**
* 服务ID,标识配置唯一
*/
@NotBlank
private String id;
/**
* 调用 api 时定位服务的路由值
*/
@NotBlank
private String route;
/**
* 服务的主机 IP 地址
*/
@NotBlank
private String host;
/**
* 服务的端口
*/
@NotBlank
private String port;
/**
* 认证用户名
*/
private String username;
/**
* 认证密码
*/
private String password;
}
}
方法执行器
package com.nuzar.fcms.framwork.route.handler;
import com.nuzar.fcms.framwork.route.annotations.RouteApiClient;
import com.nuzar.fcms.framwork.route.annotations.RouteApiMapping;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Optional;
/**
* <p>
* 动态代理对象的代理方法执行器
* </p>
*
*
*/
public interface MethodHandler {
/**
* api动态代理对象(被{@link RouteApiClient} @RouteApiClient 标识的接口)的 api 方法(被{@link RouteApiMapping} @RouteApiMapping 标识的方法)实际执行的方法
*
* @param argv
* @return
* @throws Throwable
*/
Object invoke(Object[] argv) throws Throwable;
/**
* 获取方法返回值的泛型类型
*
* @param method 方法实例
* @return 方法返回值的泛型类型
*/
default Type getReturnDataType(Method method) {
return ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[0];
}
/**
* 获取方法中被指定注解标识的入参
* @param method 方法实例
* @param argv 方法参数实例列表
* @param annotationClass 注解类型
* @return 可能不存在的入参
*/
default Optional<Object> getByAnnotation(Method method,Object[] argv, Class<? extends Annotation> annotationClass) {
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
if (parameters[i].isAnnotationPresent(annotationClass)) {
return Optional.ofNullable(argv[i]);
}
}
return Optional.empty();
}
}
package com.nuzar.fcms.framwork.route.handler;
import cn.hutool.core.lang.Assert;
import com.nuzar.fcms.framwork.route.ApiDefinition;
import com.nuzar.fcms.framwork.route.Request;
import com.nuzar.fcms.framwork.route.Result;
import com.nuzar.fcms.framwork.route.annotations.ApiParam;
import com.nuzar.fcms.framwork.route.annotations.RouteApiMapping;
import com.nuzar.fcms.framwork.route.annotations.RouteParam;
import com.nuzar.fcms.framwork.route.config.RouteServiceConfiguration;
import com.nuzar.fcms.framwork.route.config.RouteServiceProperties;
import com.nuzar.fcms.framwork.route.exception.RouteException;
import com.nuzar.fcms.framwork.route.exception.RouteExceptionEnum;
import com.nuzar.fcms.framwork.route.template.Template;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import java.lang.reflect.Method;
import java.util.Optional;
/**
* <p>
* 路由类型代理方法执行器
* </p>
*
* @param <T> {@link Result#getData()}的类型
*
*/
@AllArgsConstructor
@NoArgsConstructor
public class RouteMethodHandler<T> implements MethodHandler {
/**
* 代理方法上 {@link RouteApiMapping} @RouteApiMapping 的实例
*/
private RouteApiMapping thirdApiMapping;
/**
* 服务与api配置
*/
private RouteServiceConfiguration thirdServiceConfiguration;
/**
* 代理方法对象
*/
private Method method;
@Override
public Result<T> invoke(Object[] argv) throws Throwable {
//获取路由值
String route = getRoute(argv).map(Object::toString).orElseThrow(() -> new RouteException(RouteExceptionEnum.ILLEGAL_ARGUMENT, ", 传播方式为路由时, 路由不能为空"));
//获取请求原始参数
Optional<Object> originalParam = getRequestParam(argv);
Assert.isTrue(Result.class == method.getReturnType(), () -> new RouteException(RouteExceptionEnum.ILLEGAL_RETURN_TYPE_DEFINITION, ", 路由模式的返回值必须使用 Result 包装"));
//获取本次调用的服务api
Pair<RouteServiceProperties.ServiceConfig, ApiDefinition> api = thirdServiceConfiguration.getApi(route, thirdApiMapping.name()).orElseThrow(() -> new RouteException(RouteExceptionEnum.SERVICE_API_NOT_FOUND, ";route:[%s],apiName[%s]", route, thirdApiMapping.name()));
Template<T> template = api.getRight().getTemplateType().getTemplate();
//组装请求入参
Request request = new Request(originalParam.orElse(null), getReturnDataType(method), api.getRight(), api.getLeft());
//执行调用
return template.execute(request);
}
public RouteMethodHandler(Method method, RouteApiMapping thirdApiMapping, RouteServiceConfiguration thirdServiceConfiguration) {
this.method = method;
this.thirdApiMapping = thirdApiMapping;
this.thirdServiceConfiguration = thirdServiceConfiguration;
}
/**
* 获取路由值
*
* @param argv 方法参数实例
* @return 可能不存在的路由值
*/
private Optional<Object> getRoute(Object[] argv) {
return getByAnnotation(method, argv, RouteParam.class);
}
/**
* 获取请求原始参数
*
* @param argv 方法参数实例
* @return 可能不存在的参数
*/
private Optional<Object> getRequestParam(Object[] argv) {
return getByAnnotation(method, argv, ApiParam.class);
}
}
路由方法执行器
package com.nuzar.fcms.framwork.route.handler;
import com.nuzar.fcms.framwork.route.annotations.RouteApiMapping;
import com.nuzar.fcms.framwork.route.exception.RouteException;
import com.nuzar.fcms.framwork.route.exception.RouteExceptionEnum;
import com.nuzar.fcms.framwork.route.config.RouteServiceConfiguration;
import org.springframework.beans.factory.BeanFactory;
import java.lang.reflect.Method;
/**
* 代理方法执行器生成工厂
*
*
*/
public class MethodHandlerFactory {
/**
* 创建代理方法执行器
* @param method 被代理的方法
* @param apiMapping 被代理的方法的{@link RouteApiMapping} @RouteApiMapping 实例
* @param beanFactory bean工厂
* @return 代理方法执行器
*/
public static MethodHandler create(Method method, RouteApiMapping apiMapping, BeanFactory beanFactory) {
switch (apiMapping.spreadType()) {
case BROADCAST:
return new BroadcastMethodHandler(method, apiMapping, beanFactory.getBean(RouteServiceConfiguration.class));
case ROUTE:
return new RouteMethodHandler(method, apiMapping, beanFactory.getBean(RouteServiceConfiguration.class));
default:
throw new RouteException(RouteExceptionEnum.ENUM_INSTANT_NOT_FIND, ", 错误的方法定义:%s, 查找的传播类型:%s", method.getDeclaringClass().getName() + "#" + method.getName(), apiMapping.spreadType());
}
}
}
方法执行器工厂
package com.nuzar.fcms.framwork.route;
import com.nuzar.fcms.framwork.route.annotations.RouteApiMapping;
import com.nuzar.fcms.framwork.route.handler.MethodHandler;
import com.nuzar.fcms.framwork.route.handler.MethodHandlerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class RouteApiFactoryBean implements FactoryBean<Object> {
private Class<?> type;
private BeanFactory beanFactory;
@Override
public Object getObject() {
Map<Method, MethodHandler> methodToHandler = new HashMap<>();
for (Method method : type.getMethods()) {
if (method.isAnnotationPresent(RouteApiMapping.class)) {
methodToHandler.put(method, MethodHandlerFactory.create(method, method.getAnnotation(RouteApiMapping.class), beanFactory));
}
}
return Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]{type}, new RouteInvocationHandler(methodToHandler));
}
@Override
public Class<?> getObjectType() {
return null;
}
public void setType(Class<?> type) {
this.type = type;
}
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
}
创建代理对象
package com.nuzar.fcms.framwork.route.template;
import com.nuzar.fcms.framwork.route.Result;
import com.nuzar.fcms.framwork.route.Request;
/**
* 调用执行器
*
*
*/
public interface Template<T> {
/**
* 调用服务 api 的执行方法
* @param request 调用需要的参数
* @return 执行结果
*/
Result<T> execute(Request request);
}
调用执行器
package com.nuzar.fcms.framwork.route.template;
import com.nuzar.fcms.framwork.route.ApiDefinition;
import com.nuzar.fcms.framwork.route.Request;
import com.nuzar.fcms.framwork.route.Result;
import com.nuzar.fcms.framwork.route.log.LogHolder;
import lombok.extern.slf4j.Slf4j;
/**
* <p>
* 调用执行器抽象实现,对调用动作进行细分,用模板方法的形式对调用的各节点前后进行修饰
* </p>
*
*
*/
@Slf4j
public abstract class TemplateAbstract<T> implements Template<T> {
@Override
public Result<T> execute(Request request) {
Result<T> result;
LogHolder.startToOuter(request);
//构建请求参数
Object requestParam = buildReq(request.getApiDefinition(), request.getOriginalParam());
LogHolder.putRequestParam(requestParam);
String responseStr;
try {
log.info("[route]开始进行外部调用,请求参数:{}", requestParam);
//执行请求
responseStr = doExecute(request, requestParam);
log.info("[route]外部调用结束,返回结果:{}", responseStr);
LogHolder.putResponse(responseStr);
} catch (Exception e) {
log.error("[route]外部调用失败,e:{}", e.getMessage());
LogHolder.badEnd(e.getMessage());
throw e;
}
LogHolder.happyEnd();
//构建响应结果
result = buildResp(responseStr, request);
return result;
}
protected abstract Object buildReq(ApiDefinition apiDefinition, Object originalParam);
protected abstract String doExecute(Request request, Object requestParam);
protected abstract Result<T> buildResp(String responseMsg, Request request);
}
package com.nuzar.fcms.framwork.route.template;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.nuzar.fcms.framwork.route.ApiDefinition;
import com.nuzar.fcms.framwork.route.Request;
import com.nuzar.fcms.framwork.route.Result;
import com.nuzar.fcms.framwork.route.TosHttpAbstractApiDefinition;
import com.nuzar.fcms.framwork.route.config.RouteServiceProperties;
import java.util.Map;
/**
*
**/
public class HttpTosTemplate<T> extends TemplateAbstract<T> {
@Override
protected Object buildReq(ApiDefinition apiDefinition, Object originalParam) {
return apiDefinition.convertReq().convert(originalParam);
}
@Override
protected String doExecute(Request request, Object requestParam) {
TosHttpAbstractApiDefinition httpApiDefinition = (TosHttpAbstractApiDefinition) request.getApiDefinition();
Map<String, String> header = httpApiDefinition.buildHeader();
httpApiDefinition.authType().getHandle().attach(request.getServiceConfig(), header);
RouteServiceProperties.ServiceConfig serviceConfig = request.getServiceConfig();
String url = "http://" + serviceConfig.getHost() + ":" + serviceConfig.getPort() + httpApiDefinition.getPath();
HttpRequest httpRequest = HttpUtil
.createRequest(httpApiDefinition.getMethod(), url)
.addHeaders(header).form(httpApiDefinition.buildForm(requestParam))
.body(httpApiDefinition.buildBody(requestParam));
HttpRequest rebuildRequest = httpApiDefinition.rebuildRequest(httpRequest);
HttpResponse execute = rebuildRequest.execute();
return execute.body();
}
@Override
protected Result<T> buildResp(String responseMsg, Request request) {
Result<T> parse = request.getApiDefinition().parseResp().parse(responseMsg, request.getReturnDataType());
return parse;
}
}
package com.nuzar.fcms.framwork.route;
import cn.hutool.core.lang.Assert;
import com.nuzar.fcms.framwork.route.annotations.EnableRouteApiClients;
import com.nuzar.fcms.framwork.route.annotations.RouteApiClient;
import com.nuzar.fcms.framwork.route.exception.RouteException;
import com.nuzar.fcms.framwork.route.exception.RouteExceptionEnum;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.*;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
public class RouteApiClientsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware, ResourceLoaderAware {
private ResourceLoader resourceLoader;
private Environment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(RouteApiClient.class));
Set<String> basePackages = getBasePackages(importingClassMetadata);
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(), () -> new RouteException(RouteExceptionEnum.ILLEGAL_ANNOTATION_TARGET, " @RouteApiClient 只能作用在接口上"));
// Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(RouteApiClient.class.getCanonicalName());
registerApiClient(registry, annotationMetadata);
}
}
}
private void registerApiClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata) {
String className = annotationMetadata.getClassName();
Class clazz = ClassUtils.resolveClassName(className, null);
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory ? ((ConfigurableBeanFactory) registry) : null;
RouteApiFactoryBean factoryBean = new RouteApiFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setType(clazz);
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz, factoryBean::getObject);
definitionBuilder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definitionBuilder.setLazyInit(true);
AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
beanDefinition.setAttribute("thirdApiClientsRegistrarFactoryBean", factoryBean);
boolean primary = true;
beanDefinition.setPrimary(primary);
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{});
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
Set<String> basePackages = new HashSet<>();
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableRouteApiClients.class.getCanonicalName());
for (String pkg : (String[]) annotationAttributes.get("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : (String[]) annotationAttributes.get("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
return basePackages;
}
private ClassPathScanningCandidateComponentProvider getScanner() {
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
boolean isCandidate = false;
if (beanDefinition.getMetadata().isIndependent()) {
if (!beanDefinition.getMetadata().isAnnotation()) {
isCandidate = true;
}
}
return isCandidate;
}
};
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
package com.nuzar.fcms.framwork.route;
import com.nuzar.fcms.framwork.route.annotations.EnableRouteApiClients;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;
import java.util.*;
public class RouteApiDefinitionRegistrar implements ImportBeanDefinitionRegistrar{
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Set<String> basePackages = getBasePackages(importingClassMetadata);
//定义一个扫描器
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
scanner.scan(basePackages.toArray(new String[0]));
}
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
Set<String> basePackages = new HashSet<>();
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableRouteApiClients.class.getCanonicalName());
for (String pkg : (String[]) annotationAttributes.get("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : (String[]) annotationAttributes.get("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
return basePackages;
}
}
registrar
package com.nuzar.fcms.framwork.route;
import com.nuzar.fcms.framwork.route.handler.MethodHandler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
public class RouteInvocationHandler implements InvocationHandler {
private final Map<Method, MethodHandler> dispatch;
public RouteInvocationHandler(Map<Method, MethodHandler> dispatch) {
this.dispatch = dispatch;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return dispatch.get(method).invoke(args);
}
}
InvocationHandler