概述
写在最前
由于一个单体项目SpringBoot需要对另一个单体SpingBoot进行远程调用,调用的接口比较多,且使用SpringCloud又没有必要,所以我们对OpenFeign进行了封装。(springcloud OpenFeign不好引入项目中,会有一堆依赖问题);写着玩的,简单记录一下(其实就是模仿SpringCloudOpenFeign),如有不对请指正。(有个开源的框架也是远程调用也符合这种业务场景,但是名字给忘了)
封装方式
我们将封装成一个依赖形式,方便项目使用;
依赖结构
创建module
首先新建一个maven module ,名字如:gulimall-spring-boot-starter-feign;在pom.xml引入以下依赖:
<properties>
<openfegin.version>12.0</openfegin.version>
</properties>
<dependencies>
<dependency>
<groupId>com.gx</groupId>
<artifactId>gulimall-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>${openfegin.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
<version>${openfegin.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
创建包
- feign.config
- FeignRequestInterceptor:配置远程调用http的header内容,比如传固定的token
- IotRemoteProperties: 在yaml文件配置的远程调用配置信息
- OpenFeignConfig: 使用open-feign固定配置
- RemoteConfig:配置实体
- feign.core.annotation
- EnableFeignClients:在启动类上,包扫描注解
- GulimallFeign:在每个接口类加的注解,表示为调用的接口,
- feign.core.factory
- FeignClientFactoryBean:将加了GulimallFeign注入到spring容器中
- FeignClientRegistrar:扫描远程调用的包下面的类
具体代码
FeignRequestInterceptor
import cn.hutool.core.util.StrUtil;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.http.HttpHeaders;
/**
*
* 对所有远程调用的请求设置header 业务代码
*/
public class FeignRequestInterceptor implements RequestInterceptor {
private String authorization;
private RemoteConfig remoteConfig;
public FeignRequestInterceptor() {
}
public FeignRequestInterceptor(RemoteConfig remoteConfig) {
this.remoteConfig = remoteConfig;
}
@Override
public void apply(RequestTemplate template) {
template.header(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8;");
if (StrUtil.isNotBlank(remoteConfig.getAuthorization())) {
template.header(HttpHeaders.AUTHORIZATION, remoteConfig.getAuthorization());
}
}
}
IotRemoteProperties
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import java.util.Map;
/**
* @date 2022/11/18
* description: 业务配置类 配置远程调用地址 以及token等信息
*/
@Data
@Validated
@ConfigurationProperties("gulimall.remote")
@Component
public class IotRemoteProperties {
private Map<String, RemoteConfig> remoteConfigs;
}
OpenFeignConfig
import feign.Contract;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenFeignConfig {
@Bean
public Contract useFeignAnnotations() {
return new Contract.Default();
}
}
EnableFeignClients
import com.gx.framework.feign.core.factory.FeignClientRegistrar;
import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @version 1.0
* @description 启动类注解 说明需要对哪些包进行扫描 如:@EnableInduschainFeignClients("com.gx.module.remote.api.*")
* @date 2023/1/29 22:14
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(FeignClientRegistrar.class)
public @interface EnableFeignClients {
}
GulimallFeign
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @version 1.0
* @description 每个远程调用接口类注解
* @date 2023/1/29 22:15
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface GulimallFeign {
/**
* 远程调用服务后端服务名 如iot,aiot...
* @return
*/
String remoteName();
String desc() default "";
}
//@InduschainFeign(remoteName = "iot", desc = "调用iot的设备接口")
//public interface IotDeviceFeignApi {
// /**
// * 获取设备现在信息
// *
// * @param serialNumbers
// * @return
// */
// @RequestLine("POST /iot/device/getDeviceInfoBySerialNumbers")
// IotAjaxResultDTO<List<IotDeviceDTO>> getIotDeviceCurrentInfo(List<String> serialNumbers);
//
// /**
// * 获取设备序列号
// * @return
// */
// @RequestLine("GET /iot/device/generator")
// IotAjaxResultDTO<String> getGenerator();
//
// @RequestLine("PUT /iot/device/rule/updateAlarmRuleState/{id}/{state}")
// IotAjaxResultDTO updateState(@Param("id") Long id, @Param("state") String state);
//
//}
FeignClientFactoryBean
import com.gx.framework.feign.config.FeignRequestInterceptor;
import com.gx.framework.feign.config.IotRemoteProperties;
import com.gx.framework.feign.config.RemoteConfig;
import com.gx.framework.feign.core.annotation.GulimallFeign;
import feign.Feign;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.Map;
/**
* @version 1.0
* @description 注册每一个加了GulimallFeign 塞入bean容器中
* @date 2023/1/29 22:16
*/
public class FeignClientFactoryBean implements FactoryBean<Object>, ApplicationContextAware {
// 目标对象(FeignClient接口)
private Class<?> type;
// 服务名称
private String name;
private ApplicationContext applicationContext;
@Override
public Object getObject() throws Exception {
IotRemoteProperties iotRemoteProperties = this.applicationContext.getBean(IotRemoteProperties.class);
Map<String, RemoteConfig> remoteConfigs = iotRemoteProperties.getRemoteConfigs();
GulimallFeign annotation = type.getAnnotation(GulimallFeign.class);
RemoteConfig remoteConfig = remoteConfigs.get(annotation.remoteName());
Object feignClient = Feign.builder().decoder(new JacksonDecoder()).encoder(new JacksonEncoder())
.requestInterceptor(new FeignRequestInterceptor(remoteConfig))
.target(type, remoteConfig.getUrl());
return feignClient;
}
@Override
public Class<?> getObjectType() {
return this.type;
}
public void setType(Class<?> type) {
this.type = type;
}
public void setName(String name) {
this.name = name;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
FeignClientRegistrar
import com.gx.framework.feign.core.annotation.EnableFeignClients;
import com.gx.framework.feign.core.annotation.GulimallFeign;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import java.util.Map;
import java.util.Set;
/**
* @version 1.0
* @description
* @date 2023/1/29 22:17
*/
public class FeignClientRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
private ResourceLoader resourceLoader;
private Environment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 创建扫描器
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(GulimallFeign.class);
scanner.addIncludeFilter(annotationTypeFilter);
// 查找扫描包集合
Map<String, Object> attrs = importingClassMetadata.getAnnotationAttributes(EnableFeignClients.class.getName());
if (attrs == null) {
return;
}
String[] packages = (String[]) attrs.get("value");
if (packages.length == 0) {
packages = getDefaultPackages(importingClassMetadata);
}
for (String p : packages) {
// 扫描
Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(p);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition bd = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = bd.getMetadata();
// 注册FactoryBean
registerFeignClient(registry, annotationMetadata);
}
}
}
}
// 注册InduschainFeignClientFactoryBean
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata) {
Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(GulimallFeign.class.getName());
if (annotationAttributes == null) {
return;
}
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
definitionBuilder.addPropertyValue("type", annotationMetadata.getClassName());
definitionBuilder.addPropertyValue("name", (String) annotationAttributes.get("value"));
AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
registry.registerBeanDefinition(annotationMetadata.getClassName(), beanDefinition);
}
// 取注解所在类所在包作为扫描路径
private String[] getDefaultPackages(AnnotationMetadata importingClassMetadata) {
String className = importingClassMetadata.getClassName();
int lastDotIndex = className.lastIndexOf(".");
return lastDotIndex != -1 ? new String[]{className.substring(0, lastDotIndex)} : new String[]{};
}
private ClassPathScanningCandidateComponentProvider getScanner() {
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
// 目标beanDefinition必须是顶层类(不依赖与其他类isIndependent)且不能是注解
return beanDefinition.getMetadata().isIndependent() && !beanDefinition.getMetadata().isAnnotation();
}
};
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
application.yaml
gulimall:
remote:
remote-configs:
iot:
url: http://127.0.0.1:8886
authorization: 4c1f2ef1-ec18-4dfa-bfec-3a20c2f71608
address: 127.0.0.1:8886
token: 4c1f2ef1-ec18-4dfa-bfec-3a20c2f71608
最后
以上就是安详高跟鞋为你收集整理的在SpringBoot项目中封装OpenFeign写在最前封装方式的全部内容,希望文章能够帮你解决在SpringBoot项目中封装OpenFeign写在最前封装方式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复