文章目录
一、OpenFeign简介
OpenFeign 利用 Ribbon 维护了远程服务的列表信息,以实现客户端侧的负载均衡。
二、Springboot集成OpenFeign
1、引入依赖
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、@EnableFeignClients注解
(1)应用
在使用openfegin时需要再启动类上加上改注解,以启用 OpenFeign。
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
(2)属性解析
@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 {};
}
- basePackages 或 value
这两个属性是等价的,用于指定扫描 Feign 客户端接口的包路径。Spring Cloud 会自动扫描这些包下的接口,并为它们创建代理实现,这些代理会实现声明的 Feign 客户端接口。
@EnableFeignClients(basePackages = "com.example.demo.client")
或
@EnableFeignClients(value = "com.example.demo.client")
- defaultConfiguration
这个属性允许你指定一个默认的配置类,该类会应用于所有 Feign 客户端,除非它们有自己特定的配置。
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)
- clients
这个属性允许你明确地指定哪些接口应该作为 Feign 客户端来创建。这在你不想扫描整个包,而只想为特定的接口创建 Feign 客户端时非常有用。
@EnableFeignClients(clients = {MyServiceClient.class, AnotherServiceClient.class})
- basePackageClasses
@EnableFeignClients 注解中的 basePackageClasses 属性是一个替代 basePackages 或 value 的方式,用于指定扫描 Feign 客户端接口的基准类。Spring Cloud 会扫描这些基准类所在的包及其子包,以查找标有 @FeignClient 注解的接口,并为这些接口创建代理实现。
使用 basePackageClasses 而不是直接指定包路径的好处是,它提供了类型安全的方式来定义扫描的包。你只需提供一个位于所需包中的类的 Class 对象,Spring Cloud 就会自动确定该类的包路径,并扫描该包及其子包。
下面是一个使用 basePackageClasses 的例子:
@SpringBootApplication
@EnableFeignClients(basePackageClasses = MyFeignClient.class)
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
在这个例子中,假设 MyFeignClient 接口位于 com.example.demo.client 包中。通过指定 MyFeignClient.class 作为 basePackageClasses 的值,Spring Cloud 将扫描 com.example.demo.client 包及其所有子包,查找带有 @FeignClient 注解的接口。
这样做的好处是,即使你的包结构发生变化(例如,重命名包),只要 MyFeignClient 接口仍然位于相同的包中,你就不需要更新 @EnableFeignClients 注解。因此,它提供了一种更灵活的方式来指定扫描的包,减少了由于包路径更改而导致的维护工作量。
需要注意的是,basePackageClasses 和 basePackages/value 是互斥的,你不应该同时指定它们。你应该选择其中一种方式来定义扫描的包。通常,如果你的应用结构较为简单,直接指定包路径可能更直接。而在更复杂的场景中,使用 basePackageClasses 可能会更加灵活和易于维护。
3、 @FeignClient
(1)应用
定义一个接口,并使用 @FeignClient 注解指定远程服务的名称。
import feign.Response;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.constraints.NotBlank;
import java.util.List;
@FeignClient(name = "xxx", contextId = "FileServiceAPI", path = url)
@Validated
public interface FileServiceAPI {
/**
* 获取文件详情接口
* @param code
* @return
*/
@GetMapping
BizBaseResponse<FileInfoVo> getFileDetail(@NotBlank @RequestParam(name = "code") String code);
@GetMapping("files")
BizBaseResponse<List<FileInfoVo>> getFileDetails( @RequestParam(name = "codes") List<String> codes);
/**
* 文件下载接口
* @param code
*/
@GetMapping("/download")
Response download(@RequestParam(name = "code") String code);
(2)属性解析
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface FeignClient {
// name和value属性用于标注客户端名称,也可以用${propertyKey}获取配置属性
@AliasFor("name")
String value() default "";
// 该类的Bean名称
String contextId() default "";
// name和value属性用于标注客户端名称,也可以用${propertyKey}获取配置属性
@AliasFor("value")
String name() default "";
// 弃用 被qualifiers()替代。
@Deprecated
String qualifier() default "";
// 模拟客户端的@Qualifiers值。如果qualifier()和qualifiers()都存在,我们将使用后者,除非qualifier()返回的数组为空或只包含空值或空白值,在这种情况下,我们将首先退回到qualifier(),如果也不存在,则使用default = contextId + "FeignClient"。
String[] qualifiers() default {};
// 绝对URL或可解析主机名
String url() default "";
// 是否应该解码404而不是抛出FeignExceptions
boolean decode404() default false;
// 用于模拟客户端的自定义配置类。可以包含组成客户端部分的覆盖@Bean定义,默认配置都在FeignClientsConfiguration类中,可以指定FeignClientsConfiguration类中所有的配置
Class<?>[] configuration() default {};
// 指定失败回调类
Class<?> fallback() default void.class;
// 为指定的假客户端接口定义一个fallback工厂。fallback工厂必须生成fallback类的实例,这些实例实现了由FeignClient注释的接口。
Class<?> fallbackFactory() default void.class;
// 所有方法级映射使用的路径前缀
String path() default "";
// 是否将虚拟代理标记为主bean。默认为true。
boolean primary() default true;
}
(3)向Fegin客户端提供URL的几种方式
- 使用@FeignClient注解的url属性
这是最直接的方式。你可以在@FeignClient注解中直接指定URL。这样,Feign客户端就会使用这个URL来进行请求。
@FeignClient(name = "myServiceClient", url = "http://example.com/api")
public interface MyServiceClient {
// 定义你的方法
}
在上面的例子中,MyServiceClient接口将使用http://example.com/api作为请求的基础URL。没有负载均衡能力,只能定向发送。
- 使用配置文件:
你可以在应用的配置文件(如application.yml或application.properties)中设置Feign客户端的URL。然后,你可以通过@Value注解或者@ConfigurationProperties来注入这个URL到Feign客户端的配置中。
# application.yml
my-service-client:
url: http://example.com/api
@FeignClient(name = "myServiceClient", configuration = MyServiceClientConfiguration.class)
public interface MyServiceClient {
// 定义你的方法
}
@Configuration
public class MyServiceClientConfiguration {
@Value("${my-service-client.url}")
private String url;
@Bean
public Request.Options options() {
return new Request.Options(connectTimeoutMillis(), readTimeoutMillis());
}
private int connectTimeoutMillis() {
return 10 * 1000;
}
private int readTimeoutMillis() {
return 60 * 1000;
}
}
在这个例子中,MyServiceClientConfiguration类从配置文件中读取URL,并可能还配置了其他的Feign请求选项。没有负载均衡能力,只能定向发送,优先使用注解中的url。
- 使用服务发现
如果你的应用运行在Spring Cloud环境中,并使用了服务发现(如Eureka或Consul),那么通常不需要在Feign客户端中直接指定URL。相反,你可以通过服务名称来引用远程服务,Spring Cloud会自动解析服务名称到实际的URL。
@FeignClient(name = "my-service")
public interface MyServiceClient {
// 定义你的方法
}
在这个例子中,my-service是注册在服务发现中的服务名称。当发起请求时,Spring Cloud会查询服务发现来找到my-service的实例,并使用它们的URL进行请求。
三、openFegin应用
1、使用openfegin下载文件
(1)下载文件Controler实现
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@RestController
public class FileDownloadController {
@GetMapping("/download")
public void downloadFile(HttpServletResponse response) throws IOException {
// 文件路径
String filePath = "path/to/your/file.txt";
// 创建文件路径
Path path = Paths.get(filePath);
Resource resource = new FileSystemResource(path);
// 获取文件名
String filename = resource.getFilename();
// 设置响应头
response.reset();
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"");
// 读取文件内容并写入响应输出流
try (InputStream inputStream = Files.newInputStream(path);
OutputStream outputStream = response.getOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
}
}
}
(2)openfegin配置
import feign.Response;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.constraints.NotBlank;
import java.util.List;
@FeignClient(name = "xxx", contextId = "FileServiceAPI", path = url)
@Validated
public interface FileServiceAPI {
/**
* 文件下载接口
* @param code
*/
@GetMapping("/download")
Response download();
其中Response引用的是:feign.Response
(3)调用openfegin接口方法
public File downFileByCode() {
String path = PROJECT_PATH;
Response response = fileServiceAPI.download(ossFileCode);
mkDirs(path);
File file = new File(path);
if (response.status() == 200) {
try (InputStream inputStream = response.body().asInputStream();
FileOutputStream outputStream = new FileOutputStream(file)) {
// 将响应体中的字节写入目标文件
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch(Exception e) {
LOGGER.error(e.getMessage(), e);
throw new RunTimeException(e.getMessage());
}
}
return file;
}
public static String mkDirs(String filePath) {
if (StringUtils.isBlank(filePath)) {
throw new RuntimeException("file path is null");
}
if (!filePath.endsWith(File.separator)) {
LOGGER.info("mkdir , file path is {}", filePath);
filePath = filePath.substring(0, filePath.lastIndexOf(File.separator));
}
//如果目录不存在,自动创建文件夹
File dir = new File(filePath);
if (!dir.exists()) {
dir.mkdirs();
}
return dir.getAbsolutePath();
}
标签:FeignClient,openFegin,String,default,Spring,import,public,Cloud,客户端
From: https://blog.csdn.net/yuming226/article/details/137043148