002:代码管理GitLab与Maven私服
1 Maven私服与Gateway整合Swagger演示
今日课程任务
- 微服务Api接口响应Code规范
- 微服务接口Api文档管理整合
- SpringCloudGateway整合Swagger文档
- 构建企业级Maven私服管理
2 构建微服务接口响应Code码
接口规范的定义
{“code”:500,“msg”:“appId不能为空!”,“data”:null}
创建模块mt-shop-service-api-base
mt-shop-parent
--------mt-shop-service-base
---------------- mt-shop-service-api-base 相关base继承类
拷贝工具类
public interface Constants {
// 响应请求成功
String HTTP_RES_CODE_200_VALUE = "success";
// 系统错误
String HTTP_RES_CODE_500_VALUE = "fail";
// 响应请求成功code
Integer HTTP_RES_CODE_200 = 200;
// 系统错误
Integer HTTP_RES_CODE_500 = 500;
}
@Data
public class BaseResponse<T> {
/**
* 返回码
*/
private Integer code;
/**
* 消息
*/
private String msg;
/**
* 返回
*/
private T data;
// 分页
public BaseResponse() {
}
public BaseResponse(Integer code, String msg, T data) {
super();
this.code = code;
this.msg = msg;
this.data = data;
}
}
@Data
public class BaseApiService<T> {
public BaseResponse<T> setResultError(Integer code, String msg) {
return setResult(code, msg, null);
}
/**
* 返回错误,可以传msg
*
* @param msg
* @return
*/
public BaseResponse<T> setResultError(String msg) {
return setResult(Constants.HTTP_RES_CODE_500, msg, null);
}
/***
* 返回成功,可以传data值
* @param data
* @return
*/
public BaseResponse<T> setResultSuccess(T data) {
return setResult(Constants.HTTP_RES_CODE_200, Constants.HTTP_RES_CODE_200_VALUE, data);
}
/**
* 返回成功,沒有data值
*
* @return
*/
public BaseResponse<T> setResultSuccess() {
return setResult(Constants.HTTP_RES_CODE_200, Constants.HTTP_RES_CODE_200_VALUE, null);
}
/**
* 通用封装 通用封装
*
* @param code
* @param msg
* @param data
* @return
*/
public BaseResponse<T> setResult(Integer code, String msg, T data) {
return new BaseResponse<T>(code, msg, data);
}
}
mt-shop-service-api 工程引入mt-shop-service-api-base项目即可
会员实现类继承BaseApiService类
@RestController
public class WeiXinServiceImpl extends BaseApiService implements WeiXinService {
@Override
public BaseResponse<String> addApp(String appId, String appPwd) {
if (StringUtils.isEmpty(appId)) {
return setResultError("appId不能为空");
}
if (StringUtils.isEmpty(appPwd)) {
return setResultError("appPwd不能为空");
}
return setResultSuccess("蚂蚁课堂");
}
}
测试效果:
3 微服务项目整合Swagger文档
构建企业级别Api文档管理
接口文档主要提供给前端使用,包括请求地址、请求类型、请求方法、请求参数、响应code、响应的data等,使用swagger可以基于注解的形式生成文档。
Maven依赖配置
<!-- swagger-spring-boot -->
<dependency>
<groupId>com.spring4all</groupId>
<artifactId>swagger-spring-boot-starter</artifactId>
<version>1.7.0.RELEASE</version>
</dependency>
application.yml新增Swagger相关配置
swagger:
base-package: com.mayikt.service.impl
title: SpringCloud2.x构建微服务电商项目-微信服务接口
description: 基于SpringCloud2.x构建微服务电商项目
version: 1.1
terms-of-service-url: www.mayikt.com
contact:
name: maplefire
email: 1109733977@qq.com
url: www.mayikt.com
enabled: true
接口中Swagger注解配置
@Api(tags = "微信基本服务接口")
public interface WeiXinService {
/**
* 微信接口
*
* @return
*/
@GetMapping("appInfo")
@ApiOperation("appInfo接口")
@ApiImplicitParam(name = "userId", value = "用户Id", required = true)
@ApiResponse(code = 200, message = "响应成功")
String appInfo(@RequestParam("userId") Long userId);
@GetMapping("/addApp")
@ApiOperation("addApp接口")
@ApiImplicitParams({@ApiImplicitParam(name = "appId", value = "应用Id", required = true)
, @ApiImplicitParam(name = "appPwd", value = "应用密码", required = true)})
@ApiResponses({@ApiResponse(code = 200, message = "响应成功"),
@ApiResponse(code = 500, message = "系统错误")})
BaseResponse<String> addApp(@RequestParam("appId") String appId, @RequestParam("appPwd") String appPwd);
}
Swagger注解配置说明
@Api:用在请求的类上,表示对类的说明
tags="说明该类的作用,可以在UI界面上看到的注解"
value="该参数没什么意义,在UI界面上也看到,所以不需要配置"
@ApiOperation:用在请求的方法上,说明方法的用途、作用
value="说明方法的用途、作用"
notes="方法的备注说明"
@ApiImplicitParams:用在请求的方法上,表示一组参数说明
@ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面
name:参数名
value:参数的汉字说明、解释
required:参数是否必须传
paramType:参数放在哪个地方
· header --> 请求参数的获取:@RequestHeader
· query --> 请求参数的获取:@RequestParam
· path(用于restful接口)--> 请求参数的获取:@PathVariable
· body(不常用)
· form(不常用)
dataType:参数类型,默认String,其它值dataType="Integer"
defaultValue:参数的默认值
@ApiResponses:用在请求的方法上,表示一组响应
@ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息
code:数字,例如400
message:信息,例如"请求参数没填好"
response:抛出异常的类
@ApiModel:用于响应类上,表示一个返回响应数据的信息
(这种一般用在post创建的时候,使用@RequestBody这样的场景,
请求参数无法使用@ApiImplicitParam注解进行描述的时候)
@ApiModelProperty:用在属性上,描述响应类的属性
启动类新增配置
@EnableSwagger2Doc 注意不是@EnableSwagger2(Swagger原生启动注解)
访问地址:http://127.0.0.1:9090/swagger-ui.html
测试效果:
Swagger2Doc启动的原理
封装了SwaggerConfig类,底层自动加载SwaggerAutoConfiguration类。
4 Gateway整合Swagger
基于网关整合 Api文档
使用微服务网关统一管理整个微服务所有接口文档。
原理分析:网关把服务名称转换成ip获得接口文档。
创建模块mt-shop-basics-springcloud-gateway
mt-shop-parent
--------mt-shop-basics
----------------mt-shop-basics-springcloud-gateway
Maven依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
</dependencies>
注意:将mt-shop-parent中spring-boot-starter-web和spring-cloud-starter-openfeign依赖组件移到mt-shop-service-api的pom中,否则产生冲突报错。
application.yml
server:
port: 80
spring:
application:
name: mayikt-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
locator:
enabled: true
routes:
- id: mayikt-weixin
uri: lb://mayikt-weixin
predicates:
- Path=/mayikt-weixin/**
filters:
- SwaggerHeaderFilter
- StripPrefix=1
- id: mayikt-member
uri: lb://mayikt-member
predicates:
- Path=/mayikt-member/**
filters:
- SwaggerHeaderFilter
- StripPrefix=1
x-forwarded:
enabled: false
网关项目类
@Component
@Primary
@AllArgsConstructor
public class SwaggerProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v2/api-docs";
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>();
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))
.forEach(routeDefinition -> routeDefinition.getPredicates().stream()
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
.forEach(predicateDefinition -> resources.add(swaggerResource(routeDefinition.getId(),
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
.replace("/**", API_URI)))));
return resources;
}
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}
@Component
@Deprecated
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
private static final String HEADER_NAME = "X-Forwarded-Prefix";
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
if (!StringUtils.endsWithIgnoreCase(path, SwaggerProvider.API_URI)) {
return chain.filter(exchange);
}
String basePath = path.substring(0, path.lastIndexOf(SwaggerProvider.API_URI));
ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
return chain.filter(newExchange);
};
}
}
@RestController
@RequestMapping("/swagger-resources")
public class SwaggerHandler {
@Autowired(required = false)
private SecurityConfiguration securityConfiguration;
@Autowired(required = false)
private UiConfiguration uiConfiguration;
private final SwaggerResourcesProvider swaggerResources;
@Autowired
public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
this.swaggerResources = swaggerResources;
}
@GetMapping("/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
@SpringBootApplication
public class AppGateway {
/**
* Gateway的底层是基于webflux
* SpringMVC底层基于我们的servlet实现
* Spring-web封装了我们SpringMVC
* @param args
*/
public static void main(String[] args) {
SpringApplication.run(AppGateway.class);
}
}
测试效果:
Swagger网关重复发送服务别名解决
application.yml文件中加上配置spring.cloud.gateway.x-forwarded.enabled=false
5 企业级Maven私服仓库实现原理
什么场景用Maven私服?
在实际开发中,项目中可能会用到第三方的jar、内部通讯的服务接口都会打入到公司的私服中。
- 缓存企业级内部使用的jar包
- rpc远程调用发布微服务接口
实现原理:
6 基于docker安装Maven私服仓库
基于docker安装Maven私服
1 下载一个nexus3的镜像
docker pull sonatype/nexus3
2 将容器内部/var/nexus-data挂载到主机/root/nexus-data目录。
docker run -d -p 8081:8081 --name nexus -v /root/nexus-data:/var/nexus-data --restart=always sonatype/nexus3
Maven私服启动容器稍微比较慢,等待1分钟即可。
可以查看正在启动的日志 docker attach 容器的id
看到这个界面,说明我们的maven私服启动成功
访问http://ip:8081
maven私服仓库默认密码配置
进入到容器中
docker ps 查看dockerId
docker exec -it 8a1dcfb84f53(dockerId) bash
cat /nexus-data/admin.password
复制该密码 账号为admin,登录以后重新设置密码
7 向Maven仓库上传jar包项目
创建一个SpringBoot项目发布
创建私服仓库
创建仓库,点击Create repository,然后选择maven2(hosted)然后输入仓库名称(mayikt-release)。在version policy中选择这个仓库的类型,这里选择Release,在Deployment policy中选择Allow redeploy.
创建私服账号
点击左侧菜单栏的Users菜单,然后点击Create local user。这里创建了一个用户,账号密码都是:mayikt mayikt
本地maven路径/conf/settings.xml
<servers>
<server>
<id>mayikt</id>
<username>mayikt</username>
<password>mayikt</password>
</server>
</servers>
SpringBoot项目核心配置pom文件
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mayikt</groupId>
<artifactId>mayikt2020-springboot</artifactId>
<version>1.0-RELEASE</version>
<!--注意限定版本一定为RELEASE,因为上传的对应仓库的存储类型为RELEASE -->
<!--指定仓库地址 -->
<distributionManagement>
<repository>
<!--此名称要和.m2/settings.xml中设置的ID一致 -->
<id>mayikt</id>
<url>http://192.168.0.121:8081/repository/mayikt-release/</url>
</repository>
</distributionManagement>
<build>
<plugins>
<!--发布代码Jar插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
</plugin>
<!--发布源码插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Maven 执行命令 mvn deploy 上传jar包即可
注意版本号码一定要是为:release版本 ,否则上传jar报错
本地项目引入maven私服jar
<dependencies>
<dependency>
<groupId>com.mayikt</groupId>
<artifactId>mayikt2020-springboot</artifactId>
<version>2.0-RELEASE</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>mayikt</id>
<url>>http://192.168.0.121:8081/repository/mayikt-release/</url>
</repository>
</repositories>
如何判断文件是否发生改变
如何知道一个文件是否改变了呢?用比较文件hash值的方法,文件hash又叫文件签名,文件中哪怕一个bit位被改变了,文件hash就会不同。比较常用的文件hash算法有MD5和SHA-1。