• OpenFeign使用示例


    背景介绍

    OpenFeign是Spring Cloud的一个声明性HTTP客户端(出自于Netflix的Feign),它简化了我们与其他服务交互的方式。Spring Cloud对OpenFeign进行了增强,使得Spring Cloud OpenFeign支持Spring MVC注解。同时,Spring Cloud整合了Ribbon和 Eureka注册中心(Nacos也可以),这让 Spring Cloud OpenFeign的使用更加方便。

    Spring Cloud OpenFeign是一个声明式的 HTTP客户端,它简化了HTTP客户端的开发,使编写Web服务的客户端变得更容易。

    Spring Cloud OpenFeign能够帮助我们定义和实现依赖服务接口。在Spring Cloud OpenFeign的帮助下,只需要创建一个接口并用注解方式配置它,就可以完成服务提供方的接口绑定,减少在使用Spring Cloud Ribbon时自行封装服务调用客户端的开发量。

    注册中心

    首先搭建注册中心,本文以单机版Eureka作为注册中心

    依赖文件pom.xml

    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0modelVersion>
    	<parent>
    		<groupId>org.springframework.bootgroupId>
    		<artifactId>spring-boot-starter-parentartifactId>
    		<version>2.7.2version>
    		<relativePath/> 
    	parent>
    	<groupId>com.lwy.itgroupId>
    	<artifactId>eurekaartifactId>
    	<version>0.0.1-SNAPSHOTversion>
    	<name>eurekaname>
    	<description>Demo project for Spring Boot Servicedescription>
    	<properties>
    		<java.version>1.8java.version>
    		<spring-cloud.version>2021.0.3spring-cloud.version>
    	properties>
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.cloudgroupId>
    			<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
    		dependency>
    
    		<dependency>
    			<groupId>org.springframework.bootgroupId>
    			<artifactId>spring-boot-starter-testartifactId>
    			<scope>testscope>
    		dependency>
    	dependencies>
    	<dependencyManagement>
    		<dependencies>
    			<dependency>
    				<groupId>org.springframework.cloudgroupId>
    				<artifactId>spring-cloud-dependenciesartifactId>
    				<version>${spring-cloud.version}version>
    				<type>pomtype>
    				<scope>importscope>
    			dependency>
    		dependencies>
    	dependencyManagement>
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.bootgroupId>
    				<artifactId>spring-boot-maven-pluginartifactId>
    			plugin>
    		plugins>
    	build>
    
    project>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    启动类:

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(EurekaApplication.class, args);
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    application.yml配置如下:

    spring:
      application:
        name: eureka-server
    
    server:
      port: 8761
    
    eureka:
      instance:
        hostname: localhost
      client:
        # 是否将自己作为一个服务注册(这里是集群部署互相注册,所以需要作为一个服务注册), 默认为true
        register-with-eureka: false
        # 是否从其他eureka服务拉取已注册的服务信息, 默认为true
        fetch-registry: false
        service-url:
          defaultZone: http://localhost:8761/eureka/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    启动服务后访问:http://localhost:8761/

    服务提供者

    引入依赖pom.xml

    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
        <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>2.7.2version>
            <relativePath/> 
        parent>
        <groupId>com.lwy.itgroupId>
        <artifactId>demo-serviceartifactId>
        <version>0.0.1-SNAPSHOTversion>
        <name>demo-servicename>
        <description>Demo project for Spring Boot Servicedescription>
        <properties>
            <java.version>1.8java.version>
            <spring-cloud.version>2021.0.3spring-cloud.version>
        properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
            dependency>
    
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <optional>trueoptional>
            dependency>
        dependencies>
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloudgroupId>
                    <artifactId>spring-cloud-dependenciesartifactId>
                    <version>${spring-cloud.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
            dependencies>
        dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-maven-pluginartifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombokgroupId>
                                <artifactId>lombokartifactId>
                            exclude>
                        excludes>
                    configuration>
                plugin>
            plugins>
        build>
    
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    服务端提供一个BookVO,作为服务出入参:

    package com.lwy.it.vo;
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    @Data
    public class BookVO implements Serializable {
        private int bookId;
        private String bookName;
        private double bookPrice;
        private String bookDescription;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    同时提供一个BookController,包含表单,RequestBody,PathVariable等参数方法:

    package com.lwy.it.controller;
    
    import com.lwy.it.vo.BookVO;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PutMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Objects;
    
    @RestController
    @RequestMapping("/book")
    @Slf4j
    public class BookController {
        /**
         * 模拟数据库返回结果
         */
        private List<BookVO> database() {
            BookVO bookVO = new BookVO();
            bookVO.setBookId(1);
            bookVO.setBookName("MySQL实战教程");
            bookVO.setBookPrice(99.8);
            bookVO.setBookDescription("数据库,是一个程序员的必备技能。而MySQL作¬为时下最流行的关系型数据库管理系统,甚至在可以预见的未来MySQL都将是最流行的关系型数据库管理系统。");
            BookVO bookVO1 = new BookVO();
            bookVO1.setBookId(2);
            bookVO1.setBookName("愿你的青春不负梦想");
            bookVO1.setBookPrice(56.9);
            bookVO1.setBookDescription("梦想导师俞敏洪暌违两年,写给千万年轻人的诚意励志新作!");
            BookVO bookVO2 = new BookVO();
            bookVO2.setBookId(3);
            bookVO2.setBookName("大话设计模式");
            bookVO2.setBookPrice(66.6);
            bookVO2.setBookDescription("设计模式的趣味解读,面向对象的深入剖析。在诙谐与温馨中做一次面向对象编程思维的体操。");
            return Arrays.asList(bookVO, bookVO1, bookVO2);
        }
    
        /**
         * 无参数GET请求
         */
        @GetMapping("/list")
        public List<BookVO> getAllBooks() {
            return database();
        }
    
        /**
         * Path路径参数GET请求
         */
        @GetMapping("/{bookId}")
        public BookVO getBookById(@PathVariable Integer bookId) {
            List<BookVO> bookVOS = database();
            return bookVOS.parallelStream().filter((BookVO bookVO) -> {
                if (bookVO.getBookId() == bookId.intValue()) {
                    return true;
                }
                return false;
            }).findAny().get();
        }
    
        /**
         * 表单参数提交
         */
        @GetMapping("/login")
        public String login(String username, Integer password) {
            log.info("参数为,userName:{},password:{}", username, password);
            if (Objects.equals(username, "admin") && Objects.equals(password, 123456)) {
                return "SUCCESS";
            }
            return "FAILURE";
        }
    
        /**
         * JSON格式请求体参数
         */
        @PutMapping("/saveBook")
        public BookVO saveBook(@RequestBody BookVO bookVO) {
            log.info("存储Book为:{}", bookVO);
            return bookVO;
        }
    
        /**
         * RequestParam格式请求参数
         */
        @GetMapping("/param")
        public BookVO getBook(BookVO bookVO) {
            log.info("获取到的Book为:{}", bookVO);
            return bookVO;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    注册到Eureka注册中心配置,application.properties:

    spring.application.name=demo-server
    server.port=8088
    server.servlet.context-path=/server
    eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
    
    • 1
    • 2
    • 3
    • 4

    服务调用者

    引入依赖pom.xml:

    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
        <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>2.7.2version>
            <relativePath/> 
        parent>
        <groupId>com.lwy.itgroupId>
        <artifactId>demo-serviceartifactId>
        <version>0.0.1-SNAPSHOTversion>
        <name>demo-servicename>
        <description>Demo project for Spring Boot Servicedescription>
        <properties>
            <java.version>1.8java.version>
            <spring-cloud.version>2021.0.3spring-cloud.version>
        properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
            dependency>
    
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <optional>trueoptional>
            dependency>
        dependencies>
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloudgroupId>
                    <artifactId>spring-cloud-dependenciesartifactId>
                    <version>${spring-cloud.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
            dependencies>
        dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-maven-pluginartifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombokgroupId>
                                <artifactId>lombokartifactId>
                            exclude>
                        excludes>
                    configuration>
                plugin>
            plugins>
        build>
    
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    客户端配置,注意此处开启了GZIP。GZIP是一种数据格式,采用deflate算法压缩数据,是一种流行的文件压缩算法,应用十分广泛,当压缩—个纯文本文件时,效果是非常明显的,大约可以减少70%以上的文件大小。

    spring.application.name=demo-client
    server.port=8086
    server.servlet.context-path=/client
    eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
    
    # Spring Boot 默认日志级别是info,feign的debug日志级别就不会输入
    logging.level.com.lwy.it.feign=debug
    
    # 开启请求GZIP
    feign.compression.request.enabled=true
    # 开启响应GZIP
    feign.compression.response.enabled=true
    # 设置支持GZIP压缩的MIME类型,即请求/响应类型
    feign.compression.request.mime-types=text/xml,application/xml,application/json
    # 配置启动压缩数据量的最小阈值,单位字节。默认为2014
    feign.compression.request.min-request-size=1024
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    使用GZIP的优点在于网络数据经过压缩后实际上降低了网络传输的子节数,可以加快网页加载的速度。网页加载可以节省流量,改善用户的浏览体验。

    OpenFeign日志级别配置,OpenFeign可以开启请求响应详细日志打印,方便我们调试程序,在构建客户端、方法执行器的时候,都可以看到设置了日志类及日志级别。注意配置文件中配置日志级别为debug才可以看到。

    package com.lwy.it.config;
    
    import feign.Logger;
    import org.springframework.context.annotation.Bean;
    
    // @Configuration
    // 注意:此处使用@Configuration注解就会全局生效,如果想指定对应微服务生效,就不能配置
    public class FeignConfiguration {
    
        /**
         * 日志级别:
         * NONE:默认值,性能最佳,适用于生产环境,不记录任何日志
         * BASIC: 适用于生产环境问题追踪,仅记录请求方法、URL、响应状态代码以及执行时间
         * HEADERS:在BASIC基础上增加请求和响应header
         * FULL:比较适合开发及测试环境定位,记录请求和响应的header、body和元数据
         */
        @Bean
        public Logger.Level feignLoggerLevel() {
            return Logger.Level.FULL;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    使用Spring Cloud OpenFeign,只需要创建一个接口并注解,就能很容易地调用各服务提供的HTTP接口。

    通过OpenFeign Client访问服务端代码BookFeignService类,注意与服务提供方Controller对比异同点:

    package com.lwy.it.feign;
    
    import com.lwy.it.config.FeignConfiguration;
    import com.lwy.it.vo.BookVO;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PutMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import java.util.List;
    
    /**
     * name 指定调用rest接口所对应的服务名
     * path 指定调用rest接口所在Controller指定的@RequestMapping
     * configuration 局部配置,让调用的微服务生效,在@FeignClient注解中指定使用的配置类
     */
    
    @FeignClient(name = "demo-server", path = "/server/book", configuration = FeignConfiguration.class)
    public interface BookFeignService {
        // 声明需要调用rest接口对应的方法
        @GetMapping("/list")
        List<BookVO> getAllBooks();
    
        @GetMapping("/{bookId}")
        BookVO getBookById(@PathVariable("bookId") Integer id);
    
        /**
         * 在OpenFeign中方法参数前如果没有注解,默认添加@RequestBody注解,最多只能有一个不带注解的参数
         * 普通表单参数必须添加@RequestParam注解,如果变量名和参数名称对应可以不写name
         */
        @GetMapping("/login")
        String login(@RequestParam("username") String username, @RequestParam("password") Integer password);
    
        @PutMapping("/saveBook")
        BookVO saveBook(@RequestBody BookVO bookVO);
    
        @GetMapping("/param")
        BookVO getBook(@RequestParam int bookId, @RequestParam String bookName, @RequestParam String bookDescription, @RequestParam double bookPrice);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    这里的BookVO与服务提供方代码一致。

    启动类,启动类开启Feign Clients,否则报BookFeignService引入错误。

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    @SpringBootApplication
    @EnableFeignClients
    public class DemoClientApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(DemoClientApplication.class, args);
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    写一个调用验证的Controller(Spring Cloud OpenFeign能够帮助我们定义和实现依赖服务接口。在Spring Cloud OpenFeign的帮助下,只需要创建一个接口并用注解方式配置它,就可以完成服务提供方的接口绑定,减少在使用Spring Cloud Ribbon时自行封装服务调用客户端的开发量。):

    package com.lwy.it.controller;
    
    import com.lwy.it.feign.BookFeignService;
    import com.lwy.it.vo.BookVO;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.List;
    
    @RestController
    public class ClientController {
    
        // 直接引入BookFeignService,分别调用方法
        @Autowired
        private BookFeignService bookFeignService;
    
        @GetMapping("/books")
        public List<BookVO> getFirstBook() {
            return bookFeignService.getAllBooks();
        }
    
        @GetMapping("/book-id")
        public BookVO getBookById() {
            return bookFeignService.getBookById(2);
        }
    
        @GetMapping("/login")
        public String login() {
            return bookFeignService.login("admin", 123456);
        }
    
        @GetMapping("/book-save")
        public BookVO saveBook() {
            BookVO bookVO = new BookVO();
            bookVO.setBookId(100);
            bookVO.setBookPrice(199.99);
            bookVO.setBookName("OpenFeign教程");
            bookVO.setBookDescription("Spring Cloud出品,OpenFeign默认将Ribbon作为负载均衡器,直接内置了Ribbon。在导入OpenFeign依赖后无需专门导入Ribbon依赖。");
            return bookFeignService.saveBook(bookVO);
        }
    
        @GetMapping("/book-param")
        public BookVO getBook() {
            return bookFeignService.getBook(100, "OpenFeign教程", "Spring Cloud出品,OpenFeign默认将Ribbon作为负载均衡器,直接内置了Ribbon。在导入OpenFeign依赖后无需专门导入Ribbon依赖。", 199.99);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    访问http://localhost:8086/client/books等链接进行验证。

    Spring Cloud OpenFeign基于OpenFeign实现,它除了提供声明式的 HTTP客户端外,还整合了Spring Cloud Hystrix,能够轻松实现熔断器模型。

    推荐学习资料:https://blog.csdn.net/qq_43437874/category_11612066.html

  • 相关阅读:
    数图互通高校房产管理信息系统周转房如何管理?
    CentOS 同时安装多个版本的Python3
    力扣labuladong——一刷day44
    信息化与信息系统5
    RePaint: Inpainting using Denoising Diffusion Probabilistic Models 论文解读和感想
    【owt】构建m79的owt-client-native:使用vs2017
    过滤器、拦截器、AOP、ControllerAdvcie的使用对比、执行顺序及代码教程
    安装pytorch
    如何批量将 Excel 的工作表按行进行拆分
    Flask框架——基于Celery的后台任务
  • 原文地址:https://blog.csdn.net/liwenyang1992/article/details/126167210