欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Feign踩坑源碼解讀之@FeignClient注入容器

 更新時(shí)間:2024年11月12日 08:48:31   作者:碼猿手  
文章主要介紹了FeignClientsRegistrar類的作用和注冊Feign客戶端的過程,以及FeignClientFactoryBean的使用和目標(biāo)對象的獲取

一. @EnableFeignClients

1.1.類介紹

從上面注釋可以看出是掃描聲明了@FeignClient接口的類,還引入了 FeignClientsRegistrar類,從字面意思可以看出是進(jìn)行了 FeignClient 客戶端類的注冊。

1.2.FeignClientsRegistrar 詳解

最主要的一個(gè)方法:registerBeanDefinitions注冊bean定義信息,主要功能是實(shí)現(xiàn)向容器注冊Feign客戶端配置信息和所有的使用了@FeignClient的類;

1.2.1.registerDefaultConfiguration()

private void registerDefaultConfiguration(AnnotationMetadata metadata,
        BeanDefinitionRegistry registry) {
    // 獲取@EnableFeignClients注解的屬性和值
    Map<String, Object> defaultAttrs = metadata
            .getAnnotationAttributes(EnableFeignClients.class.getName(), true);
    // 獲取屬性中的默認(rèn)配置 defaultConfiguration,name是main主程序
    if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
        String name;
        if (metadata.hasEnclosingClass()) {
            name = "default." + metadata.getEnclosingClassName();
        }
        else {
            name = "default." + metadata.getClassName();
        }
        //默認(rèn)配置信息進(jìn)行容器注冊
        registerClientConfiguration(registry, name,
                defaultAttrs.get("defaultConfiguration"));
    }
}

1.2.1.1.registerClientConfiguration

private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
        Object configuration) {
    // 創(chuàng)建一個(gè) FeignClientSpecification.class 構(gòu)造器
    BeanDefinitionBuilder builder = BeanDefinitionBuilder
            .genericBeanDefinition(FeignClientSpecification.class);
    builder.addConstructorArgValue(name);
    builder.addConstructorArgValue(configuration);
    // 向容器中注冊默認(rèn)配置
    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)中取出進(jìn)行bean實(shí)例化
                    //此時(shí)是處于invokeBeanFactoryPostProcessors(beanFactory);階段,進(jìn)行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) {
        //獲取一個(gè)掃描器
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        //設(shè)置資源路徑
        scanner.setResourceLoader(this.resourceLoader);
        //包路徑
        Set<String> basePackages;
        //獲取EnableFeignClients注解的參數(shù)和值,有5個(gè)值,其中clients對應(yīng)的class[0]是沒有值的
        Map<String, Object> attrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName());
        //定義一個(gè) 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對應(yīng)的根路徑包
            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)));
        }
        //遍歷包路徑,獲取標(biāo)記@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的參數(shù)和值
                    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

    //定義一個(gè) FeignClientFactoryBean bean定義構(gòu)造器(重點(diǎn))
    BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
    ..........
    //前面根據(jù)attributes進(jìn)行屬性賦值后注入到Spring容器中
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

二.@FeignClient

從上面可以看到在注冊客戶端時(shí)注冊了一個(gè)FeignClientFactoryBean,所以FeignClient的獲取是從getObject()獲取的:

2.1.getTarget()

	<T> T getTarget() {
		//獲取創(chuàng)建feign實(shí)例的工廠
		FeignContext context = this.applicationContext.getBean(FeignContext.class);
		//獲取feign構(gòu)造器
		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();
			//創(chuàng)建實(shí)例
			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()

    //添加日志對象、編碼器、解碼器、解析規(guī)則器
    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等注解進(jìn)行解析
                .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) {
        //根據(jù)context上下文獲取客戶端實(shí)例,client:TraceLoadBalancerFeignClient,負(fù)載均衡
        Client client = getOptional(context, Client.class);
        if (client != null) {
            //客戶端創(chuàng)建
            builder.client(client);
            //獲取HystrixTargerer
            Targeter targeter = get(context, Targeter.class);
            //調(diào)用HystrixTargeter#target進(jìn)行實(shí)例創(chuàng)建
            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) {
    // 不是同一種類型,進(jìn)入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() {
      //同步方法處理工廠,構(gòu)建了日志級別信息
      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>();
    //默認(rèn)方法存放集合
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
    //對feign類方法進(jìn)行遍歷
    for (Method method : target.type().getMethods()) {
      if (method.getDeclaringClass() == Object.class) {
        continue;
        //默認(rèn)方法
      } 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動態(tài)代理創(chuàng)建對象
    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;
  }

總結(jié)

主要應(yīng)用了FactoryBean的特性getObject()進(jìn)行jdk動態(tài)創(chuàng)建一個(gè)ReflectiveFeign的代理對象。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring中@ConditionalOnProperty注解的作用詳解

    Spring中@ConditionalOnProperty注解的作用詳解

    這篇文章主要介紹了Spring中@ConditionalOnProperty注解的作用詳解,@ConditionalOnProperty注解主要是用來判斷配置文件中的內(nèi)容來決定配置類是否生效用的,如果條件不匹配,則配置類不生效,需要的朋友可以參考下
    2024-01-01
  • flink進(jìn)階富函數(shù)生命周期介紹

    flink進(jìn)階富函數(shù)生命周期介紹

    這篇文章主要為大家介紹了flink進(jìn)階富函數(shù)生命周期的舉例介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • mybatis-plus報(bào)錯(cuò)net.sf.jsqlparser.statement.select.SelectBody的問題解決

    mybatis-plus報(bào)錯(cuò)net.sf.jsqlparser.statement.select.SelectBody的

    本文主要介紹了mybatis-plus報(bào)錯(cuò)net.sf.jsqlparser.statement.select.SelectBody的問題解決,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-08-08
  • java實(shí)現(xiàn)坦克大戰(zhàn)小游戲

    java實(shí)現(xiàn)坦克大戰(zhàn)小游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)坦克大戰(zhàn)小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-01-01
  • Struts2 控制文件上傳下載功能實(shí)例代碼

    Struts2 控制文件上傳下載功能實(shí)例代碼

    這篇文章主要介紹了Struts2 控制文件上傳下載功能實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-05-05
  • java操作Apache druid的實(shí)例代碼

    java操作Apache druid的實(shí)例代碼

    這篇文章主要介紹了java操作Apache druid的實(shí)例代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Java一致性Hash算法的實(shí)現(xiàn)詳解

    Java一致性Hash算法的實(shí)現(xiàn)詳解

    這篇文章主要介紹了Java一致性Hash算法的實(shí)現(xiàn)詳解,hash的意思是散列,目的將一組輸入的數(shù)據(jù)均勻的分開、打散,往往用來配合路由算法做負(fù)載均衡,多用在分布式系統(tǒng)中,需要的朋友可以參考下
    2024-01-01
  • Java設(shè)計(jì)模式之責(zé)任鏈模式(Chain of Responsibility模式)介紹

    Java設(shè)計(jì)模式之責(zé)任鏈模式(Chain of Responsibility模式)介紹

    這篇文章主要介紹了Java設(shè)計(jì)模式之責(zé)任鏈模式(Chain of Responsibility模式)介紹,本文講解了如何使用責(zé)任鏈模式,并給出了4種使用實(shí)例,需要的朋友可以參考下
    2015-03-03
  • 詳解Java函數(shù)式編程和lambda表達(dá)式

    詳解Java函數(shù)式編程和lambda表達(dá)式

    這篇文章主要介紹了Java函數(shù)式編程和lambda表達(dá)式,對lambda感興趣的同學(xué),一定要看一下
    2021-04-04
  • 全面剖析java中的注解(Annotation)

    全面剖析java中的注解(Annotation)

    一個(gè)詞就可以描述注解,那就是元數(shù)據(jù),即一種描述數(shù)據(jù)的數(shù)據(jù)。所以,可以說注解就是源代碼的元數(shù)據(jù)。文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04

最新評論