zoukankan      html  css  js  c++  java
  • Feign源码的入口

    Feign源码的入口

    首先看一下demo工具的整体结构,ServiceB和ServiceA都要注册到eureka中,而serviceA-api是一个通用的api模块,由于Feign具有继承的特性,所以把接口都抽取出来了。

    image-20211019103201640

    具体的代码如下:

    @RequestMapping("/user")  
    public interface ServiceAInterface {
    
    	@RequestMapping(value = "/sayHello/{id}", method = RequestMethod.GET)
    	String sayHello(@PathVariable("id") Long id,
                        @RequestParam("name") String name,
                        @RequestParam("age") Integer age);
    
    	@RequestMapping(value = "/", method = RequestMethod.POST)
    	String createUser(@RequestBody User user);
    
    	@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
    	String updateUser(@PathVariable("id") Long id, @RequestBody User user);
    
    	@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    	String deleteUser(@PathVariable("id") Long id);
    
    	@RequestMapping(value = "/{id}", method = RequestMethod.GET)
    	User getById(@PathVariable("id") Long id);
    	
    }
    

    而在ServiceAClient中,只要对这个接口进行继承,加上注解就可以了。

    @FeignClient("ServiceA")
    public interface ServiceAClient extends ServiceAInterface {
     
    }
    

    还要在启动上加上开启Feign客户端的注解@EnableFeignClients,就能起作用了。那我们就点进去这个注解看看。

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(FeignClientsRegistrar.class)
    public @interface EnableFeignClients {
    
    
    	String[] value() default {};
    
    	String[] basePackages() default {};
    
    	Class<?>[] basePackageClasses() default {};
    
    	Class<?>[] defaultConfiguration() default {};
    
    	Class<?>[] clients() default {};
    
    }
    

    注解上面会注入Feign客户端的注册器,那我们就点进去看看。

    image-20211019104836503

    ImportBeanDefinitionRegistrar来实现Bean的动态注入。它是Spring中一个强大的扩展接口,可以动态的往容器中,注入Bean,基本可以确定,就是实现它,来往容器中注入Feign的客户端的。

    	@Override
    	public void registerBeanDefinitions(AnnotationMetadata metadata,
    			BeanDefinitionRegistry registry) {
    		registerDefaultConfiguration(metadata, registry);
    		registerFeignClients(metadata, registry);
    	}
    

    首先先看一下第一个方法,名字注册一些默认的配置信息,点进去看看。大概的意思就是获取到EnableFeignClients这个注解上的一些信息,如果信息不为空,且包含模型配置的leu,就进入下面的判断。此方法不是核心的方法,主要留意一下registerFeignClients方法。

    	private void registerDefaultConfiguration(AnnotationMetadata metadata,
    			BeanDefinitionRegistry registry) {
    		Map<String, Object> defaultAttrs = metadata
    				.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
    
    		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"));
    		}
    	}
    

    那就点到registerFeignClients方法看看。

    	public void registerFeignClients(AnnotationMetadata metadata,
    			BeanDefinitionRegistry registry) {
    		ClassPathScanningCandidateComponentProvider scanner = getScanner();
    		scanner.setResourceLoader(this.resourceLoader);
    
    		Set<String> basePackages;
                    //拿到注解的一些信息
    		Map<String, Object> attrs = metadata
    				.getAnnotationAttributes(EnableFeignClients.class.getName());
                    //设置注解类型的过滤器,看上去是为了获取标记FeignClient这个注解类
    		AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
    				FeignClient.class);
    		final Class<?>[] clients = attrs == null ? null
    				: (Class<?>[]) attrs.get("clients");
            
                    //由于@EnableFeignClients注解中来配置clients属性,一般不会填,所以可以clients默认一般都是空的
    		if (clients == null || clients.length == 0) {
    			scanner.addIncludeFilter(annotationTypeFilter);
                           //getBasePackages这里肯定获取@EnableFeignClients这个注解的包的路径
    			basePackages = getBasePackages(metadata);
    		}
    		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)));
    		}
    
    	for (String basePackage : basePackages) {
                //1.这步的话就是根据包路径,去找到这个包下面有哪些FeignClient注解的类
    			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");
                                           //根据注解的元数据信息去获取注解参数里面的值
    					Map<String, Object> attributes = annotationMetadata
    							.getAnnotationAttributes(
    									FeignClient.class.getCanonicalName());
                                           //获取对应的服务名陈
    					String name = getClientName(attributes);
    					registerClientConfiguration(registry, name,
    							attributes.get("configuration"));
    
    					registerFeignClient(registry, annotationMetadata, attributes);
    				}
    			}
    		}
    	}
    

    下面这个方法至关重要,那我们就点进去看看

    			Set<BeanDefinition> candidateComponents = scanner
    					.findCandidateComponents(basePackage);
    

    image-20211019134121544

    下面就来到scanCandidateComponents方法中

    private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    		Set<BeanDefinition> candidates = new LinkedHashSet<>();
    		try {
                           //此处就是包的路径加上/**/*.class
    			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
    					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
                           //根据上面得到处理的路径,去拿到包路径下面所有的class文件,转成一个个Resource
    			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
    			boolean traceEnabled = logger.isTraceEnabled();
    			boolean debugEnabled = logger.isDebugEnabled();
                
                            //遍历所有的资源
    			for (Resource resource : resources) {
    				if (traceEnabled) {
    					logger.trace("Scanning " + resource);
    				}
    				if (resource.isReadable()) {
    					try {
    						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                                                     //此处方法最为重要,由于前面创建的scanner。重写了isCandidateComponent方法,这里就是从所有的class中
                                                  //   获取所有有的Feign的客户端
    						if (isCandidateComponent(metadataReader)) {
    							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
    							sbd.setSource(resource);
    							if (isCandidateComponent(sbd)) {
    								if (debugEnabled) {
    									logger.debug("Identified candidate component class: " + resource);
    								}
    								candidates.add(sbd);
    							}
    							else {
    								if (debugEnabled) {
    									logger.debug("Ignored because not a concrete top-level class: " + resource);
    								}
    							}
    						}
    						else {
    							if (traceEnabled) {
    								logger.trace("Ignored because not matching any filter: " + resource);
    							}
    						}
    					}
    					catch (Throwable ex) {
    						throw new BeanDefinitionStoreException(
    								"Failed to read candidate component class: " + resource, ex);
    					}
    				}
    				else {
    					if (traceEnabled) {
    						logger.trace("Ignored because not readable: " + resource);
    					}
    				}
    			}
    		}
    		catch (IOException ex) {
    			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    		}
    		return candidates;
    	}
    

    下面就断点进去看一下那个方法,就是之前把过滤条件传进来了,此处就可以获得了。

    image-20211019134248630

    image-20211019134453000

    拿到所有的Feign客户端就准备开始注册了

    image-20211019134534603

  • 相关阅读:
    【神经网络与深度学习】Caffe Model Zoo许多训练好的caffemodel
    【计算机视觉】论文笔记:Ten years of pedestrian detection, what have we learned?
    【计算机视觉】论文笔记:Ten years of pedestrian detection, what have we learned?
    【计算机视觉】行人检测资源汇总
    【计算机视觉】行人检测资源汇总
    【神经网络与深度学习】卷积神经网络(CNN)
    【神经网络与深度学习】卷积神经网络(CNN)
    【CUDA开发】__syncthreads的理解
    【CUDA开发】__syncthreads的理解
    微信公众号-增加智能自动回复的功能--使用图灵机器人
  • 原文地址:https://www.cnblogs.com/dalianpai/p/15424414.html
Copyright © 2011-2022 走看看