• 通过实现仿照FeignClient框架原理的示例来看清FeignClient的本质


    前言

    FeignClient的实现原理网上一搜一大把,此处我就不详细再说明,比如:Feign原理 (图解) - 疯狂创客圈 - 博客园 (cnblogs.com),而且关于FeignClient的使用技巧我之前文章《feignclient各种使用技巧说明》已经讲过,此处仅说一下核心步骤:

    1. 启动时:@EnableFeignClients注解-->@Import(FeignClientsRegistrar.class)-->FeignClientsRegistrar.registerBeanDefinitions-->org.springframework.cloud.netflix.feign.FeignClientsRegistrar#registerFeignClients-->扫描有添加了@FeignClient注解的接口类注解BEAN元信息列表【即:AnnotatedBeanDefinition】-->org.springframework.cloud.netflix.feign.FeignClientsRegistrar#registerFeignClient-->构建一个FeignClientFactoryBean的BeanDefinitionBuilder,并将type等相关信息设置给FeignClientFactoryBean,-->BeanDefinitionReaderUtils.registerBeanDefinition【即注册成FactoryBean】;

    2. 实际注入FeignClient接口类依赖时:根据FeignClient接口类class找到FeignClientFactoryBean对象实例-->org.springframework.cloud.netflix.feign.FeignClientFactoryBean#getObject-->org.springframework.cloud.netflix.feign.FeignClientFactoryBean#feign【得到Feign.Builder】-->targeter = get(context, Targeter.class);-->targeter.target-->feign.target(target)-->feign.Feign.Builder#build-->feign.ReflectiveFeign#newInstance-->handler = factory.create(target, methodToHandler)【得到InvocationHandler】

    3. 执行时:(feign.hystrix.HystrixInvocationHandler【feign.hystrix.enabled=true时】 OR feign.ReflectiveFeign.FeignInvocationHandler#)#invoke -->dispatch.get(method).invoke(args);【得到代理方法SynchronousMethodHandler并执行该方法】-->Client#execute【Client的实现类,其中:LoadBalancerFeignClient 是使用ribbon组件时默认实现的】

    上面核心步骤其实也还是很多,我这里一句概括核心:将@FeignClient标注的接口类通过FeignClientFactoryBean生成代理类(InvocationHandler,注意有多种实现子类),再执行InvocationHandler.invoke方法,间接执行内部的MethodHandler(SynchronousMethodHandler实现类之一)invoke方法,最后由实际的Client来完成远程URL请求及响应结果转换;其中最重要也是复杂的是InvocationHandler的实现类、MethodHandler的实现类;

    FeignClient的扩展点非常多,比如:FeignClientsConfiguration 类中所有默认配置均可以自行替换自定义的实现类,若需单个FeignClient生效,则可通过@FeignClient注解的configuration属性指明对应这个FeignClient的特有配置类(如:MyFeignClientConfiguration)【注意自定义的配置类此处不能使用@Configuration注解,否则将导致全局生效,不加@Configuration注解时,则会由对应的contextId的FeignClientContext单独创建】

    那么说了这么多,为了大家能够理解FeignClient的核心实现原理,同时因为我项目中也要实现类似的功能(目的让开发人员对复杂部份透明,调用远程BEAN的方法就像调本地一样,即RPC的初衷),我(梦在旅途 www.zuowenjun.cn)通过实现仿照FeignClient框架原理的示例来看清FeignClient的本质,代码全部贴出来了,大家应该一看就懂,不懂复制到DEMO项目中DEBUG起来就也就明白了。实际运行的结果符合预期;

    1. 定义注解

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface DemoClient {
        //定义相关的注解属性
    }
    

    2. 定义标注了@DemoClient注解接口对应的真实代理类FactoryBean

    public class DemoClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
    
        private Class<?> type;
    
        private ApplicationContext context;
    
        //可添加其它属性字段(同时记得至少添加setter方法)
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.context = applicationContext;
        }
    
        @Override
        public Object getObject() throws Exception {
            return Proxy.newProxyInstance(RemoteTestClientFactoryBean.class.getClassLoader(), new Class<?>[]{this.type}, 自定义代理对象处理类【需继承自InvocationHandler,也可以直接采用lambda表达式】,这里也是真正的执行业务逻辑的核心);
        }
    
    
        @Override
        public Class<?> getObjectType() {
            return this.type;
        }
    
        @Override
        public boolean isSingleton() {
            return true;
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
    
        }
    
        /**
         * 设置当前工厂要生成的BEAN Type,由BeanWrapper动态赋值,内部支持根据class String直接转换为Class对象
         *
         * @param type
         */
        public void setType(Class<?> type) {
            
            this.type = type;
        }
    }
    

    3. 定义扫描标注了@DemoClient注解接口并自动注册为如上第2步定义的FactoryBean的Bean

    public class DemoClientsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
    
        private Environment environment;
    
        @Override
        public void setEnvironment(Environment environment) {
            this.environment=environment;
        }
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
            ClassPathScanningCandidateComponentProvider scanner=getScanner();
            scanner.addIncludeFilter(new AnnotationTypeFilter(RemoteTestClient.class));
            Set<BeanDefinition>  candidateComponents=scanner.findCandidateComponents(ClassUtils.getPackageName(Application.class));
            if (CollectionUtils.isEmpty(candidateComponents)){
                return;
            }
    
            for (BeanDefinition candidateComponent : candidateComponents) {
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                    AnnotationMetadata metadata = beanDefinition.getMetadata();
                    Assert.isTrue(metadata.isInterface(),
                            "@DemoClient can only be specified on an interface");
    
                    registerDemoClient(beanDefinitionRegistry,metadata);
                }
            }
    
        }
    
        private void  registerDemoClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata){
            String className = annotationMetadata.getClassName();
            BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(DemoClientFactoryBean.class);
            //这里将类名传给class<?>属性,看似类型不匹配,其实赋值时会由BeanWrapper进行自动转换
            definition.addPropertyValue("type",className);
            //这里还可以赋值更多属性,具体依据DemoClientFactoryBean的属性定义
            registry.registerBeanDefinition(className + "DemoClient",definition.getBeanDefinition());
        }
    
        protected ClassPathScanningCandidateComponentProvider getScanner() {
            return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
    
                @Override
                protected boolean isCandidateComponent(
                        AnnotatedBeanDefinition beanDefinition) {
                    //必需是独立的 且 是接口 才是符合扫描条件的【可以更严格的过滤判断】
                    if (beanDefinition.getMetadata().isIndependent() && beanDefinition.getMetadata().isInterface()) {
                        if (beanDefinition.getMetadata().isInterface()
                                && beanDefinition.getMetadata()
                                .getInterfaceNames().length == 1
                                && Annotation.class.getName().equals(beanDefinition
                                .getMetadata().getInterfaceNames()[0])) {
                            try {
                                Class<?> target = ClassUtils.forName(
                                        beanDefinition.getMetadata().getClassName(),
                                        RemoteTestClientsRegistrar.class.getClassLoader());
                                return !target.isAnnotation();
                            }
                            catch (Exception ex) {
                                this.logger.error(
                                        "Could not load target class: "
                                                + beanDefinition.getMetadata().getClassName(),
                                        ex);
    
                            }
                        }
                        return true;
                    }
                    return false;
    
                }
            };
        }
    
    
    }
    

    4.定义配置启动扫描并注册代理Bean的注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(DemoClientsRegistrar.class)
    public @interface EnableDemoClient {
    
    }
    

    5.最后将第4步定义的注解@EnableDemoClient添加到Spring Applcation入口类上即可

    @SpringBootApplication
    @EnableDemoClient
    public class Application {
    
        public static void main(String[] args){
            SpringApplication.run(Application.class,args);
        }
    }
    

    实际用法示例如下:

    @DemoClient
    public interface Demo1Client{
        String getRemoteResult(Long id);
    }
    
    @Service
    public class Demo1Service{
        @Autowired
        private Demo1Client demo1Client;//此处实际注入的是DemoClientFactoryBean.getObject方法返回的InvocationHandler的代理类实例
        
        public String doMany(Long id){
            return demo1Client.getRemoteResult(id);//实际调用的是:InvocationHandler的代理类实例的invoke方法
        }
    }
    
  • 相关阅读:
    批处理命令之实现修改环境变量的值
    【hihocoder 1304】搜索一·24点
    【hihocoder 1297】数论四·扩展欧几里德
    【hihocoder 1298】 数论五·欧拉函数
    【hihocoder 1303】模线性方程组
    C语言如何动态分配二维数组
    Istream中的函数
    string 与 char * 转换
    boost 系列 1:boost 直接使用
    glog功能介绍 一分钟 51CTO技术博客
  • 原文地址:https://www.cnblogs.com/zuowj/p/15535271.html
Copyright © 2020-2023  润新知