Feign踩坑源碼解讀之@FeignClient注入容器
一. @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注解的作用詳解,@ConditionalOnProperty注解主要是用來判斷配置文件中的內(nèi)容來決定配置類是否生效用的,如果條件不匹配,則配置類不生效,需要的朋友可以參考下2024-01-01mybatis-plus報(bào)錯(cuò)net.sf.jsqlparser.statement.select.SelectBody的
本文主要介紹了mybatis-plus報(bào)錯(cuò)net.sf.jsqlparser.statement.select.SelectBody的問題解決,具有一定的參考價(jià)值,感興趣的可以了解一下2024-08-08java實(shí)現(xiàn)坦克大戰(zhàn)小游戲
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)坦克大戰(zhàn)小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01Java設(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á)式,對lambda感興趣的同學(xué),一定要看一下2021-04-04