前言 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的初衷),我(梦在旅途 www.zuowenjun.cn)通过实现仿照FeignClient框架原理的示例来看清FeignClient的本质,代码全部贴出来了,大家应该一看就懂,不懂复制到DEMO项目中DEBUG起来就也就明白了。实际运行的结果符合预期;
1. 定义注解 1 2 3 4 5 6 7 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface DemoClient { }
2. 定义标注了@DemoClient注解接口对应的真实代理类FactoryBean 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class DemoClientFactoryBean implements FactoryBean <Object>, InitializingBean, ApplicationContextAware { private Class<?> type; private ApplicationContext context; @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 { } public void setType (Class<?> type) { this .type = type; } }
3. 定义扫描标注了@DemoClient注解接口并自动注册为如上第2步定义的FactoryBean的Bean 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 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); definition.addPropertyValue("type" ,className); 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的注解 1 2 3 4 5 6 7 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(DemoClientsRegistrar.class) public @interface EnableDemoClient {}
5.最后将第4步定义的注解@EnableDemoClient添加到Spring Applcation入口类上即可 1 2 3 4 5 6 7 8 @SpringBootApplication @EnableDemoClient public class Application { public static void main (String[] args) { SpringApplication.run(Application.class,args); } }
实际用法示例如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @DemoClient public interface Demo1Client { String getRemoteResult (Long id) ; } @Service public class Demo1Service { @Autowired private Demo1Client demo1Client; public String doMany (Long id) { return demo1Client.getRemoteResult(id); } }