前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >仿写fegin

仿写fegin

作者头像
分享干货的你
发布2021-04-06 16:30:52
6420
发布2021-04-06 16:30:52
举报
文章被收录于专栏:分享干货的你分享干货的你

我们都知道fegin 是一个很好用的远程调用工具, 底层就是restTemplate 实现的。但是具体他是怎么工作的,这里我们我们今天来自己实现一下, 仿写一个feign。

我们先设计一下目录结构,

1, 自定义注解扫描feignScan 扫描 ,带有自定义注解feignClient 标记的注册进去。

2, 自定义factoryBean 用代理+resttempalte 实现真正的调用。

里面主要用到的是 ClassPathBeanDefinitionScanner,ImportBeanDefinitionRegistrar , cglib 代理

项目结构:

3, 先定义注解

代码语言:javascript
复制
/**
 *  开启feign的使用, 在启动类上面定义扫描的包路径。
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(FeignRegister.class)// 使用了自定义bean的注册器
public @interface EnableFeign {

    //扫描包的路径
    String[] basePackages() default {};
}


/**
 *  用来声明加入spring的ioc 容器里面 , 在获取baseUrl 的值。
 *  baseurl  可以用类似 @value 注解的方式从配置文件读取。
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {

    //根部的url,可以从配置文件读取 , 必传。
    String baseUrl();
}



/**
 * 作用在方法上面,表明请求的url 和方法。方法是一个枚举类型 
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignRequest {
    //请求url
    String url();

    //默认请求方式get
    RequestMethonEnum methon() default RequestMethonEnum.GET;
}

/**
 *  请求方式枚举类型
 */
public enum RequestMethonEnum {

    GET,POST,DELETE
}

4, 配置类

代码语言:javascript
复制
// 注册类
public class FeignRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    Logger logger = LoggerFactory.getLogger(FeignRegister.class);


    private Environment environment;

    /**
     *  要将环境变量的配置加进去
     * @param environment
     */
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Set<String> packages = getPackages(importingClassMetadata);
        CustomScanner customScanner = new CustomScanner(registry, environment);
        customScanner.scan(packages.toArray(new String[0]));
    }

    /**
     *  获取包名称,并且去重
     * @param metadata
     * @return
     */
    private Set<String> getPackages(AnnotationMetadata metadata) {
        AnnotationAttributes attributes =
                AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(EnableFeign.class.getName()));
        // 获取basePackages 的属性
        String[] basePackages = attributes.getStringArray("basePackages");
        return new LinkedHashSet<>(Arrays.asList(basePackages));
    }
}

// 扫描类 
public class CustomScanner extends ClassPathBeanDefinitionScanner {

    Logger logger = LoggerFactory.getLogger(CustomScanner.class);

    private Environment environment;

    /**
     *  构造器给register 注册用的
     * @param registry
     * @param environment
     */
    public CustomScanner(BeanDefinitionRegistry registry,Environment environment) {
            super(registry, false);
            this.environment = environment;
            registerFilter();
        }


    public void registerFilter () {
            // 表示要过滤出带有 feignClient 注解的类
            addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
        }

        //扫描包下待有`@feignClient` 注解的接口,调用 processBeanDefinitions() 实现接口代理类生成注册
        @Override
        protected Set<BeanDefinitionHolder> doScan (String...basePackages){
            Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
            if (beanDefinitions.isEmpty()) {
                logger.warn("No @feignClient interface was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
            } else {
                // 执行以下bean的注册
                processBeanDefinitions(beanDefinitions);
            }
            return beanDefinitions;
        }

        /**
         * 重写候选判断逻辑,捞出带有注解的接口
         *
         * @param beanDefinition
         * @return
         */
        @Override
        protected boolean isCandidateComponent (AnnotatedBeanDefinition beanDefinition){
            //interface  接口会自动跳过, 这里要把接口加进去beanDefinition 里面
            boolean isCandidate = false;
            AnnotationMetadata metadata = beanDefinition.getMetadata();
            if (metadata.isIndependent()) {
                if ( !metadata.isAnnotation() && metadata.hasAnnotation(FeignClient.class.getName())) {
                    isCandidate = true;
                }
            }
            return isCandidate;
        }

    /**
     *  真正进行bean 的注册
     * @param beanDefinitions
     */
    private void processBeanDefinitions (Set < BeanDefinitionHolder > beanDefinitions) {
                GenericBeanDefinition definition;
            for (BeanDefinitionHolder holder : beanDefinitions) {
                definition = (GenericBeanDefinition) holder.getBeanDefinition();
                // definition.getConstructorArgumentValues(),BeanClass需要提供包含该属性的构造方法,否则会注入失败
                definition.getConstructorArgumentValues().addGenericArgumentValue(Objects.requireNonNull(definition.getBeanClassName()));
                // 获取注解的元注解对象
                AnnotationMetadata beanMetadata = ((AnnotatedBeanDefinition) definition).getMetadata();
                AnnotationAttributes attributes = AnnotationAttributes.fromMap(beanMetadata.getAnnotationAttributes(FeignClient.class.getName()));
                //解析interface上@FeignClientInfo注解信息,转换成FeignClientInfo对象
                FeignClientInfo feignClientInfo = new FeignClientInfo(attributes, environment);
                // 使用自己的factoryBean
                definition.setBeanClass(FeignFactoryBean.class);
                //FactoryBean属性
                definition.getPropertyValues().addPropertyValue("feignClientInfo", feignClientInfo);
            }
        }

}

public class FeignFactoryBean<T> implements FactoryBean<T> , ApplicationContextAware {

    Logger logger = LoggerFactory.getLogger(FeignFactoryBean.class);

    private Class<T> targetClass;

    private ApplicationContext applicationContext;

    private FeignClientInfo feignClientInfo;

    public FeignFactoryBean(Class<T> targetClass) {
        this.targetClass = targetClass;
    }



    @Override
    public boolean isSingleton() {
        return false;
    }

    @Override
        @SuppressWarnings("unchecked")
        public T getObject() throws Exception {
            return ProxyUtil.newProxyInstance(targetClass, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 获取resttemplate
                    RestTemplate restTemplate = applicationContext.getBean(RestTemplate.class);
                    MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
                    // 参数绑定
                    for (int index = 0; index < args.length; index++) {
                        Parameter p = method.getParameters()[index];
                        params.add(p.getName(), args[index]);
                    }
                    String url = feignClientInfo.getBaseUrl();
                    FeignRequest annotation = method.getAnnotation(FeignRequest.class);
                    logger.info("FeignRequest注解上面的url是: "+url);
                    RequestMethonEnum methon = annotation.methon();
                    String response = null;
                    // 这里只写了post 
                    switch (methon){
                        case POST :
                            response   = restTemplate.postForObject(url+annotation.url(), params, String.class);
                            break;
                    }

                    // 判断返回类型是不是string
                    if (method.getReturnType() == String.class) {
                        return response;
                    }
                    return JSONObject.parseObject(response, method.getReturnType());
                }
            });
        }

    @Override
    public Class<?> getObjectType() {
        return targetClass;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

            this.applicationContext=applicationContext;
    }

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public FeignClientInfo getFeignClientInfo() {
        return feignClientInfo;
    }

    public void setFeignClientInfo(FeignClientInfo feignClientInfo) {
        this.feignClientInfo = feignClientInfo;
    }
}

@Configuration
public class RestTemplateConfig {

    /**
     *  生命restTemplate是一个bean 
     * @return
     */
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplateBuilder().build();
    }
}

5, 具体使用

启动项目看一下日志

postman 调一下看一下结果

参数是我写死的,这里只是测试,最终能返回json 格式的response 就行了。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-03-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 分享干货的你 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档