All Downloads are FREE. Search and download functionalities are using the official Maven repository.

group.flyfish.rest.registry.proxy.RestProxyInvoker Maven / Gradle / Ivy

There is a newer version: 1.2.0
Show newest version
package group.flyfish.rest.registry.proxy;

import com.fasterxml.jackson.databind.JavaType;
import group.flyfish.rest.annotation.AutoMapping;
import group.flyfish.rest.annotation.RestService;
import group.flyfish.rest.configuration.RestClientProperties;
import group.flyfish.rest.configuration.configure.PropertiesConfigurable;
import group.flyfish.rest.core.auth.RestAuthProvider;
import group.flyfish.rest.core.client.RestClient;
import group.flyfish.rest.core.client.RestClientBuilder;
import group.flyfish.rest.mapping.RestResultMapping;
import group.flyfish.rest.registry.RestApiRegistry;
import group.flyfish.rest.registry.proxy.entity.RestMethod;
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 lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.config.RequestConfig;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

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.concurrent.ConcurrentHashMap;

/**
 * Rest代理执行器
 *
 * @author wangyu
 */
@Slf4j
public class RestProxyInvoker implements InvocationHandler, PropertiesConfigurable {

    // 方法缓存
    private final Map methods = new ConcurrentHashMap<>();
    // 要代理的目标类
    private final Class targetType;
    // 服务映射
    private final RestService restService;
    // 配置属性
    @Getter
    private RestClientProperties properties;
    // 初始的基本路径
    @Getter
    private String baseUrl;
    // 超时时间
    private RequestConfig config;
    // 注册器,包含基础信息
    private final RestApiRegistry registry;
    // 结果映射
    private RestResultMapping mapping;
    // 鉴权提供者
    private RestAuthProvider authProvider;


    /**
     * 构造器
     *
     * @param targetType 目标类型
     * @param registry   注册器
     */
    private RestProxyInvoker(Class targetType, RestApiRegistry registry) {
        this.targetType = targetType;
        this.registry = registry;
        // 注解的优先级高于全局基本路径
        this.restService = AnnotationUtils.findAnnotation(targetType, RestService.class);
        Assert.notNull(restService, "当前类尚未添加@RestService注解!");
    }

    /**
     * 生产一个实现类
     *
     * @param target 目标
     * @param     泛型
     * @return 结果
     */
    public static  T produce(Class target, RestApiRegistry registry) {
        RestProxyInvoker invoker = new RestProxyInvoker(target, registry);
        RestInvokers.add(invoker);
        return DataUtils.cast(Proxy.newProxyInstance(target.getClassLoader(), new Class[]{target}, invoker));
    }

    /**
     * 完成配置
     *
     * @param properties 属性
     */
    @Override
    public void configure(RestClientProperties properties) {
        this.properties = properties;
        this.config = RequestConfig.custom().setConnectTimeout((int) properties.getConnectionTimeout().toMillis()).setSocketTimeout(restService.timeout()).build();
        this.baseUrl = this.findBaseUrl();
        this.authProvider = determineAuthProvider();
    }

    /**
     * 执行rest请求的地方,这里很简单易懂
     *
     * @param proxy  代理对象
     * @param target 代理方法
     * @param args   参数
     * @return 结果
     * @throws Throwable 可能抛出的异常
     */
    @Override
    public Object invoke(Object proxy, Method target, Object[] args) throws Throwable {
        // 解析方法,做基本验证
        RestMethod method = methods.computeIfAbsent(target.hashCode(), k -> RestMethod.resolve(target, this));
        if (method.isInvalid()) {
            throw new IllegalAccessException("【Rest调用】未声明rest配置的方法被调用!请检查代码!");
        }
        RestArgumentResolverComposite composite = registry.getComposite();
        // 第一步就解析参数
        ArgumentResolveContext context = composite.resolve(method, args);
        // 构造和调用,这里的restClient不保存状态
        RestClientBuilder builder = RestClient.create().url(resolveUrl(method.getUrl(), context))
                .method(method.getMethod())
                .config(config);
        // 需要带cookie的带上
        if (method.isCredentials()) {
            builder.withCredential();
        }
        // 判断情况,赋值参数
        if (context.hasBody()) {
            builder.body(context.getBody());
        }
        // 赋值文件体
        if (context.hasMultipart()) {
            builder.multipart();
            context.getFiles().forEach((key, value) -> {
                value.setFilename(context.getFilename(key, value.getFilename()));
                builder.addMultipartBody(value);
            });
        }
        // 赋值参数们
        if (context.hasParams()) {
            builder.queryParams(context.getParam());
        }
        // 赋值头
        if (context.hasHeaders()) {
            builder.headers(context.getHeaders());
        }
        // 添加鉴权信息
        if (null != authProvider) {
            authProvider.provide(builder);
        }
        // 构建客户端
        RestClient client = builder.build();
        // 设置客户端
        client.setClient(registry.getProvider());
        // 是否对结果进行映射
        boolean map = null != mapping && method.isBare();
        // 执行请求
        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, RestMethod 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() {
        // 当且仅当存在时进入
        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 resolveUrl(String url, ArgumentResolveContext context) {
        // 尝试解析路径参数
        return context.hasPathParams() ? UrlCompiler.compile(url, context.getPathParams()) : url;
    }

    /**
     * 确定鉴权提供者
     *
     * @return 实例化的提供者
     */
    @SneakyThrows
    private RestAuthProvider determineAuthProvider() {
        Class candidate = restService.authProvider();
        if (ClassUtils.isAssignable(RestAuthProvider.class, candidate)) {
            return (RestAuthProvider) candidate.newInstance();
        }
        return properties.getAuthProvider();
    }

    /**
     * 如果调用到了Object类的方法,则绕行
     *
     * @param method 方法
     * @param args   参数
     * @return 结果
     */
    @Deprecated
    private Object passObjectMethod(Method method, Object[] args) {
        // 处理基本方法被代理的情况
        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;
        }
        return null;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy