仿写fegin

2021-04-06 16:30:52 浏览数 (3)

我们都知道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 就行了。

0 人点赞