首页 > 其他分享 >@FeignClient注入找不到异常如何解决

@FeignClient注入找不到异常如何解决

时间:2023-01-22 15:11:14浏览次数:40  
标签:FeignClient String EnableFeignClients 注解 attributes metadata 异常 注入

@FeignClient注入找不到异常如何解决

前言

Springcloud中的服务间调用是通过Feign进行调用的,在调用方服务中,我们需要定义一些带有@FeignClient注解的接口类。并且在启动类上加上@EnableFeignClients注解。

程序启动的时候,会检查是否有@EnableFeignClients注解,如果有该注解,则开启包扫描,扫描带有@FeignClient注解的接口。

这里结合之前遇到的一个问题来和大家一起学习下@EnableFeignClients启动过程。

问题描述

之前搭建一个简单demo的时候,启动之后总是报错

Field client1Feign in com.aiqinhai.client2.controller.Testrequired a bean of type

'com.aiqinhai.client2.feignclient.Client1Feign' that could not be found.

Action:

Consider defining a bean of type 'com.aiqinhai.client2.feignclient.Client1Feign' in your

Process finished with exit code 1

排查了一顿之后,发现启动上的@EnableFeignClients注解,没有指定basePackages包扫描路径。

而且Client1Feign接口不是和启动类在同一目录下,所以启动的时候就会报上述错误。

后来在@EnableFeignClients指定了扫描包路径就解决了。

@SpringBootApplication

@EnableDiscoveryClient

@EnableFeignClients(basePackages=)

public class Client2Application {

 static void main(String[] args) {

    SpringApplication.run(Client2Application.class, args);

}

}

问题确实解决了,但是我们还是需要了解服务启动的时候,@EnableFeignClients注解干了哪些事。

刨根@EnableFeignClients

想了解清楚这个注解的作用,最好的方式就是看看注解的源码。点进去之后,可以看到

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.TYPE})

@Documented

@Import(FeignClientsRegistrar.class)

public @interface EnableFeignClients {

//同basePackages

String[] value() default {};

    //扫描的package.

String[] basePackages() default {};

//feigin client全局配置,默认配置在FeignClientsConfiguration类中

Class>[] defaultConfiguration() default {};

//@FeignClient注解的接口,如果指定了该属性,则关闭扫描。

Class>[] clients() default {};

}

可以看到,注解中import了FeignClientsRegistrar类,我们进去看看,可以看到包扫描的逻辑就是在FeignClientsRegistrar中实现的,会扫描所有的.class文件,过滤出@FeignClient标注的接口,然后通过BeanDefinitionBuilder生成FeignClientFactoryBean对象,注入到IOC容器中。

具体代码如下

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,

	ResourceLoaderAware, EnvironmentAware {

//包扫描方法入口

@Override

public void registerBeanDefinitions(AnnotationMetadata metadata,

		BeanDefinitionRegistry registry) {

	//注册默认配置bean到ioc

	registerDefaultConfiguration(metadata, registry);

	//注册@FeignClients标注的接口bean,为其生成动态代理

	registerFeignClients(metadata, registry);

}

private void registerDefaultConfiguration(AnnotationMetadata metadata,

		BeanDefinitionRegistry registry) {

	//获取@EnableFeignClients注解属性

	Map 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();

		}

		//注册@EnableFeignClients defaultConfiguration 类中定义的bean到ioc

		registerClientConfiguration(registry, name,

				defaultAttrs.get("defaultConfiguration"));		}

}

public void registerFeignClients(AnnotationMetadata metadata,

		BeanDefinitionRegistry registry) {

	//spring 扫描工具类

	ClassPathScanningCandidateComponentProvider scanner=getScanner();

	scanner.setResourceLoader(this.resourceLoader);

    //待扫描的包

	Set basePackages;

    //@EnableFeignClients注解属性

	Map attrs=metadata

			.getAnnotationAttributes(EnableFeignClients.class.getName());

	//@FeignClient注解过滤器,只扫描@FeignClient注解标注的接口

	AnnotationTypeFilter annotationTypeFilter=new AnnotationTypeFilter(

			FeignClient.class);

	//解析@EnableFeignClient 属性clients

	final Class>[] clients=attrs==null ? null

			: (Class>[]) attrs.get("clients");

	//如果@EnableFeignClient没有指定clients,则开启包扫描,否则关闭扫描,使用clients

	if (clients==null || clients.length==0) {

		scanner.addIncludeFilter(annotationTypeFilter);

		basePackages=getBasePackages(metadata);

	}

	else {

		final Set 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) {

	    //从类路径下的所有.class文件中扫描@FeignClient注解的接口

		Set 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();

				//@FeignClient注解必须应用在接口上,否则抛出异常。

				Assert.isTrue(annotationMetadata.isInterface(),

						"@FeignClient can only be specified on an interface");

                    //获取FeignClient注解属性

				Map attributes=annotationMetadata

						.getAnnotationAttributes(

								FeignClient.class.getCanonicalName());

                    //服务名称

				String name=getClientName(attributes);

				//注册configuration中指定的bean

				registerClientConfiguration(registry, name,

						attributes.get("configuration"));

				//生成FeignClient bean,并注册到ioc

				registerFeignClient(registry, annotationMetadata, attributes);

			}

		}

	}

}



//生成bean,注入到IOC容器

private void registerFeignClient(BeanDefinitionRegistry registry,

		AnnotationMetadata annotationMetadata, Map attributes) {

	// feignclient类名称

	String className=annotationMetadata.getClassName();

	BeanDefinitionBuilder definition=BeanDefinitionBuilder

			.genericBeanDefinition(FeignClientFactoryBean.class);

	validate(attributes);

	definition.addPropertyValue("url", getUrl(attributes));

	definition.addPropertyValue("path", getPath(attributes));

	String name=getName(attributes);

	definition.addPropertyValue("name", name);

	definition.addPropertyValue("type", className);

	definition.addPropertyValue("decode404", attributes.get("decode404"));

	definition.addPropertyValue("fallback", attributes.get("fallback"));

	definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));

	definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); 

	String alias=name + "FeignClient";

	AbstractBeanDefinition beanDefinition=definition.getBeanDefinition(); 

	boolean primary=(Boolean)attributes.get("primary"); // has a default, won't be null

	beanDefinition.setPrimary(primary);

	String qualifier=getQualifier(attributes);

	if (StringUtils.hasText(qualifier)) {

		alias=qualifier;

	}

	BeanDefinitionHolder holder=new BeanDefinitionHolder(beanDefinition, className,

			new String[] { alias });

	//注册到ioc

	BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

}

}

@FeignClient 类,注入找不到类

原因:

@FeignClinet 和 @EnableFeignClients 不是同一个包。

可能因为springboot和springcloud的版本不一致造成的。

标签:FeignClient,String,EnableFeignClients,注解,attributes,metadata,异常,注入
From: https://www.cnblogs.com/dituirenwu/p/17064447.html

相关文章

  • spring boot——请求与参数校验——重要概念——异常处理——@ControllerAdvice注解
        PS,访问资源,引入如下:<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thyme......
  • spring boot——请求与参数校验——重要概念——异常处理——@ExceptionHandler注解
          自定义一个类:MyExceptionpackageorg.example.Exception;publicclassMyExceptionextendsException{privatestaticfinallongseria......
  • python异常处理
    Python的异常机制主要依赖try、except、else、finally和raise五个关键字,其中在try关键字后缩进的代码块简称try块,它里面放置的是可能引发异常的代码;在except后对应的是......
  • C++实战笔记(三)异常处理
    tags:C++Interview写在前面简单总结一下C++异常处理部分(Exception).异常只是C++为了处理错误提出的一种解决方案,并不是唯一的一种.异常处理特点异常处理的流程完全独立......
  • springboot统一处理异常
    增加业务异常处理类:packagecom.example.demo.config;importlombok.Data;@DatapublicclassBizExceptionextendsRuntimeException{protectedIntegererr......
  • DVWA靶机-全级别测试-SQL注入(盲注)
    sql盲注盲注只回复是或否,Sql注入回复详细信息;过程一般如下:1、判断是否存在注入,注入时字符型还是数字型2、猜解数据库的长度,猜解数据库的名称3、猜解数据库中有几个表,猜解......
  • SpringMVC异常处理
    简介系统中异常包括:编译时异常和运行时异常RuntimeException;编译异常就是检查异常,需要捕获;运行时异常可以捕获也可以不捕获。 异常处理思路在springmvc中,异常处理的......
  • 手动抛异常
    ---------------------------------------阶段3----------------------------------------------------------------------------------------------declarev_deptdept%ro......
  • C#异常
    usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;namespacelearn_try{internalclassP......
  • DVWA靶机-全级别测试-SQL注入(显示)
    SQL注入SQL注入就是用户在能够控制SQL查询、更新、插入、删除等语句的参数的情况下,攻击者通过构造特殊的输入字符串使后端程序错误地识别SQL查询语句中的代码与数据部分从而......