在Spring boot中类似于spring cloud 一样的使用OpenFeign(1)

一、前言

在文章开始之前,首先得了解下为什么?不然我自己搞不明白,看这篇文章的人也搞不明白

为什么在spring boot中(非spring cloud环境下)以spring cloud的方式使用openFeign?

首先项目不是很大,使用spring cloud有种杀鸡用牛刀的感觉,所以该项目并没有使用spring cloud,但是项目中做了一定的拆分,主要是微信小程序需要去调用一些业务性的服务,接口不算太多,但也不是很少。spring cloud提供了很多的功能,比如总线、网关之类的,暂时还用不上,所以没有使用spring cloud。也许有更好得解决方式,但是我受限于自身得知识结构,想不到啊,所以,谁有更好的方式告诉我啊

为什么非得使用OpenFeign?

在web环境中去调用其它的http服务有很多的方式,比如apacheHttpClient,或者直接使用spring提供的RestTemplate,都非常的简单易用,原本也是这样做的,但是当你调用的http接口很多的时候,你就发现很多的代码都是重复性的代码,都是格式化的代码,而真正需要改变的永远只是接口地址、请求方法、请求参数、请求头等那么几个。

OpenFeign把一个HTTP请求中经常会变的几个参数交给用户以接口+注解的形式去声明,而那些重复性的工作(发起请求,解析数据等)则自己去做了,使用起来,至少在结构上非常的简单清晰明了。

二、OpenFeign的使用

openFeign的使用非常简单,简单来说就两个步骤:

  1. 创建一个接口,声明请求
  2. 构建接口的实例,调用接口

1)定义一个接口

openFeign要求用户在接口类中声明请求,使用@RequestLine注解,声明请求方法及地址,@Headers声明请求的请求头,@Param参数设置请求参数的值,@QueryMap处理动态参数。
具体的使用,可以参照openFeign的github
openFeign使用说明

public interface HxGpsService {
	
	@RequestLine("GET /location/traceReplay/{vehicleId}?startTime={startTime}&endTime={endTime}")
	@Headers("Token: {token}")
	public List<Gps> traceReplay(@Param("token") String token,
								 @Param("vehicleId") String vehicleId,
								 @Param("startTime") String startTime,
								 @Param("endTime") String endTime);
}

2)使用

构建者模式创建接口代理对象

HxGpsService hxGpsService =  
		Feign.builder()
			 .client(new OkHttpClient())
			 .encoder(new GsonEncoder())
			 .decoder(new GsonDecoder())
			 .target(HxGpsService.class, "http://localhost:8383/dispatch");

List<Gps> gpsLists = hxGpsService.traceReplay("XXXX","XX","XXXX-XX-XX ZZ:ZZ:ZZ","XXXX-XX-XX ZZ:ZZ:ZZ")

三、在spring boot中使用OpenFeign

1)创建@Configuration配置类

声明请求的接口类定义好之后,使用Feign代理创建接口的实例,如果该实例其它的地方需要引用,我们可以把Feign代理创建的对象交给spring 容器来管理,那使用的地方就只需要注入即可

@Configuration
public class OpenFeignInterfaceConfiguration {
	@Bean
	public HxGpsService hxGpsService(){
		Feign.builder()
			 .client(new OkHttpClient())
			 .encoder(new GsonEncoder())
			 .decoder(new GsonDecoder())
			 .target(HxGpsService.class, "http://localhost:8383/dispatch");
	}
}

http://localhost:8383/dispatch在上面示例中硬编码了,这个地址我们可以在yml文件中定义,在这里注入即可,但我这个只是示例,各位将就着看下。

2)使用

@Service("gpsService")
public GpsService {

	@Autowired
	private HxGpsService hxGpsService;
	
	public List<Gps> getGps(String token,String vehicleId,String startTime,String endTime){
		return hxGpsService.traceReplay(token,vehicleId,startTime,endTime);
	}
}

这样我们就可以非常方便的在任意地方调用远程接口,就像调用本地方法一样。如果接口不多的情况下,我们自己去配置一个Bean没有什么很大的难度,也不会觉得繁琐,但是如果接口比较多的情况下,我们就发现这些都是重复性的工作,mybatis的mapper接口我们只需要定义然后配置扫描就可以交给spring容器来管理,那openFeign接口有没有可能像mybatis一样的使用呢?答案是肯定的,只要肯钻研,办法还是有的。

四、向IOC容器中自动注入接口类

在介绍之前,我们先得了解一下spring boot是怎么把对象搞到IOC容器中的?,因为要把OpenFeign声明的请求接口自动注入到IOC容器中,需要知道下面这些原理。
在springboot中有些类我们不需要配置,只需要引入jar包,spring boot就能帮我们把对象弄到IOC容器中。还有些组件,我们只要加一个@EnableXXXX的注解,spring 就可以帮我们把该组件需要注入到IOC容器中的类自动注入,使用起来非常方便,怎么做到的?

1)第一种引入jar包自动注入

这种jar一般都是starter,有些starter是spring官方实现的,有些则是各个组件厂商自己实现的。starter的原理,一般是在jar包的META-INFO目录下定义一个spring.factories文件,在这个spring.fatories文件中配置一个XXXAutoConfiguration的类,然后再在这个类中定义哪些对象交给spring自动注入。
spring boot在启动时会扫描所有引入的jar包是否含有spring.factories文件,然后解析文件,判断是否存在这些jar,如果存在则自动加载,不存在就不加载

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration{
	
	 .....
	
	  @Bean
	  @ConditionalOnMissingBean
	  public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
	    ExecutorType executorType = this.properties.getExecutorType();
	    if (executorType != null) {
	      return new SqlSessionTemplate(sqlSessionFactory, executorType);
	    } else {
	      return new SqlSessionTemplate(sqlSessionFactory);
	    }
	  }
	}

		......

@ConditionalXXXX注解是spring的条件注解,意思只有满足某些条件我才加载或不加载,比如上面的例子中只有项目中存在SqlSessionFactory.class, SqlSessionFactoryBean.class两个类时才加载对象到spring容器中,感兴趣的可以单独去搜搜,这里不展开讨论。

2)EnableXXXX

还有些类也会被spring自动加载,但前提条件是需要我们自己去把开关打开,在spring boot的启动类中加@EnableXXXX注解,spring就会自动帮我把要注入的对象注入到容器中。那spring又是怎么做到的?
这里就需要了解spring @Import注解注入对象的三种模式,spring在扫描到@Import注解时,会去执行@Import注解中引入的类,本文的要实现的目标,其原理就是这个。

1、以XXXXConfiguration结尾

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {

}

在XXXXConfiguration类上添加@Configuration注解,然后在类中创建bean

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {

	@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
		return new ScheduledAnnotationBeanPostProcessor();
	}

}

这种模式可直接创建要注入到容器中的对象

2、以XXXXSelector结尾

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching

Selector模式一般根据用户的配置来选择要以什么方式,把哪些类加载到容器中,需要实现ImportSelector接口,在selectImports方法中返回要注入的类的完全类名,spring根据类名实现注入

public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector{
	
	....
	
	@Override
	public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
		Class<?> annoType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
		AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
		if (attributes == null) {
			throw new IllegalArgumentException(String.format(
				"@%s is not present on importing class '%s' as expected",
				annoType.getSimpleName(), importingClassMetadata.getClassName()));
		}

		AdviceMode adviceMode = attributes.getEnum(this.getAdviceModeAttributeName());
		String[] imports = selectImports(adviceMode);
		if (imports == null) {
			throw new IllegalArgumentException(String.format("Unknown AdviceMode: '%s'", adviceMode));
		}
		return imports;
	}
	
	......
	
}

public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching>{
	
	....
	
	@Override
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return getProxyImports();
			case ASPECTJ:
				return getAspectJImports();
			default:
				return null;
		}
	}
	
	.....
	
}

3、以XXXXRegistrar结尾

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan

这种模式最复杂,可以动态的注入bean。
我去、前面废话了太多,还没有开始正题,文章就已经很长了,好吧,正题下一篇
在Spring boot中类似于spring cloud 一样的使用OpenFeign(2)

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值