Docker 安装
CentOS 版本官方安装文档:https://docs.docker.com/engine/install/centos/
卸载旧版
$ sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine
设置 yum 库 ```shell $ sudo yum install -y yum-utils
官方源,比较慢
$ sudo yum-config-manager \ —add-repo \ https://download.docker.com/linux/centos/docker-ce.repo
建议使用阿里源
$ sudo yum-config-manager \ —add-repo \ http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
- 安装```shell$ sudo yum install docker-ce docker-ce-cli containerd.io
启动
$ sudo systemctl start docker
设置开机自启
$ systemctl enable docker
测试
$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZE$ docker -vDocker version 19.03.12, build 48a66213fe
配置镜像阿里云加速


sudo mkdir -p /etc/dockersudo tee /etc/docker/daemon.json <<-'EOF'{"registry-mirrors": ["https://bcozo2zh.mirror.aliyuncs.com"]}EOFsudo systemctl daemon-reloadsudo systemctl restart docker
Docker 安装 Mysql
拉取 Mysql 镜像
# 注意只能用 5.7 版本,其他版本的操作不一样docker pull mysql:5.7
运行容器,
\代表换行docker run -p 3306:3306 --name mysql \-v /www/docker/mysql/log:/var/log/mysql \-v /www/docker/mysql/data:/var/lib/mysql \-v /www/docker/mysql/conf:/etc/mysql \-e MYSQL_ROOT_PASSWORD=123 \-d mysql
配置字符编码
[mysqld]default-character-set = utf8# character_set_server = utf8
Docker 安装 Redis
拉取 Redis 镜像
docker pull redis
运行容器
docker run -p 8883:6379 --name redis \-v /www/docker/redis/data:/data \-v /www/docker/redis/conf/redis.conf:/etc/redis/redis.conf \-d redis redis-server /etc/redis/redis.conf
使用redis 镜像执行 redis-cli 命令连接
docker exec -it redis redis-cli
配置持久化
在 redis/conf/redis.conf 中加入以下内容,然后重启容器
appendonly yes
想知道 redis 的更多配置可以参照官方文档
配置 Maven 与 Java
$ mvn -vApache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)Maven home: /Library/apache-maven-3.6.3Java version: 1.8.0_221, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jreDefault locale: zh_CN, platform encoding: UTF-8OS name: "mac os x", version: "10.14.4", arch: "x86_64", family: "mac"$ java -vUnrecognized option: -vError: Could not create the Java Virtual Machine.Error: A fatal exception has occurred. Program will exit.caideMacBook-Pro:NewServer cai$
配置 IDEA 插件
lombok 插件:自动生成 getter 和 setter 方法
mybatisx 插件:从 xxxMapper.xml 快速跳转到对应接口
配置 git
$ git config --global user.name "Cai_Programmer"$ git config --global user.email "1050440470@qq.com"$ ssh-keygen -t rsa -C "1050440470@qq.com"Generating public/private rsa key pair.Enter file in which to save the key (/root/.ssh/id_rsa):Enter passphrase (empty for no passphrase):Enter same passphrase again:Your identification has been saved in /root/.ssh/id_rsa.Your public key has been saved in /root/.ssh/id_rsa.pub.The key fingerprint is:SHA256:k/bvYXYUbvV3tuilP4LTMKdO3WnWQY76fS6luzm5pxY 1050440470@qq.comThe key's randomart image is:+---[RSA 2048]----+| || || ...|| . .+o.|| S .+o=|| . o oo+E.O|| .o@o.%.|| .Bo=%oo|| .o+=O%+|+----[SHA256]-----+[root@iZwz91sneajs2ji49vo4wxZ docker]# cat /root/.ssh/id_rsa.pubssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCnP4Y9fKMNQF7pm7OqfL/hh5wYcmKqQl2op2QpLkKxvYdRmxyziDC43fXBQfbovErGSFgGrvyJkMH+sZfwntv7GbmOI2xLMJVYDGlpKBZEFRqvNTV8HrXrwTBS0w7+COm3jndwZcmzwccWwb0E81wVBTMFmRZ/BkS+BXtN92KqJnJnzI0goCnjG2PAyILSAqWx84MmOX5zAGRgy2TDHgUZ2Fjod3QmiWrR2nHzk/6lrAMXWjjtSEhIa+Odvi9IM/qSp7NKmW7gEnEH3AmOe+AZBJvq/vzIDXrrDSJw+Puc00FZyOzyhzfmO2gi3yAkIKwJ4aj11w8BkL6wHNGnv7bP 1050440470@qq.com
登陆码云 -> 进入设置 -> 安全设置 -> SSH 公钥 -> 把生成的密钥配置到码云
使用 ssh -T git@gitee.com 测试配置是否成功
$ ssh -T git@gitee.comThe authenticity of host 'gitee.com (212.64.62.174)' can't be established.ECDSA key fingerprint is SHA256:FQGC9*****bVr17bmjey0Wc.ECDSA key fingerprint is MD5:27:e5:d******d:1f:c1:47:a3:54:b1.Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added 'gitee.com,212.64.62.174' (ECDSA) to the list of known hosts.Hi 蔡俊伟! You've successfully authenticated, but GITEE.COM does not provide shell access.
使用人人开源生成器
使用 nacos 做注册中心
github 地址:https://github.com/alibaba/nacos 下载地址:https://github.com/alibaba/nacos/releases
# 解压$ unzip nacos-server-1.0.0.zip$ cd nacos/bin# 启动, 默认访问路径为 localhost:8848/nacos$ startup.sh -m standalone
- 在项目公共 pom.xml 文件中添加依赖
```xml
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery  
- 在 application.yml 中添加配置```sqlspring:cloud:nacos:discovery:# 注册中心地址server-addr: 127.0.0.1:8848application:# 服务名称name: gulimall-coupon
先启动 spring boot 服务,访问 localhost:8848/nacos,默认登陆账号和密码都是 nacos,在注册中心可以看到服务注册进来了
Spring Cloud Feign 远程调用
引入依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>
开启 feign 功能
src/main/java/com/atguigu/gulimall/member/GulimallMemberApplication.java
// 扫描 feign 包@EnableFeignClients(basePackages = "com.atguigu.gulimall.member.feign")@EnableDiscoveryClient@SpringBootApplicationpublic class GulimallMemberApplication {public static void main(String[] args) {SpringApplication.run(GulimallMemberApplication.class, args);}}
src/main/java/com/atguigu/gulimall/member/feign/CouponFeignService.java
// 服务名称@FeignClient("gulimall-coupon")public interface CouponFeignService {// 请求 url@RequestMapping("/coupon/coupon/member/list")public R membercoupons();}
- 调用 feign
src/main/java/com/atguigu/gulimall/member/controller/MemberController.java
 
@Autowiredprivate MemberService memberService;@AutowiredCouponFeignService couponFeignService;@RequestMapping("/coupons")public R test(){MemberEntity memberEntity = new MemberEntity();memberEntity.setNickname("张三");R memberCoupons = couponFeignService.memberCoupons();return R.ok().put("member",memberEntity).put("coupons",memberCoupons.get("coupons"));}
使用 nacos 做配置中心
使用 nacos 做配置中心,统一管理配置,修改配置不需要打包重新部署项目。
一、基本使用方法
- 在公共模块中引入依赖
gulimall-common/pom.xml
 
<!-- 配置中心来做配置管理 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency>
- 创建一个 bootstrap.properties,声明注册中心地址
gulimall-coupon/src/main/resources/bootstrap.properties
 
spring.application.name=gulimall-couponspring.cloud.nacos.config.server-addr=127.0.0.1:8848
- 在配置中心增加数据集(Data Id)
 
默认命名规则:应用名.properties
- 在控制器中获取配置信息
 
Spring 会优先使用配置中心的配置@RefreshScopre:动态获取配置,每次请求都从配置中心获取配置信息@Value:获取配置
二、命名空间与分组
1. 命名空间
默认命名空间为 public,命名空间的作用类似文件夹,起到配置分离的作用。
常规使用方法:
- 生产与开发环境各使用一个命名空间,发布的时候需要修改命名空间 id
 - 每个微服务使用一个命名空间,使用分组隔离生产与开发环境配置,发布的时候需要修改分组名称
新建一个优惠券系统命名空间
 

在该命名空间下新建默认配置文件

在 bootstrap.properties 中声明命名空间,注意配置的是命名空间 id,不是命名空间名称 gulimall-coupon/src/main/resources/bootstrap.properties
spring.cloud.nacos.config.namespace=59927bf3-18a3-4c42-816a-bcdc528c9a5f
这样就会使用 coupon 命名空间下的 gulimall-coupon.properties 了
2. 配置集
3. 配置集 ID
4. 配置分组
功能类似命名空间,我们可以为每个配置设置一个分组
常规使用方法:新建一套分组为 dev 的配置文件,另加一套分组为 prod 的配置文件,发布的时候只要在 boostrap.properties 中修改分组名称就 ok
新建两个默认配置文件,分组名称分别为 dev 和 prod

在 bootstrap.properties 中声明分组名称
spring.cloud.nacos.config.group=prod
三、加载多个配置集
一个微服务的所有配置如果都写在同一个配置文件中会比较难维护,所以一般会把一个微服务的配置拆分到多个配置文件中,使用 nacos 也是可以加载多个配置文件的
新建多个配置文件,datasource.yml、mybatis.yml

在 bootstrap.properties 中加入配置 gulimall-coupon/src/main/resources/bootstrap.properties
spring.application.name=gulimall-couponspring.cloud.nacos.config.server-addr=127.0.0.1:8848# 配置命名空间spring.cloud.nacos.config.namespace=59927bf3-18a3-4c42-816a-bcdc528c9a5f# 配置分组spring.cloud.nacos.config.group=dev# 加载多个配置文件# 配置 data-idspring.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# 同上,加载第二个配置文件spring.cloud.nacos.config.ext-config[1].data-id=mybatis.ymlspring.cloud.nacos.config.ext-config[1].group=devspring.cloud.nacos.config.ext-config[1].refresh=true
这样就可以加载多个配置文件了,项目配置中只需要一个 boostrap.properties,其它配置文件都迁移到配置中心
SpringCloud Gateway 网关
官方文档:https://docs.spring.io/spring-cloud-gateway/docs/2.2.7.RELEASE/reference/html
将网关注册到注册中心,在启动类中增加注解 @EnableDiscoveryClient
@EnableDiscoveryClient@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})public class GulimallGatewayApplication {public static void main(String[] args) {SpringApplication.run(GulimallGatewayApplication.class, args);}}
新建 bootstrap.properties,加入注册中心与配置中心地址
spring.application.name=gulimall-gatewayspring.cloud.nacos.discovery.server-addr=127.0.0.1:8848spring.cloud.nacos.config.server-addr=127.0.0.1:8848server.port=88
新建 application.yml,测试网关
当请求参数存在 url,且值为 baidu 时,会跳转到 https://www.baidu.com
当请求参数存在 url,且值为 qq 时,会跳转到 https://www.qq.com
spring:cloud:gateway:routes:- id: test_routeuri: https://www.baidu.compredicates:- Query=url,baidu- id: qq_routeuri: https://www.qq.compredicates:- Query=url,qq
后台管理网关配置:
gulimall-gateway/src/main/resources/application.yml
spring:cloud:gateway:routes:- id: product_route# lb 代表负载均衡,填写微服务名称uri: lb://gulimall-productpredicates:# 填写匹配规则,根据路径配置- Path=/api/product/**filters:# 配置路径重写# 例如:http://localhost:88/api/product/category/list/tree# 重写后路径:http://server/product/category/list/tree- RewritePath=/api/(?<segment>.*),/$\{segment}- id: admin_routeuri: lb://renren-fastpredicates:- Path=/api/**filters:- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}
在网关中配置允许跨域:
gulimall-gateway/src/main/java/com/atguigu/gulimall/gateway/config/GulimallCorsConfiguration.java
@Configurationpublic class GulimallCorsConfiguration {@Beanpublic CorsWebFilter corsWebFilter(){UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();CorsConfiguration corsConfiguration = new CorsConfiguration();// 配置跨域corsConfiguration.addAllowedHeader("*");corsConfiguration.addAllowedMethod("*");corsConfiguration.addAllowedOrigin("*");corsConfiguration.setAllowCredentials(true);// 任意路径source.registerCorsConfiguration("/**",corsConfiguration);return new CorsWebFilter(source);}}
阿里云OSS对象存储
使用服务端签名直传,文件无需经过应用服务器,直接传给 oss
官方文档:https://help.aliyun.com/document_detail/31926.html?spm=a2c4g.11186623.6.1737.7d5e5a025RchiN
新建 gulimall-third-party 模块,用于封装第三方服务
引入阿里oss starter
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alicloud-oss</artifactId></dependency>
配置注册中心与 oss 账号信息 ```yaml spring: cloud: nacos:
discovery:server-addr: 127.0.0.1:8848
alicloud:
access-key: LTAIQkDnEUVliI8Zsecret-key: 67Y2amus4dFKiX5kBWuXP4Jy8xMLvBoss:endpoint: oss-accelerate.aliyuncs.combucket: iep-server
application: name: gulimall-third-party
server: port: 30000
- 新建控制器 OssController,用于获取签名```java@RestControllerpublic class OssController {@AutowiredOSS ossClient;@Value("${spring.cloud.alicloud.oss.endpoint}")private String endpoint;@Value("${spring.cloud.alicloud.oss.bucket}")private String bucket;@Value("${spring.cloud.alicloud.access-key}")private String accessId;@RequestMapping("/oss/policy")public R policy() {String host = "https://" + bucket + "." + endpoint;// 用户上传文件时指定的前缀。String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());String dir = format + "/";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(StandardCharsets.UTF_8);String encodedPolicy = BinaryUtil.toBase64String(binaryData);String postSignature = ossClient.calculatePostSignature(postPolicy);Map<String, String> respMap = new LinkedHashMap<>();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));return R.ok().put("data",respMap);}}
JSR-303数据校验
作者:谁在烽烟彼岸 https://www.jianshu.com/p/554533f88370
JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。
JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。
Bean Validation 中内置的 constraint:
使用方法:
给Bean添加校验注解:
javax.validation.constraints,并定义自己的message提示@NotNull(groups = {AddGroup.class})@Min(value = 0, message = "排序必须大于等于0")private Integer sort;
控制器接收前端参数时增加
@Validated注解,代表开启参数校验功能,如果不增加此注解则不会开启校验@RequestMapping("/save")public R save(@Validated() @RequestBody BrandEntity brand){brandService.save(brand);return R.ok();}
分组校验功能,用于多场景的复杂校验:
- 定义分组,只需要声明接口 ```java public interface UpdateGroup {}
 
public interface AddGroup {}
- 在校验注解中传入 groups 属性,给校验注解标注什么情况需要进行校验```java@NotBlank(message = "品牌名必须提交", groups = {AddGroup.class, UpdateGroup.class})
- 在控制器中的 Validated 注解中声明分组,默认没有指定分组的校验注解 @NotBlank,在分组校验情况@Validated({AddGroup.class}) 下不生效,只会在 @Validated  生效 
@RequestMapping("/save")public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand){brandService.save(brand);return R.ok();}
 
自定义校验注解:
创建注解
@Documented@Constraint(validatedBy = { ListValueConstraintValidator.class })@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })@Retention(RUNTIME)public @interface ListValue {String message() default "{com.atguigu.common.valid.ListValue.message}";Class<?>[] groups() default { };Class<? extends Payload>[] payload() default { };int[] vals() default { };}
创建校验器
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {private Set<Integer> set = new HashSet<>();@Overridepublic void initialize(ListValue constraintAnnotation) {int[] vals = constraintAnnotation.vals();for (int val : vals) {set.add(val);}}/*** @param value 需要校验的值* @param context* @return*/@Overridepublic boolean isValid(Integer value, ConstraintValidatorContext context) {return set.contains(value);}}
全局异常处理
集中处理控制器中抛出的异常,例如处理 JSR-303数据校验异常
com/atguigu/gulimall/product/exception/GulimallExceptionControllerAdvice.java/*** 集中处理所有异常*/@Slf4j@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")public class GulimallExceptionControllerAdvice {@ExceptionHandler(value = MethodArgumentNotValidException.class)public R handleVaildException(MethodArgumentNotValidException e) {log.error("数据校验出现问题{},异常类型:{}", e.getMessage(), e.getClass());BindingResult bindingResult = e.getBindingResult();Map<String, String> errorMap = new HashMap<>();bindingResult.getFieldErrors().forEach((fieldError) -> {errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());});return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(), BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data", errorMap);}}
SKU 与SPU
电商系统中的sku与spu
