跳至主要內容

SpringCloud编程起步

wangdx大约 14 分钟

RESTful 实现简介

RESTfUI

  • RESTfuI 项目设计中最重要的部分就是资源的获取,而在大部分的项目设计之中,资源般都需要进行有效的统一管理(例如:文件系统、关系型数据库、NOSQL 数据库等)在本项目中就将基于 MySQL 数据库实现资源的存储,本次将通过一张部门信息表保存资源数据。

DROP DATABASE IF EXISTS yootk8001;
CREATE DATABASE yootk8001 CHARACTER SET UTF8;
USE yootk8001;
CREATE TABLE dept (
    deptno		BIGINT 		AUTO_INCREMENT,
    dname			VARCHAR(50),
    loc			VARCHAR(50),
    CONSTRAINT pk_deptno PRIMARY KEY(deptno)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO dept(dname,loc) VALUES ('开发部', database());
INSERT INTO dept(dname,loc) VALUES ('财务部', database());
INSERT INTO dept(dname,loc) VALUES ('市场部', database());
INSERT INTO dept(dname,loc) VALUES ('后勤部', database());
INSERT INTO dept(dname,loc) VALUES ('公关部', database());
COMMIT;

构建 SpringCloud 项目

1、
ext.versions = [                // 定义全部的依赖库版本号
   springboot                      : '2.2.5.RELEASE',      // SpringBoot版本号
   springcloud                     : 'Hoxton.SR3', // SpringCloud版本号
   alibabacloud                    : '2.2.1.RELEASE', // SpringCloudAlibaba版本号
   lombok                          : '1.18.20', // Lombok版本号
   junit                           : '5.6.3', // 配置JUnit测试工具的版本编号
   junitPlatformLauncher           : '1.6.3',  // JUnit测试工具运行平台版本编号

]
ext.libraries = [            // 依赖库引入配置
   'spring-boot-gradle-plugin':
      "org.springframework.boot:spring-boot-gradle-plugin:${versions.springboot}",
   'spring-cloud-dependencies':
      "org.springframework.cloud:spring-cloud-dependencies:${versions.springcloud}",
   'spring-cloud-alibaba-dependencies':
      "com.alibaba.cloud:spring-cloud-alibaba-dependencies:${versions.alibabacloud}",
   // 以下的配置为与项目用例测试有关的依赖
   'junit-jupiter-api':
      "org.junit.jupiter:junit-jupiter-api:${versions.junit}",
   'junit-vintage-engine':
      "org.junit.vintage:junit-vintage-engine:${versions.junit}",
   'junit-jupiter-engine':
      "org.junit.jupiter:junit-jupiter-engine:${versions.junit}",
   'junit-platform-launcher':
      "org.junit.platform:junit-platform-launcher:${versions.junitPlatformLauncher}",
   'junit-platform-engine':
      "org.junit.platform:junit-platform-engine:${versions.junitPlatformLauncher}",
   'junit-jupiter-params':
      "org.junit.jupiter:junit-jupiter-params:${versions.junit}",
   'junit-bom': "org.junit:junit-bom:${versions.junit}",
   'junit-platform-commons':
      "org.junit.platform:junit-platform-commons:${versions.junitPlatformLauncher}",
   // 以下的配置为Lombok组件有关的依赖
   'lombok': "org.projectlombok:lombok:${versions.lombok}",
]


2、
buildscript {                        // 定义脚本使用资源
    apply from: 'dependencies.gradle' // 引入所需要的依赖库文件
    repositories {                        // 脚本资源仓库
        maven { url 'https://maven.aliyun.com/repository/public' }
    }
    dependencies {                        // 依赖库
        classpath libraries.'spring-boot-gradle-plugin' // SpringBoot插件
    }
}
group project_group 	// 组织名称
version project_version // 项目版本

apply from: 'dependencies.gradle' // 导入依赖配置
def env = System.getProperty("env") ?: 'dev' 	// 获取env环境属性
subprojects {   					// 配置子项目
    apply plugin: 'java' 				// 子模块插件
    apply plugin: 'org.springframework.boot' 	// 引入SpringBoot插件
    apply plugin: 'io.spring.dependency-management'	// 版本号管理
    sourceCompatibility = project_jdk   		// 源代码版本
    targetCompatibility = project_jdk   		// 生成类版本
    repositories {  				// 配置Gradle仓库
        mavenLocal()
        maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
        mavenCentral()
        jcenter()
    }
    dependencyManagement {// 版本控制插件
        imports {
            mavenBom libraries.'spring-cloud-dependencies' // SpringCloud依赖管理
            mavenBom libraries.'spring-cloud-alibaba-dependencies' // SpringCloudAlibaba依赖管理
        }
    }
    dependencies {  				// 公共依赖库管理
        compile('org.springframework.boot:spring-boot-devtools') // 项目热部署
        // 以下为测试环境的相关依赖配置
        testImplementation('org.springframework.boot:spring-boot-starter-test') {
            exclude group: 'junit', module: 'junit' // 移除Junit4
        }
        testImplementation(enforcedPlatform(libraries.'junit-bom')) // 绑定为JUnit5运行
        testImplementation(libraries.'junit-platform-commons') // Junit5测试组件
        testImplementation(libraries.'junit-platform-engine') // Junit5测试组件
        testImplementation(libraries.'junit-jupiter-api') // Junit5测试组件
        testImplementation(libraries.'junit-vintage-engine') // Junit5测试组件
        testImplementation(libraries.'junit-jupiter-engine') // Junit5测试组件
        testImplementation(libraries.'junit-platform-launcher') // Junit5测试组件
        // 以下为Lombok插件的相关依赖配置
        compileOnly(libraries.'lombok') // 编译时生效
        annotationProcessor(libraries.'lombok') // 注解时生效
    }
    sourceSets {    				// 源代码目录配置
        main { 					// main及相关子目录配置
            java { srcDirs = ['src/main/java'] }
            resources { srcDirs = ['src/main/resources', "src/main/profiles/$env"] }
        }
        test { 					// test及相关子目录配置
            java { srcDirs = ['src/test/java'] }
            resources { srcDirs = ['src/test/resources'] }
        }
    }
    test {  					// 配置测试任务
        useJUnitPlatform()				// 使用JUnit测试平台
    }
    task sourceJar(type: Jar, dependsOn: classes) { // 源代码的打包任务
        archiveClassifier = 'sources' 		// 设置文件的后缀
        from sourceSets.main.allSource 		// 所有源代码的读取路径
    }
    task javadocTask(type: Javadoc) { 		// JavaDoc文档打包任务
        options.encoding = 'UTF-8' 			// 设置文件编码
        source = sourceSets.main.allJava 		// 定义所有的Java源代码
    }
    task javadocJar(type: Jar, dependsOn: javadocTask) { // 先生成JavaDoc再打包
        archiveClassifier = 'javadoc' 		// 文件标记类型
        from javadocTask.destinationDir 		// 通过JavadocTask任务中找到目标路径
    }
    tasks.withType(Javadoc) {   			// 文档编码配置
        options.encoding = 'UTF-8' 			// 定义编码
    }
    tasks.withType(JavaCompile) {   		// 编译编码配置
        options.encoding = 'UTF-8' 			// 定义编码
    }
    artifacts { 					// 最终的打包的操作任务
        archives sourceJar 			// 源代码打包
        archives javadocJar 			// javadoc打包
    }
    gradle.taskGraph.whenReady {    		// 在所有的操作准备好后触发
        tasks.each { task ->    			// 找出所有的任务
            if (task.name.contains('test')) {   	// 如果发现有test任务
                // 如果将enabled设置为true表示要执行测试任务,如果设置为false表示不执行测试任务
                task.enabled = true
            }
        }
    }
    [compileJava, compileTestJava, javadoc]*.options*.encoding = 'UTF-8'// 编码配置
}

3、
127.0.0.1	provider-dept-8001
127.0.0.1	consumer-springboot-80


4、
https://gitee.com/threesoil/microcloud

REST 公共模块

微服务提供者

ext.versions = [
        springboot   : '2.6.13',      // SpringBoot版本号
        springcloud  : '2021.0.5', // SpringCloud版本号
        alibabacloud : '2021.0.5.0', // SpringCloudAlibaba版本号
        lombok       : '1.18.20', // Lombok版本号
        junit        : '5.9.3',          // 配置JUnit测试工具的版本编号
        junitPlatform: '1.9.3',          // JUnit测试工具运行平台版本编号
        mysqlJ       : '8.0.33',          // MySQL驱动的版本
        druid        : '1.2.21',          // Druid版本
        mybatisPlus  : '3.5.5', // MyBatisPlus依赖版本
]
ext.libraries = [
        'spring-boot-gradle-plugin'        : "org.springframework.boot:spring-boot-gradle-plugin:${versions.springboot}",
        'spring-cloud-dependencies'        : "org.springframework.cloud:spring-cloud-dependencies:${versions.springcloud}",
        'spring-cloud-alibaba-dependencies': "com.alibaba.cloud:spring-cloud-alibaba-dependencies:${versions.alibabacloud}",
        // 以下的配置为与项目用例测试有关的依赖
        'junit-jupiter-api'                : "org.junit.jupiter:junit-jupiter-api:${versions.junit}",
        'junit-vintage-engine'             : "org.junit.vintage:junit-vintage-engine:${versions.junit}",
        'junit-jupiter-engine'             : "org.junit.jupiter:junit-jupiter-engine:${versions.junit}",
        'junit-platform-launcher'          : "org.junit.platform:junit-platform-launcher:${versions.junitPlatform}",
        'junit-platform-engine'            : "org.junit.platform:junit-platform-engine:${versions.junitPlatform}",
        'junit-jupiter-params'             : "org.junit.jupiter:junit-jupiter-params:${versions.junit}",
        'junit-bom'                        : "org.junit:junit-bom:${versions.junit}",
        'junit-platform-commons'           : "org.junit.platform:junit-platform-commons:${versions.junitPlatform}",
        // 以下的配置为Lombok组件有关的依赖
        'lombok'                           : "org.projectlombok:lombok:${versions.lombok}",
        // 以下的配置为数据库开发所需要的依赖:
        'mysql-connector-j'                : "com.mysql:mysql-connector-j:${versions.mysqlJ}",
        'druid-spring-boot-starter'        : "com.alibaba:druid-spring-boot-starter:${versions.druid}",
        'druid'                            : "com.alibaba:druid:${versions.druid}",
        'mybatis-plus-boot-starter'        : "com.baomidou:mybatis-plus-boot-starter:${versions.mybatisPlus}",
]
2.
project(":provider-dept-8001") {    // 部门微服务
    dependencies {
        implementation(project(":common-api")) // 导入公共的子模块
        implementation(libraries.'mybatis-plus-boot-starter')
        implementation(libraries.'mysql-connector-j')
        implementation(libraries.'druid')
    }
}
3.
server: # 服务端配置
  port: 8001 # 8001端口
mybatis-plus: # MyBatisPlus配置
  type-aliases-package: com.yix.provider.vo  # 别名配置
spring:
  datasource: # 数据源配置
    type: com.alibaba.druid.pool.DruidDataSource    # 数据源类型
    driver-class-name: com.mysql.cj.jdbc.Driver     # 驱动程序类
    url: jdbc:mysql://192.168.16.8:3306/yootk8001          # 连接地址
    username: root                                  # 用户名
    password: root                            # 连接密码
    druid: # druid相关配置
      initial-size: 5                               # 初始化连接池大小
      min-idle: 10                                  # 最小维持连接池大小
      max-active: 50                                # 最大支持连接池大小
      max-wait: 60000                               # 最大等待时间
      time-between-eviction-runs-millis: 60000      # 关闭空闲连接间隔
      min-evictable-idle-time-millis: 30000         # 连接最小生存时间
      validation-query: SELECT 1 FROM dual          # 状态检测
      test-while-idle: true # 空闲时检测连接是否有效
      test-on-borrow: false # 申请时检测连接是否有效
      test-on-return: false # 归还时检测连接是否有效
      pool-prepared-statements: false # PSCache缓存
      max-pool-prepared-statement-per-connection-size: 20 # 配置PS缓存
      filters: stat, wall, slf4j # 开启过滤
      stat-view-servlet: # 监控界面配置
        enabled: true # 启用druid监控界面
        allow: 127.0.0.1      # 访问白名单
        login-username: muyan # 用户名
        login-password: yootk # 密码
        reset-enable: true # 允许重置
        url-pattern: /druid/* # 访问路径
      web-stat-filter:
        enabled: true # 启动URI监控
        url-pattern: /* # 跟踪全部服务
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" # 跟踪排除
      filter:
        slf4j: # 日志
          enabled: true # 启用SLF4j监控
          data-source-log-enabled: true # 启用数据库日志
          statement-executable-sql-log-enable: true # 执行日志
          result-set-log-enabled: true # ResultSet日志启用
        stat: # SQL监控
          merge-sql: true # 合并统计
          log-slow-sql: true # 慢执行记录
          slow-sql-millis: 1 # 慢SQL执行时间
        wall: # SQL防火墙
          enabled: true   # SQL防火墙
          config: # 防火墙规则
            multi-statement-allow: true # 允许执行批量SQL
            delete-allow: false # 禁止执行删除语句
      aop-patterns: "com.yootk.provider.action.*,com.yootk.provider.service.*,com.yootk.provider.dao.*" # Spring监控

Postman.Apifox 接口测试

Postman

  • Postman 是一款强大的页面调试工具的客户端可以直接实现 WebAPI& HTTP 请求调试功能,同时也可以模拟任意的 HTTP 请求模式实现请求参数以及头信息的发送。Postman 为开源工具,如果需要使用该工具,直接登录"www.postman.com"官方网站即可下载

微服务消费者

REST 消费端

  • SpringCloud 中所提供的微服务为一个个独立的原子单位,而在进行业务处理时,往往需要将通过若干个微服务的调用才可以实现完整的处理业务,同时考虑到安全性的问题大部分微服务只允许定义为内网访问。这这样一来如果要想对外提供完整的业务逻辑,就需要有一个服务整合的应用(或者称为“消费端应用”)出现,该应用可以调用内网服务,同时又可以对外提供新的服务接口

1.
@Configuration
public class RestTemplateConfig { // 实现RestTemplate的相关配置
    @Autowired
    private MicroServiceHTTPInterceptor interceptor; // 注入拦截器
    @Bean // 向Spring容器之中进行Bean注册
    public RestTemplate getRestTemplate() {
        RestTemplate template = new RestTemplate();
        template.setInterceptors(Collections.singletonList(this.interceptor));
        return template;
    }
}

2.
@Slf4j // 添加日志注解
@Component // 配置之后自动拦截生效
public class MicroServiceHTTPInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        log.info("【HTTP请求拦截】服务主机:{}、REST路径:{}",
                request.getURI().getHost(), request.getURI().getPath());
        // 此时可以将一些Token的数据保存在头信息之中,会随着每次的请求一起发送到服务端
        request.getHeaders().set("token", "www.yootk.com"); // 随意添加的头信息
        return execution.execute(request, body); // 发送请求
    }
}
3.
@RestController
@RequestMapping("/consumer/dept/*") // 两个不同的服务路径
public class DeptConsumerAction { // 消费端Action
    // 定义出要访问的部门微服务所需要的核心路径前缀,随后在具体请求的时候添加传递的参数
    public static final String DEPT_ADD_URL =
            "http://provider-dept-8001:8001/provider/dept/add";
    public static final String DEPT_GET_URL =
            "http://provider-dept-8001:8001/provider/dept/get/"; // id是自己变更的
    public static final String DEPT_LIST_URL =
            "http://provider-dept-8001:8001/provider/dept/list";
    public static final String DEPT_SPLIT_URL =
            "http://provider-dept-8001:8001/provider/dept/split";
    @Autowired
    private RestTemplate restTemplate; // 本章你是主角儿

    @GetMapping("add") // 消费端接口名称
    public Object addDept(DeptDTO dto) {
        // 需要将当前的DTO对象传递到部门微服务之中,所以此时就要通过RestTemplate对象处理
        return this.restTemplate.postForObject(DEPT_ADD_URL, dto, Boolean.class);
    }

    @GetMapping("get/{id}")
    public Object get(@PathVariable("id") Long deptno) {
        return this.restTemplate.getForObject(DEPT_GET_URL + deptno, DeptDTO.class);
    }

    @GetMapping("list")
    public Object list() {
        return this.restTemplate.getForObject(DEPT_LIST_URL, List.class);
    }

    @GetMapping("split")
    public Object split(int cp, int ls, String col, String kw) {
        return this.restTemplate.getForObject(DEPT_SPLIT_URL + "?cp=" + cp + "&ls=" + ls + "&col=" + col + "&kw=" + kw, Map.class);
    }
}

HTTP 请求拦截

Swagger 接口描述 springdoc-openapi-ui

REST 接口文档

  • 随着项目业务的不断完善,!一定会带来更多的微服务节点,而为了便于微服务的调用那么就需要对微服务提供详细且完善的使用说明以及调用测试环境,为了解决这类问题在项目开发中可以通过 Swagger 项目来实现文档生成与维护操作

Swagger 框架现在的最新版本是 3.x,官网地址为:springfox.io,该版本可以更加方便的与 SpringBoot 进行整合。同时在该版本对应的 GITHUB 中(地址:https://github.com/springfox/springfox)已经明确给出了Swagger框架的快速整合应用。

1、
// https://mvnrepository.com/artifact/io.springfox/springfox-boot-starter
implementation group: 'io.springfox', name: 'springfox-boot-starter', version: '3.0.0'

// https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui
implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.4.0'
// https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-ui
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.7.0'

2、
ext.versions = [                // 定义全部的依赖库版本号
    springboot           : '2.2.5.RELEASE',      // SpringBoot版本号
    springcloud          : 'Hoxton.SR3', // SpringCloud版本号
    alibabacloud         : '2.2.1.RELEASE', // SpringCloudAlibaba版本号
    lombok               : '1.18.20', // Lombok版本号
    junit                : '5.6.3', // 配置JUnit测试工具的版本编号
    junitPlatformLauncher: '1.6.3',  // JUnit测试工具运行平台版本编号
    mybatisPlus          : '3.4.3', // MyBatisPlus的版本号
    mysql                : '8.0.25', // MySQL数据库驱动版本
    druid                : '1.2.6', // Druid版本号
    swagger              : '3.0.0', // Swagger版本号
]
ext.libraries = [            // 依赖库引入配置
     'spring-boot-gradle-plugin'        :
             "org.springframework.boot:spring-boot-gradle-plugin:${versions.springboot}",
     'spring-cloud-dependencies'        :
             "org.springframework.cloud:spring-cloud-dependencies:${versions.springcloud}",
     'spring-cloud-alibaba-dependencies':
             "com.alibaba.cloud:spring-cloud-alibaba-dependencies:${versions.alibabacloud}",
     // 以下的配置为与项目用例测试有关的依赖
     'junit-jupiter-api'                :
             "org.junit.jupiter:junit-jupiter-api:${versions.junit}",
     'junit-vintage-engine'             :
             "org.junit.vintage:junit-vintage-engine:${versions.junit}",
     'junit-jupiter-engine'             :
             "org.junit.jupiter:junit-jupiter-engine:${versions.junit}",
     'junit-platform-launcher'          :
             "org.junit.platform:junit-platform-launcher:${versions.junitPlatformLauncher}",
     'junit-platform-engine'            :
             "org.junit.platform:junit-platform-engine:${versions.junitPlatformLauncher}",
     'junit-jupiter-params'             :
             "org.junit.jupiter:junit-jupiter-params:${versions.junit}",
     'junit-bom'                        : "org.junit:junit-bom:${versions.junit}",
     'junit-platform-commons'           :
             "org.junit.platform:junit-platform-commons:${versions.junitPlatformLauncher}",
     // 以下的配置为Lombok组件有关的依赖
     'lombok'                           : "org.projectlombok:lombok:${versions.lombok}",
     // 以下的配置为数据库开发有关的依赖
     'mybatis-plus-boot-starter'        : "com.baomidou:mybatis-plus-boot-starter:${versions.mybatisPlus}",
     'mysql-connector-java'             : "mysql:mysql-connector-java:${versions.mysql}",
     'druid'                            : "com.alibaba:druid:${versions.druid}",
     // 以下的配置为Swagger有关的依赖库
    'springfox-boot-starter'            : "io.springfox:springfox-boot-starter:${versions.swagger}"
]


3、
project(":provider-dept-8001") {    // 部门微服务
    dependencies {
        implementation(project(":common-api")) // 导入公共的子模块
        implementation(libraries.'mybatis-plus-boot-starter')
        implementation(libraries.'mysql-connector-java')
        implementation(libraries.'druid')
        implementation(libraries.'springfox-boot-starter')
    }
}

4、
http://provider-dept-8001:8001/swagger-ui/

顺着这个 issue 看了一下,这个问题主要出现在 Spring Boot 2.6 及以后,只要是 Spring Boot 2.6 引入的新 PathPatternParser 导致的。

1.
Path 匹配策略切换回 ant_path_matcher
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
2.
添加下面这个Bean的定义
@Bean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) {
  List<ExposableEndpoint<?>> allEndpoints = new ArrayList();
  Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
  allEndpoints.addAll(webEndpoints);
  allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
  allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
  String basePath = webEndpointProperties.getBasePath();
  EndpointMapping endpointMapping = new EndpointMapping(basePath);
  boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
  return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
}

private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
  return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}

@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
    return new BeanPostProcessor() {

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
            }
            return bean;
        }

        private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
            List<T> copy = mappings.stream()
                    .filter(mapping -> mapping.getPatternParser() == null)
                    .collect(Collectors.toList());
            mappings.clear();
            mappings.addAll(copy);
        }

        @SuppressWarnings("unchecked")
        private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
            try {
                Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
                field.setAccessible(true);
                return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                throw new IllegalStateException(e);
            }
        }
    };
}

REST 接口描述

1、
package com.yootk.provider.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

@Configuration
public class SwaggerConfig { // Swagger配置类
    private ApiInfo getApiInfo() { // 文档之中的头部的信息项
        return new ApiInfoBuilder().title("【沐言科技】部门微服务")
                .description("实现部门数据的统一管理,包括:增加部门信息、查询部门信息、部门列表显示等,此处省略5000字...")
                .termsOfServiceUrl("https://www.yootk.com")
                .contact(new Contact("爆可爱的小李老师", "edu.yootk.com", "784420216@qq.com"))
                .license("沐言科技 - 授权管理").version("1.0.0").build();
    }
    @Bean
    public Docket getDocker() { // 所有的详细描述在此类中定义
        return new Docket(DocumentationType.SWAGGER_2) // 使用的文档版本类型
            .apiInfo(this.getApiInfo())
            .select() // 所有的接口一定要放在指定的包中
            .apis(RequestHandlerSelectors.basePackage("com.yootk.provider.action"))
                .paths(PathSelectors.any()).build();

    }
}


2、
package com.yootk.provider.action;

import com.yootk.common.dto.DeptDTO;
import com.yootk.service.IDeptService;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

@RestController
@RequestMapping("/provider/dept/*") // 微服务提供者父路径
@Slf4j // 使用一个注解
public class DeptAction {
    @Autowired
    private IDeptService deptService;
    @ApiOperation(value="部门查询", notes = "根据部门编号查询部门详细信息")
    @GetMapping("get/{id}")
    public Object get(@PathVariable("id") long id) {
        this.printRequestHeaders("get");
        return this.deptService.get(id);
    }
    @ApiOperation(value="部门增加", notes = "增加新的部门信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "deptDTO", required = true,
                    dataType = "DeptDTO", value = "部门传输对象实例")
    })
    @PostMapping("add")
    public Object add(@RequestBody  DeptDTO deptDTO) {    // 后面会修改参数模式为JSON
        this.printRequestHeaders("add");
        return this.deptService.add(deptDTO);
    }
    @ApiOperation(value="部门列表", notes = "查询部门的完整信息")
    @GetMapping("list")
    public Object list() {
        this.printRequestHeaders("list");
        return this.deptService.list();
    }
    @ApiOperation(value="部门分页查询", notes = "根据指定的数据库参数实现部门数据的分页加载")
    @ApiImplicitParams({
            @ApiImplicitParam(name="cp", value = "当前所在页", required = true, dataType = "int"),
            @ApiImplicitParam(name="ls", value = "每页显示的数据行数", required = true, dataType = "int"),
            @ApiImplicitParam(name="col", value = "模糊查询列", required = true, dataType = "String"),
            @ApiImplicitParam(name="kw", value = "模糊查询关键字", required = true, dataType = "String")
    })
    @GetMapping("split")
    public Object split(int cp, int ls, String col, String kw) {
        this.printRequestHeaders("split");
        return this.deptService.split(cp, ls, col, kw);
    }
    private void printRequestHeaders(String restName) {    // 实现所有请求头信息的输出
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        Enumeration<String> headerEnums = request.getHeaderNames();
        while (headerEnums.hasMoreElements()) {
            String headerName = headerEnums.nextElement();
            log.info("【{}】头信息:{} = {}", restName, headerName, request.getHeader(headerName));
        }
    }
}

@Configuration
public class SpringDocConfig {
    @Bean
    public OpenAPI myOpenAPI() {
        return new OpenAPI()
                .info(new Info()
                        .title("程序员API")
                        .description("程序员的大本营")
                        .version("v1.0.0")
                        .license(new License()
                                .name("许可协议")
                                .url("https://shusheng007.top"))
                        .contact(new Contact()
                                .name("书生007")
                                .email("wangben850115@gmail.com")))
                .externalDocs(new ExternalDocumentation()
                        .description("ShuSheng007博客")
                        .url("https://shusheng007.top"));
    }
}
@Configuration
public class SpringDocConfig {
   ...
    @Bean
    public GroupedOpenApi publicApi() {
        return GroupedOpenApi.builder()
                .group("api")
                .pathsToMatch("/api/**")
                .build();
    }

    @Bean
    public GroupedOpenApi adminApi() {
        return GroupedOpenApi.builder()
                .group("admin")
                .pathsToMatch("/admin/**")
                .build();
    }
}
@RestController
@Tag(name = "程序员", description = "程序员乐园")
@RequestMapping("/provider/dept/*") // 微服务提供者父路径
@Slf4j // 使用一个注解
public class DeptAction {
    @Autowired
    private IDeptService deptService;

    @GetMapping("get/{id}")
    @Operation(summary = "创建程序员", description = "用于创建一个闷骚的程序员")
    public Object get(@PathVariable("id") long id) {
        this.printRequestHeaders("get");
        return this.deptService.get(id);
    }

@Data // Lombok注解,自动生成所需要的类结构
@Schema(description = "创建程序员入参")
public class DeptDTO implements Serializable {
    private Long deptno; // 部门编号
    @Schema(description = "名称", example = "王二狗")
    private String dname; // 部门名称
    @Min(18)
    @Max(35)
    @Schema(description = "年龄", example = "35")
    private String loc; // 部门位置
}

Swagger 安全配置

1、
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '2.5.3'


2、
project(":provider-dept-8001") {    // 部门微服务
    dependencies {
        implementation(project(":common-api")) // 导入公共的子模块
        implementation(libraries.'mybatis-plus-boot-starter')
        implementation(libraries.'mysql-connector-java')
        implementation(libraries.'druid')
        implementation(libraries.'springfox-boot-starter')
        implementation('org.springframework.boot:spring-boot-starter-security')
    }
}

3、
package com.yootk.test;

import org.springframework.security.crypto.factory.PasswordEncoderFactories;

public class TestCreatePassword {
    public static void main(String[] args) {
        String pwd = PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("yootk");
        System.out.println(pwd);
    }
}


4、
package com.yootk.provider.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SwaggerWebSecurityConfig extends WebSecurityConfigurerAdapter { // Swagger安全配置
    // 如果有其他的需要,你可以继续进行数据库的连接配置,具体的讲解已经提供过了
    private static final String DEFAULT_PASSWORD =
            "{bcrypt}$2a$10$bvOY6ixvY5DmgiNW.Z79qeV9abQM9a6NbM1n9sejeUnB98C0kKAMu";
    @Bean
    public PasswordEncoder getPasswordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("swagger") // 默认用户名
                .password(DEFAULT_PASSWORD) // 默认密码
                .roles("USER", "ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/swagger-ui/**", "/v2/api-docs").hasRole("ADMIN")
                .and().httpBasic().and().formLogin()
                .permitAll().and().csrf().disable();
    }
}


5、
http://provider-dept-8001:8001/swagger-ui

demo


上次编辑于: