Feign踩坑源码解读之@FeignClient注入容器
一. @EnableFeignClients
1.1.类介绍
从上面注释可以看出是扫描声明了@FeignClient接口的类,还引入了 FeignClientsRegistrar类,从字面意思可以看出是进行了 FeignClient 客户端类的注册。
1.2.FeignClientsRegistrar 详解
最主要的一个方法:registerBeanDefinitions注册bean定义信息,主要功能是实现向容器注册Feign客户端配置信息和所有的使用了@FeignClient的类;
1.2.1.registerDefaultConfiguration()
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { // 获取@EnableFeignClients注解的属性和值 Map<String, Object> defaultAttrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName(), true); // 获取属性中的默认配置 defaultConfiguration,name是main主程序 if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) { String name; if (metadata.hasEnclosingClass()) { name = "default." + metadata.getEnclosingClassName(); } else { name = "default." + metadata.getClassName(); } //默认配置信息进行容器注册 registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration")); } }
1.2.1.1.registerClientConfiguration
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { // 创建一个 FeignClientSpecification.class 构造器 BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition(FeignClientSpecification.class); builder.addConstructorArgValue(name); builder.addConstructorArgValue(configuration); // 向容器中注册默认配置 registry.registerBeanDefinition( name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition()); }
1.2.1.2.registerBeanDefinition
核心代码在:org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { ............ if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { //放到beanDefinitionMap,到AbstractApplicationContext#finishBeanFactoryInitialization(beanFactory)中取出进行bean实例化 //此时是处于invokeBeanFactoryPostProcessors(beanFactory);阶段,进行BeanDefinitionRegistryPostProcessor的处理 this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; removeManualSingletonName(beanName); } } .......... }
1.2.2.registerFeignClients()
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //获取一个扫描器 ClassPathScanningCandidateComponentProvider scanner = getScanner(); //设置资源路径 scanner.setResourceLoader(this.resourceLoader); //包路径 Set<String> basePackages; //获取EnableFeignClients注解的参数和值,有5个值,其中clients对应的class[0]是没有值的 Map<String, Object> attrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName()); //定义一个 FeignClient 注解类型过滤器 AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter( FeignClient.class); //clients为class[0],长度为0 final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients"); if (clients == null || clients.length == 0) { //FeignClient 注解过滤器添加到扫描器中 scanner.addIncludeFilter(annotationTypeFilter); //获取Application对应的根路径包 basePackages = getBasePackages(metadata); } //clients不为空:FeignClient 注解过滤器添加到扫描器中、获取包路径 else { final Set<String> clientClasses = new HashSet<>(); basePackages = new HashSet<>(); for (Class<?> clazz : clients) { basePackages.add(ClassUtils.getPackageName(clazz)); clientClasses.add(clazz.getCanonicalName()); } AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() { @Override protected boolean match(ClassMetadata metadata) { String cleaned = metadata.getClassName().replaceAll("\\$", "."); return clientClasses.contains(cleaned); } }; scanner.addIncludeFilter( new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter))); } //遍历包路径,获取标记@FeignClient注解的接口向容器中注入 for (String basePackage : basePackages) { Set<BeanDefinition> candidateComponents = scanner .findCandidateComponents(basePackage); for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { // verify annotated class is an interface AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface"); //获取@FeignClient的参数和值 Map<String, Object> attributes = annotationMetadata .getAnnotationAttributes( FeignClient.class.getCanonicalName()); String name = getClientName(attributes); //再次更新配置 registerClientConfiguration(registry, name, attributes.get("configuration")); //注册 registerFeignClient(registry, annotationMetadata, attributes); } } } }
1.2.2.2.registerFeignClient
//定义一个 FeignClientFactoryBean bean定义构造器(重点) BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class); .......... //前面根据attributes进行属性赋值后注入到Spring容器中 BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
二.@FeignClient
从上面可以看到在注册客户端时注册了一个FeignClientFactoryBean,所以FeignClient的获取是从getObject()获取的:
2.1.getTarget()
<T> T getTarget() { //获取创建feign实例的工厂 FeignContext context = this.applicationContext.getBean(FeignContext.class); //获取feign构造器 Feign.Builder builder = feign(context); //url拼接 if (!StringUtils.hasText(this.url)) { //拼接name if (!this.name.startsWith("http")) { this.url = "http://" + this.name; } else { this.url = this.name; } //拼接path this.url += cleanPath(); //创建实例 return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type, this.name, this.url)); } if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) { this.url = "http://" + this.url; } String url = this.url + cleanPath(); Client client = getOptional(context, Client.class); if (client != null) { if (client instanceof LoadBalancerFeignClient) { // not load balancing because we have a url, // but ribbon is on the classpath, so unwrap client = ((LoadBalancerFeignClient) client).getDelegate(); } builder.client(client); } Targeter targeter = get(context, Targeter.class); return (T) targeter.target(this, builder, context, new HardCodedTarget<>(this.type, this.name, url)); }
2.1.1. feign()
//添加日志对象、编码器、解码器、解析规则器 protected Feign.Builder feign(FeignContext context) { FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class); Logger logger = loggerFactory.create(this.type); // @formatter:off Feign.Builder builder = get(context, Feign.Builder.class) // required values .logger(logger) .encoder(get(context, Encoder.class)) .decoder(get(context, Decoder.class)) //SpringMvcContract:对RequestMapping、RequestParam、RequestHeader等注解进行解析 .contract(get(context, Contract.class)); // @formatter:on configureFeign(context, builder); return builder; }
2.1.2. loadBalance()
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) { //根据context上下文获取客户端实例,client:TraceLoadBalancerFeignClient,负载均衡 Client client = getOptional(context, Client.class); if (client != null) { //客户端创建 builder.client(client); //获取HystrixTargerer Targeter targeter = get(context, Targeter.class); //调用HystrixTargeter#target进行实例创建 return targeter.target(this, builder, context, target); } throw new IllegalStateException( "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?"); }
2.1.3. HystrixTargeter#target
class HystrixTargeter implements Targeter { @Override public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) { // 不是同一种类型,进入feign#target方法 if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) { return feign.target(target); } ...... }
2.1.4. Feign.Builder#target
public <T> T target(Target<T> target) { return build().newInstance(target); }
2.1.4.1. Feign.Builder#target
public Feign build() { //同步方法处理工厂,构建了日志级别信息 SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy); //解析信息:编码、解密、同步方法处理工厂 ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); //new对象 return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); }
2.1.4.2.ReflectiveFeign#newInstance
@Override public <T> T newInstance(Target<T> target) { //得到feign类的定义方法 Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target); //定义方法存放集合 Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>(); //默认方法存放集合 List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>(); //对feign类方法进行遍历 for (Method method : target.type().getMethods()) { if (method.getDeclaringClass() == Object.class) { continue; //默认方法 } else if (Util.isDefault(method)) { DefaultMethodHandler handler = new DefaultMethodHandler(method); defaultMethodHandlers.add(handler); methodToHandler.put(method, handler); } else { methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method))); } } //jdk动态代理创建对象 InvocationHandler handler = factory.create(target, methodToHandler); T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler); for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { defaultMethodHandler.bindTo(proxy); } return proxy; }
总结
主要应用了FactoryBean的特性getObject()进行jdk动态创建一个ReflectiveFeign的代理对象。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
SpringBoot3结合gRpc实现远程服务调用的流程步骤
gRPC是一个现代开源高性能远程过程调用(RPC)框架,可以在任何环境中运行,它由Google开发,旨在帮助开发人员更轻松地构建分布式应用,特别是当代码可能在不同地方运行的时候,本文介绍了SpringBoot3结合gRpc实现远程服务调用的流程步骤,需要的朋友可以参考下2024-07-07Springboot项目因为kackson版本问题启动报错解决方案
这篇文章主要介绍了Springboot项目因为kackson版本问题启动报错解决方案,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2020-07-07RestTemplat中关于getForobject方法的使用
这篇文章主要介绍了RestTemplat中关于getForobject方法的使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2023-07-07
最新评论