手写feign路由框架


/**
 * 约束定义 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 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值