微服务、注册中心、配置中心、远程调用、Feign、网关
Nacos使用步骤:
导入相关依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
添加配置文件
注册中心配置:application.yml
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
application:
name: productName
配置中心配置:bootstrap.properties
# 改名字,对应nacos里的配置文件名
spring.application.name=gulimall-coupon
# nacos 地址
spring.cloud.nacos.config.server-addr=localhost:8848
# 可以选择对应的命名空间,写上对应环境的命名空间ID
spring.cloud.nacos.config.namespace=79d5ebaa-bd56-446e-97e1-ba06cd7e51fa
# 更改配置分组
spring.cloud.nacos.config.group=dev
# 拉取配置文件
spring.cloud.nacos.config.extension-configs[0].data-id=datasource.yml
spring.cloud.nacos.config.extension-configs[0].group=dev
spring.cloud.nacos.config.extension-configs[0].refresh=true
启动类添加相关注解开启功能
@EnableDiscoveryClient
Feign的使用步骤:
导入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
启动类添加注解:指定要扫描的包,也就是要进行远程调用的包
@EnableFeignClients(basePackages="包名")
@EnableFeignClients("com.ljn.gulimall.product.feign")
编写远程调用类,并指定远程调用的服务名和路径
// 要调用的服务
@FeignClient("gulimall-coupon")
public interface CouponFeignService {
//找到gulimall-coupon服务,给/coupon/spubounds/save发送请求。
@PostMapping("/coupon/spubounds/save")
R saveSpuBounds(@RequestBody SpuBoundTo spuBoundTo);
}
在需要远程调用的方法中调用该方法
@Autowired
CouponFeignService couponFeignService;
R r = couponFeignService.saveSpuBounds(spuBoundTo);
Gateway使用步骤:
创建Gateway
服务
导入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
将服务注册到注册中心
spring.application.name=gulimall-gateway
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
配置路由,请求转发
spring:
cloud:
gateway:
routes:
- id: product_route
uri: lb://gulimall-product # 要路由的服务 lb 代表负载均衡
predicates: # 什么情况下路由给它
- Path=/api/product/**
filters: # 过滤
# 把/api/* 去掉,剩下的留下来
- RewritePath=/api/(?>.*),/$\{segment}
其他统一处理(如:统一跨域处理)
@Configuration // gateway
public class GulimallCorsConfiguration {
@Bean // 添加过滤器
public CorsWebFilter corsWebFilter() {
// 基于url跨域,选择reactive包下的
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 跨域配置信息
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 允许跨域的头
corsConfiguration.addAllowedHeader("*");
// 允许跨域的请求方式
corsConfiguration.addAllowedMethod("*");
// 允许跨域的请求来源
corsConfiguration.addAllowedOrigin("*");
// 是否允许携带cookie跨域
corsConfiguration.setAllowCredentials(true);
// 任意url都要进行跨域配置
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsWebFilter(source);
}
}
SpringBoot2.0、SpringCloud、Mybatis-Plus、Vue组件化、阿里云对象存储
在对后台管理系统的开发中:
SpringCloud Alibaba
引入依赖管理:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.1.0.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
是阿里云提供的海量、安全、低成本、高可靠的云存储服务。
图片上传方式:
择服务端签名后上传
的方式实现图片的存储功能。
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alicloud-ossartifactId>
dependency>
application.ym
l中配置key
,secret
和endpoint
相关信息
spring:
cloud:
alicloud:
secret-key: XXXXXXXXXXXXXXXXXX
access-key: XXXXXXXXXXXXXXXXXX
oss:
endpoint: oss-cn-hangzhou.aliyuncs.com
bucket: XXXXXXXXXXX
按照OSS 的java SDK 编写controller
package com.ljn.gulimall.thirdparty.Controller;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.ljn.common.utils.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
@RestController
public class OssController {
@Autowired
OSS ossClient;
@Value("${spring.cloud.alicloud.oss.endpoint}")
String endpoint;
@Value("${spring.cloud.alicloud.oss.bucket}")
String bucket;
@Value("${spring.cloud.alicloud.access-key}")
String accessId;
@Value("${spring.cloud.alicloud.secret-key}")
String accessKey;
@RequestMapping("/oss/policy")
public R policy() {
// host的格式为 bucketname.endpoint
String host = "https://" + bucket + "." + endpoint;
String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
// 用户上传文件时指定的前缀。
String dir = format;
Map<String, String> respMap = null;
try {
long expireTime = 30;
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
Date expiration = new Date(expireEndTime);
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
byte[] binaryData = postPolicy.getBytes("utf-8");
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = ossClient.calculatePostSignature(postPolicy);
respMap = new LinkedHashMap<String, String>();
respMap.put("accessid", accessId);
respMap.put("policy", encodedPolicy);
respMap.put("signature", postSignature);
respMap.put("dir", dir);
respMap.put("host", host);
respMap.put("expire", String.valueOf(expireEndTime / 1000));
} catch (Exception e) {
// Assert.fail(e.getMessage());
System.out.println(e.getMessage());
} finally {
ossClient.shutdown();
}
return R.ok().put("data",respMap);
}
}
• Linux、Docker、MySQL、Redis、逆向工程
• 数据校验JSR303、全局异常处理、全局统一返回、全局跨域处理
• 枚举状态、业务状态码、VO与TO与PO划分、逻辑删除
• Lombok:@Data、@Slf4j
对于数据的新增与修改使用JSR303进行数据校验,配置全局的异常处理、统一返回和跨域处理,将项目中固定的状态使用枚举进行编写。项目中还使用了Mybatis-Plus的逻辑删除功能。
新版本springboot需要添加validation
启动器
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-validationartifactId>
<version>2.3.7.RELEASEversion>
dependency>
给Bean添加校验注解参考:javax.validation.constraints
包下,并定义自己的message提示,如:
@NotBlank(message = "品牌名不能为空")
private String name;
@NotEmpty
@URL(message = "logo地址必须合法")
private String logo;
@Pattern(regexp = "/^[a-zA-Z]$/", message = "检索首字母必须是一个字母")
private String firstLetter;
@Min(value = 0,message = "排序必须大于等于0")
private Integer sort;
controller
的方法中添加@Valid
注解开启校验,如:
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
分组校验(多场景的复杂校验)
@Validated
注解指定分组groups
,指定什么情况下才需要进行校验如:指定在更新和添加的时候,都需要进行校验。新增时不需要带id,修改时必须带id
@NotNull(message = "修改必须定制品牌id", groups = {UpdateGroup.class})
@Null(message = "新增不能指定id", groups = {AddGroup.class})
@TableId
private Long brandId;
@RequestMapping("/save")
public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand) {
brandService.save(brand);
return R.ok();
}
common
创建文件ValidationMessages.properties
配置文件ConstraintValidator
配置全局的逻辑删除规则(可省略)
mybatis-plus:
global-config:
db-config:
id-type: auto
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
给Bean加上逻辑删除注解@TableLogic
/**配置特定的逻辑删除状态
* 是否显示[0-不显示,1显示]
*/
@TableLogic(value = "1",delval = "0")
private Integer showStatus;
@ControllerAdvice。
@ControllerAdvice
,通过basePackages
能够说明处理哪些路径下的异常。@ExceptionHandler
标注方法可以处理的异常。com.ljn.gulimall.product.exception.GuliMallExceptionControllerAdvice
,对数据校验做统一的异常处理:
@Slf4j
@RestControllerAdvice(basePackages = "com.ljn.gulimall.product.controller")
public class GuliMallExceptionControllerAdvice {
// 数据校验异常
@ExceptionHandler(value = Exception.class)
public R handleValidException(MethodArgumentNotValidException exception) {
Map<String, String> map = new HashMap<>();
// 1. 获取数据校验的错误结果
BindingResult bindingResult = exception.getBindingResult();
// 2. 遍历获取结果
bindingResult.getFieldErrors().forEach(fieldError -> {
String message = fieldError.getDefaultMessage();
String field = fieldError.getField();
map.put(field, message);
});
log.error("数据校验出现问题{},异常类型{}", exception.getMessage(), exception.getClass());
return R.error(400, "数据校验出现问题").put("data", map);
}
// 默认异常
@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable) {
log.error("未知异常{},异常类型{}", throwable.getMessage(), throwable.getClass());
return R.error(400, "数据校验出现问题");
}
}