SrpingBoot集成原生Feign

Feign的目的是为了减少复杂度尽快地实现Http API的连接。在Spring项目中使用原生的Feign代替HttpClient是一个不错的选择。

Feign GitHub地址

一、Feign 客户端的使用
  1. 添加依赖

    <!-- https://mvnrepository.com/artifact/com.netflix.feign/feign-core -->
    <dependency>
        <groupId>com.netflix.feign</groupId>
    	<artifactId>feign-core</artifactId>
    	<version>8.18.0</version>
    </dependency>
    
    <!-- 不用Gson进行encoder可以换成其他的依赖,例如,使用Jackson -->
    <!-- https://mvnrepository.com/artifact/com.netflix.feign/feign-gson -->
    <dependency>
    	<groupId>com.netflix.feign</groupId>
        <artifactId>feign-gson</artifactId>
        <version>8.18.0</version>
    </dependency>
    
  2. 编写请求接口

    
    public interface BaiQiShiRepository {
    	/**
     	 * GET 请求
     	 */
    	@RequestLine("GET /api/v1/{secretKey}/tick/{tick}")
    	@Headers("Content-Type:charset=UTF-8")
    	JSONObject query(@Param("secretKey") String secretKey, @Param("tick") String tick);
    	
    	/**
     	 * POST 请求,JSON格式参数
     	 */
    	@RequestLine("POST /query")
    	@Headers("Content-Type:application/json,charset=UTF-8")
    	@Body("%7b\"partnerId\":\"{baiQiShi.partnerId}\",\"verifyKey\":\"{baiQiShi.verifyKey}\",\"platform\":\"{baiQiShi.platform}\",\"tokenKey\":\"{baiQiShi.tokenKey}\"%7d")
    	JSONObject query(BaiQiShi baiQiShi);
    
    	/**
     	 * POST 请求,表单形式提交
     	 */
    	@RequestLine("POST /riskService")
    	@Headers("Content-Type:application/x-www-form-urlencoded,charset=UTF-8")
    	JSONObject riskService(@QueryMap Map<String, String> queryMap);
    	
    }
    
  3. Feign使用

    String url = "http:.....";
    BaiQiShiRepository repository = Feign.builder().encoder(new GsonEncoder()).decoder(new GsonDecoder()).target(BaiQiShiRepository.class, url);
    JSONObject result = repository.query("secretKey", "tick");
    
二、集成到Spring的项目中

想使用Feign跟SpringCloud一样,使用的时候自动注入。

  1. 项目中添加依赖

  2. 编写调用接口,和上面第二步一样

  3. 定义一个注解,用于将对象注册到Spring

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface FeignRepository {
    
    	String value() default "";
    
    	/**
     	* 配置文件定义的url的key,配置文件中定义请求的url
     	*/
    	String uriPropertyName() default "";
    }
    

    使用示例:

    /**
     * tong-dun-uri是配置文件中的key,定义了请求的接口
     */
    @FeignRepository(uriPropertyName = "tong-dun-uri")
    public interface TongDunRepository {
    
    	@RequestLine("POST /riskService")
    	@Headers("Content-Type:application/x-www-form-urlencoded,charset=UTF-8")
    	JSONObject riskService(@QueryMap Map<String, String> queryMap);
    	
    }
    
  4. 之后要处理这个注解,并注册到Spring容器中,首先要扫描到这个注解并处理
    定义注解FeignRepositoryScan使用@Import实现Bean的自动注入

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(FeignRepositoryScanner.class)
    public @interface FeignRepositoryScan {
    
    	/**
     	* 扫描路径,为空就扫描当前对应的
     	*/
    	String[] basePackage() default {};
    }
    

    实现解析FeignRepositoryScan注解的实现类:FeignRepositoryScanner

    public class FeignRepositoryScanner implements ImportBeanDefinitionRegistrar {
    
    	private static final String BASE_PACKAGE_KEY = "basePackage";
    
    	@Override
    	public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry  beanDefinitionRegistry) {
    
        	AnnotationAttributes attributes = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(FeignRepositoryScan.class.getName()));
    
        	if (attributes == null) {
            	return;
        	}
        
        	// 获取 FeignRepositoryScan 注解basePackage参数中的值
        	String[] basePackages = attributes.getStringArray(BASE_PACKAGE_KEY);
        	if (basePackages == null || basePackages.length == 0) {
            	basePackages = new String[]{((StandardAnnotationMetadata) annotationMetadata).getIntrospectedClass()
                    .getPackage().getName()};
        	}
    
    		// 自定义包扫描类,用于扫描带有 FeignRepository 注解的类,并注册到容器中
        	FeignRepositoryClassPathScanHandle scanHandle = new FeignRepositoryClassPathScanHandle(beanDefinitionRegistry, false);
    
        	//扫描指定路径下的接口
        	scanHandle.doScan(basePackages);
    
    	}
    }
    

    自定义扫描类:FeignRepositoryClassPathScanHandle (继承ClassPathBeanDefinitionScanner

    public class FeignRepositoryClassPathScanHandle extends ClassPathBeanDefinitionScanner {
    
    	FeignRepositoryClassPathScanHandle(BeanDefinitionRegistry registry, boolean useDefaultFilters {
        	super(registry, useDefaultFilters);
    	}
    
    	@Override
    	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        	addIncludeFilter(new AnnotationTypeFilter(FeignRepository.class));
        	Set<BeanDefinitionHolder> definitionHolders = super.doScan(basePackages);
        	if (!CollectionUtils.isEmpty(definitionHolders)) {
            	for (BeanDefinitionHolder holder : definitionHolders) {
                	GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
                	String beanClassName = definition.getBeanClassName();
                	definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
                	// 自定义一个工程方法,用于创建自定义的Feign客户端对象,需要实现FactoryBean接口(主要的是getObject()方法)
                	definition.setBeanClass(FeignRepositoryFactoryBean.class);
                	definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
            	}
        	}
        	return definitionHolders;
    	}
    
    	/**
    	 * 重写该方法是为了让接口可以被认为是获选组件,否则无法初始化扫描的对象
    	 */
    	@Override
    	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        	return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    	}
    
    }
    

    工厂类:FeignRepositoryFactoryBean(实现FactoryBean

    public class FeignRepositoryFactoryBean<T> implements FactoryBean<T> {
    
    	@Resource
    	private Environment env;
    
    	private Class<T> clazz;
    
    	public FeignRepositoryFactoryBean(Class<T> clazz) {
        	this.clazz = clazz;
    	}
    
    	@Override
    	public T getObject() throws Exception {
        	FeignRepository feignRepository = clazz.getAnnotation(FeignRepository.class);
        	String propertyName = feignRepository.uriPropertyName();
        	if (StringUtils.isEmpty(propertyName)) {
            	throw new SystemException("Feign uriPropertyName is null!");
        	}
        	String uri = env.getProperty(propertyName, "");
        	if (StringUtils.isEmpty(uri)) {
            	throw new SystemException("Feign " + propertyName + " not set");
        	}
        	return Feign.builder().encoder(new GsonEncoder()).decoder(new GsonDecoder()).target(clazz, uri);
    	}
    
    	@Override
    	public Class<?> getObjectType() {
        	return clazz;
    	}
    
    
    }
    

    使用:启动类加入FeignRepositoryScan注解

    @SpringBootApplication
    @FeignRepositoryScan(basePackage = "com.wangxiaonan.berry.repository")
    public class BerryApplication {
    
    	public static void main(String[] args) {
        	SpringApplication.run(BerryApplication.class, args);
    	}
    
    }
    

    到此自动注入结束,启动就可以像其他服务一样使用@Resources@Autowired自动注入了

三、常用

Interface Annotations

Feign annotations define the Contract between the interface and how the underlying client
should work. Feign’s default contract defines the following annotations:

AnnotationInterface TargetUsage
@RequestLineMethodDefines the HttpMethod and UriTemplate for request. Expressions, values wrapped in curly-braces {expression} are resolved using their corresponding @Param annotated parameters.
@ParamParameterDefines a template variable, whose value will be used to resolve the corresponding template Expression, by name.
@HeadersMethod, TypeDefines a HeaderTemplate; a variation on a UriTemplate. that uses @Param annotated values to resolve the corresponding Expressions. When used on a Type, the template will be applied to every request. When used on a Method, the template will apply only to the annotated method.
@QueryMapParameterDefines a Map of name-value pairs, or POJO, to expand into a query string.
@HeaderMapParameterDefines a Map of name-value pairs, to expand into Http Headers
@BodyMethodDefines a Template, similar to a UriTemplate and HeaderTemplate, that uses @Param annotated values to resolve the corresponding Expressions.

其他插件的集成,例如,SLF4J日志,JSON格式,SOAP,编码解码等

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0x77786e

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值