一. @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(对于FactoryBean的扩展知识和案例),所以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; }
文章来源:https://www.toymoban.com/news/detail-438063.html
三.总结
主要应用了FactoryBean的特性getObject()进行jdk动态创建一个ReflectiveFeign的代理对象。文章来源地址https://www.toymoban.com/news/detail-438063.html
到了这里,关于Feign踩坑源码分析--@FeignClient注入容器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!