FeignClient的实现原理网上一搜一大把,此处我就不详细再说明,比如:Feign原理 (图解) - 疯狂创客圈 - 博客园 (cnblogs.com),而且关于FeignClient的使用技巧我之前文章《feignclient各种使用技巧说明》已经讲过,此处仅说一下核心步骤:
启动时:@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】;
实际注入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】
执行时:(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的初衷),我(梦在旅途 )通过实现仿照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); } }