Nacos
Nacos 文档地址: https://nacos.io/zh-cn/docs/quick-start.html
sh startup.sh -m standalone 以非集群的方式启动 127.0.0.1:8848/ 账号: nacos nacos
使用nacos作为注册中心:将微服务注册到 nacos 中
将微服务注册到nacos,各个服务之间的相互调用需要使用注册中心
1. 首先,修改 pom.xml 文件,引入 Nacos Discovery Starter
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacosdiscovery</artifactId>
</dependency>
2. 在应用的 application.properties 中配置 Nacos Server 地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.application.name=service-provider 为每个服务创建名字是必须的
server.port=8000
3.在主程序中使用@EnableDiscoveryClient 开启服务注册发现功能
使用nacos作为配置中心:
统一管理各微服务配置。在配置中心发布配置,项目在运行阶段可动态读取配置,无需重启项目即可做到修改项目配置
1、pom.xml 引入 NacosConfigStarter。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2、新建一个bootstrap.properties 配置文件中配置 NacosConfig 元数据 该配置文件在springboot中会比applicaion.yml的优先级高主要配置应用名和配置中心地址
spring.application.name=nacos-config-example
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
3、需要给配置中心默认添加一个叫 数据集(Data Id)gulimall-coupon.properties。默认规则,应用名.properties
4、给应用名.properties 添加任何配置
* 5、动态获取配置。
@RefreshScope:动态获取并刷新配置 加在类上controller
@Value("${配置项的名}"):获取到配置。 在controller上获取配置
//如果配置中心和当前应用的配置文件中都配置了相同的项,优先使用配置中心的配置。
扩展
1)、命名空间:配置隔离;nacos默认:public(保留空间);默认新增的所有配置都在public空间。
开发,测试,生产:利用命名空间来做环境隔离。
注意:在bootstrap.properties;配置上,需要使用哪个命名空间下的配置,
spring.cloud.nacos.config.namespace=9de62e44-cd2a-4a82-bf5c-95878bd5e871
这个ID是命名空间id
2、每一个微服务之间互相隔离配置,每一个微服务都创建自己的命名空间,只加载自己命名空间下的所有配置
项目中的使用:每个微服务创建自己的命名空间,使用配置分组区分环境,dev,test,prod
每一类配置分文件放:比如:datasource.yml 、mybatisPuls.yum 、 other.yum
加载每一个配置文件:
spring.cloud.nacos.config.ext-config[0].data-id=datasource.yml
spring.cloud.nacos.config.ext-config[0].group=dev
spring.cloud.nacos.config.ext-config[0].refresh=true 开启动态刷新,修改配置也会读取到,而不是只在启动时读取
3)、@Value,@ConfigurationProperties .... 以前SpringBoot任何方法从配置文件中获取值,都能使用。配置中心有的优先使用配置中心中的,
mybatis-plus
文档地址 https://baomidou.com/
配置全局逻辑删除规则
logic-delete-value: 1
logic-not-delete-value: 0
在实体类中加上逻辑删除注解
@TableLogic
private Integer deleted;
使用JSR303规范校验传输中的实体类
使用说明
常用的注解有 @Null、 @NotNull 、@Min等等
下面是一个例子,在实体类中的某个属性上加上校验注解,控制类在接受该实体类时会核对该校验,校验失败则会返回message消息.在实体类后紧跟着一个BindingResult就可以拿到校验结果,自己在返回校验信息。
//控制类代码:save
@RequestMapping("/save")
public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand,, BindingResult bandingResult){
brandService.save(brand);
return R.ok();
}
//控制类代码:update
@RequestMapping("/update")
public R update(@Validated({UpdateGroup.class})@RequestBody BrandEntity brand){
brandService.updateById(brand);
return R.ok();
}
//实体类代码
@Pattern(regexp = "^[a-zA-Z]$",message = "必须为一个a-zA-Z的单个字母",groups = {AddGroup.class, UpdateGroup.class})
@NotBlank(message = "检索首字母不能为空",groups = {AddGroup.class})
private String firstLetter;
对同一个实体类可以对不同场景使用不同检验组。这需要使用@Validated注解指定检验组,检验组中的类是一个纯接口
//纯接口,只是为了给校验分类。添加数据指定该组规则
public interface AddGroup {
}
//纯接口,只是为了给校验分类。更新数据指定该组规则
public interface UpdateGroup {
}
自定义校验注解
虽然上面的场景已经满足大多数要求了,但是你一定有自己的规则,想定义自己的规则也很简单,我们先来点一个注解的源码进去看看它是怎么写的
@Documented
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
public @interface NotBlank {
String message() default "{javax.validation.constraints.NotBlank.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
/**
* Defines several {@code @NotBlank} constraints on the same element.
*
* @see NotBlank
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
public @interface List {
NotBlank[] value();
}
}
可以看到自定义校验注解也要有上面的元注解信息,其中
@Constraint(validatedBy = { })用来指定该检验注解使用什么校验器,这里用空的是源码在初始化中去定义了。
message用于指定校验出错返回的信息去哪里取,我们可以在resources下创建一个ValidationMessages.properties文件用来存放该信息
groups用来指定该注解用在哪个分组
payload用来定义写负载信息
我们安照上面的源码照葫芦画瓢,自己定义一个用来校验该值是否是我们自己指定的整数
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class }) //指定使用的自定义校验器
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
String message() default "{com.myself.common.valid.ListValue.message}"; // 指定错误提示的消息
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int[] vals() default { };//用来存放指定的整数
}
创建注解时我们发现这个的一些包没有导入,我们要在项目的Pom文件中导入javax校验相关的包
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
在resources下创建一个ValidationMessages.properties文件用来存放该信息,也可以不创建,我们直接在message中写校验出错的信息
com.atguigu.common.valid.ListValue.message="值必须为0或1"
最后我们还差一个校验器。检验器需要事项ConstraintValidator接口,第一个泛型是我们的校验注解,第二个是校验的类型
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private Set<Integer> set = new HashSet<>();
//初始化方法
@Override
public void initialize(ListValue constraintAnnotation) {
//初始化时我们把规则传入的数值放入自己定义的set集合中
int[] vals = constraintAnnotation.vals();
for (int val : vals) {
set.add(val);
}
}
/**
*判断是否校验成功
* @param value 需要校验的值
* @param context
* @return
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
//这里去判断需要校验的值是不是在我们的规则中
return set.contains(value);
}
}
最后我们使用这个自定义的校验注解
/**
* 显示状态[0-不显示;1-显示] 添加和修改时都必须校验该值是否为0或1
*/
@ListValue(vals = {0,1},groups = {AddGroup.class, UpdateGroup.class})
private Integer showStatus;
使用JsonInclud注解
1.在类上加入@JsonInclude(value = JsonInclude.Include.NON_NULL),表明该类为NULL的字段不参加序列化!
2.将该标记放在属性上,如果该属性为NULL则不参与序列化 !
3.如果放在类上边,那对这个类的全部属性起作用 !
使用场景:如果数据库中查出的数据为空,正常会返回一个null,或者默认值如[] 0,想要数据为空则不返回该字段就可使用
//属性为null时不序列化
@JsonInclude(value = JsonInclude.Include.NON_NULL)
private List<CategoryEntity> child;
Include.Include.ALWAYS -------------->默认,总是序列化
Include.NON_DEFAULT ------------->属性为默认值不序列化
Include.NON_EMPTY ---------------->属性为 空(“”) 或者为 NULL 都不序列化
Include.NON_NULL ------------------>属性为NULL 不序列化
统一异常返回
@Slf4j
@RestControllerAdvice
public class GuliMaillExceptionControllerAdvice {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handlerVaildException(MethodArgumentNotValidException e){
log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
// 拿到数据校验异常信息类
BindingResult bindingResult = e.getBindingResult();
Map<String,String> errorMap = new HashMap<>();
// 将所有校验出错的字段全部放到map中。
bindingResult.getFieldErrors().forEach(fieldError -> errorMap.put(fieldError.getField(),fieldError.getDefaultMessage()));
// 统一返回所有出错的字段和提示信息。异常码,和异常类型定义在BizCodeEnume枚举类中
return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",errorMap);
}
@ExceptionHandler(value = Throwable.class)
public R handlerException(Throwable t){
return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
}
}