目录
创建父项目
当你在创建项目的时候,将打包方式选择了Jar包的方式,那么项目就不会自动导入spring-boot-starter-web依赖,需要自己手动导入。
选择完之后,点击Next下一步。
创建完父项目后,千万不要急着去创建子项目,不然后面启动项目的时候会出错(需要重启IDEA解决)。 创建完父项目后,要先对父项目整理一下。下面的图片中父项目中已经有了子项目(这是一个错误行为),读者不用去创建子项目,一步一步往下操作即可。
配置父项目
整理父项目的目录结构
以下是删除过后的目录结构
修改父项目的POM文件
父项目的POM文件中的build标签中的内容可以全部删除
以下是删除过后的父项目的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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.china</groupId>
<artifactId>test-mall</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>test-mall</name>
<description>test-mall</description>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.7.6</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
1. 修改父项目中的打包方式
2. 给父项目配置所有的子项目
3. 将父项目中的dependencies标签中的依赖移入dependencyManagement标签中的dependencies标签里(为什么要做这一步:最外面的dependencies标签中的依赖会被所有子模块继承,但是现实情况并不是所有子模块都需要继承父项目中的依赖。)
4. 所有移入dependencyManagement标签中的dependencies标签里的springboot起步依赖都必须要指定版本号(为什么一定要指定版本号: 里面的dependencies标签中的依赖只有当子类在POM文件中引入依赖时才会被继承,并且子类引入依赖的时候无需指定版本号,由父项目统一指定依赖的版本号,防止出现版本不一致的问题)
以下是父项目修改过后的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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.china</groupId>
<artifactId>test-mall</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging> <!--1.父项目的打包方式必须是pom,否则报错-->
<name>test-mall</name>
<description>test-mall</description>
<modules>
<module>mall-product</module> <!--2.指定子模块(有哪些子模块,都需要配置进来,目前我就写了一个)-->
</modules>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.7.6</spring-boot.version>
</properties>
<dependencies>
<!--这个标签中的依赖会被所有子模块继承(但是现实情况并不是所有子模块都需要继承父项目中的依赖)-->
</dependencies>
<dependencyManagement>
<dependencies><!--这个标签中的依赖只有当子类在POM文件中引入依赖时才会被继承,并且子类引入依赖的时候无需
指定版本号,由父项目统一指定依赖的版本号,防止出现版本不一致的问题-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
使用多模块开发项目
创建子项目
创建子项目的步骤和上面创建父项目test-mall一致
以下是创建成功后的目录结构
配置子项目pom文件
1. 配置父项目的坐标信息。
2. 导入项目中需要的依赖,不需要指定版本号,因为父项目已经确定版本号,子项目只需要导入即可。
3. 删除bulid标签和dependencyManagement标签,保持pom文件内容的整洁。
以下是子项目修改后的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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.china</groupId>
<artifactId>mall-product</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>mall-product</name>
<description>mall-product</description>
<!--配置父项目的坐标信息-->
<parent>
<groupId>cn.china</groupId>
<artifactId>test-mall</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.7.6</spring-boot.version>
</properties>
<dependencies>
<!--导入该项目中需要的依赖,不需要写版本号-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
启动子项目
出现以下界面说明多模块项目创建成功。
将子项目放入注册中心
如果希望注册中心是Nacos的小伙伴,可以阅读另一篇文章SpringCloud+Nacos
下载consul
启动consul
将下载得到的consul压缩包,解压到本地文件夹,进入consul文件夹,在地址栏使用cmd命令,打开命令行窗口。
输入consul agent -dev然后回车,就可以启动consul了。
在浏览器中输入http://localhost:8500就可以进入consul的可视化界面。
给父项目中注入依赖
在父项目的dependencyManagement标签中的dependencies标签里注入依赖(该依赖的作用是当子项目导入SpringCloud相关的依赖时,不需要指定版本,这个依赖会帮我们下载最合适的版本):
<!-- 指定 Spring Cloud 版本 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
给子项目中注入依赖
将子项目注册到consul的注册中心
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-consul-discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
在子项目中写配置文件
在子项目中的resource文件夹下创建application.yml文件,配置consul的相关信息,application中的name用来指定服务的名字,用于consul区分是否为相同服务,一般用项目的名字作为name值。当需要配置服务集群时,会配置相同name值的服务。
server:
port: 8081
spring:
application:
name: mall-product
cloud:
consul:
host: localhost
port: 8500
discovery:
heartbeat:
enabled: true
在子项目的启动类上加注解
在启动类的上方加@EnableDiscoveryClient注解。
启动子项目后,子项目就会被进入到注册中心中。
使用OpenFeign完成服务之间的通信
每一个子项目就是一个服务,任何一个服务中难免会需要调用另外一个服务的接口,如果服务之间不能相互调用的话,就需要再写一个一模一样的接口,这就会导致代码重复。实现服务之间的通信,就需要用到OpenFeign组件。
导入OpenFeign相关的依赖
导入依赖的时候,哪个服务需要调用另外一个服务的接口,就在那个服务中导入OpenFeign相关的依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
使用OpenFeign
第一步,我们需要再创建一个子项目给子项目mall-product调用。
在父项目的pom文件中子模块mall-order配置进去
在子模块mall-order中指定父项目的坐标
第二步,我们需要在这个子项目mall-order中写一个接口供子项目mall-product调用,并且将mall-order注册到consul注册中心中去。
在子模块mall-order中创建了一个controller包,在包里面创建了OrderController类,在里面写了一个最简单的接口,返回结果为String,但是实际情况需要我们返回的是Result类型的结果。这就需要我们去封装一个Result类,用来保存响应的结果。(后面我们会单独创建一个公共的子项目,用来存放实体类、工具类、配置类等一些公共的东西。)
把一个子项目注册到consul注册中心上面已经叙述过了,这里将mall-order注册到consul注册中心的步骤不再阐述。
第三步,我们需要在子项目mall-product中,写一个接口,用来指定调用的是哪一个服务,以及服务中的哪些接口。
首先在子项目mall-product中创建一个feign包,在里面创建一个OrderFeign接口(命名可以随意,但是一般以Feign结尾用来标识这是一个Feign接口,前缀为调用的服务的名字。)使用@FeignClient注解,注解中使用value属性,用来指定调用的服务的名字。子项目之间的通信是通过Feign根据服务名去consul中拉取服务,然后再由Feign去调用服务中的接口,然后获得响应结果。OrderFeign中的接口需要写全路径名,如果contorller中有前缀,那么就需要在OrderFeign中的接口上使用@RequestMapping注解将前缀也加上。OederFeign中的接口需要与OrderController中的接口一致,包括接口名、参数列表以及返回结果。
第四步,在ProductController中注入OrderFeign,然后调用OrderFeign中的接口。
在ProductController中使用@Autowired注解,获取OrderFeign接口的对象。在需要调用mall-order 服务中的接口的地方,使用orderFeign对象调用接口即可。
最后访问localhost:8081/product/hello,就可以看到Hello World!。
创建公共模块
创建公共模块,可以将Result类和一些工具类,还有实体类放在里面,当其他模块需要使用工具类和实体类的时候,只需要在pom文件中导入公共模块的坐标就可以使用了。
package cn.util;
/**
* @author noBody
* @className: Result
* @date 2024 09 06
* @description:
**/
public class Result {
private Integer code;
private String message;
private Object data;
public Result() {
}
public Result(Integer code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
public Result(Integer code, String message){
this.code = code;
this.message = message;
}
public Result(Integer code,Object data){
this.code = code;
this.data = data;
}
public static Result success(Integer code,Object data){
return new Result(code,data);
}
public static Result success(String message,Object data){
return new Result(200,message,data);
}
public static Result success(String message){
return new Result(200,message);
}
public static Result success(Object data){
return new Result(200,data);
}
public static Result fail(String message , Object data){
return new Result(404,message,data);
}
public static Result fail(Object data){
return new Result(404,data);
}
public static Result fail(String message){
return new Result(404,message);
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
@Override
public String toString() {
return "Result{" +
"code=" + code +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
复制公共模块的groupId、artifactId、version,粘贴到需要使用公共模块中的类的子项目。使用dependency标签将本地Maven仓库中的项目导入。
在SpringCloud使用MybatisPlus
SpringBoot使用MybatisPlus 步骤和这篇文章一样。在公共模块中使用CodeGenerator类逆向生成实体类、mapper、service类、controller类。
Feign+MyBatisPlus获取数据
第一步,在OrderController中编写一个接口,获取数据。
第二步,在application.yml中配置datasource和MyBatisPlus信息。(在spring的后面靠边粘贴)
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123456
hikari:
connection-timeout: 6000
maximum-pool-size: 5
# jackson:
# date-format: yyyy-MM-dd HH:mm:ss
# time-zone: GMT+8
# serialization:
# write-dates-as-timestamps: false
mybatis-plus:
configuration:
#开启驼峰功能,数据库字段hello_world 实体类helloWorld 也能对应匹配
map-underscore-to-camel-case: true
#结果集自动映射(resultMap)
auto-mapping-behavior: full
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:mapping/*Mapper.xml
global-config:
# 逻辑删除配置
db-config:
# 删除前
logic-not-delete-value: 1
# 删除后
logic-delete-value: 0
第三步,在启动类上使用@MapperScan注解,指定mapper接口所在的位置。
使用Hystrix实现服务降级
什么是服务降级:需要调用的是服务A,但是服务A中调用了服务B,服务B中又调用了服务C。突然有一天,服务C中出了bug跑不动了,但是服务A仍然在被访问,但是每当调用服务C的时候就停在那了,然后程序依然在跑,JVM内存就一直不会回收分给这段程序的空间,随着访问量的增加,JVM的内存会一直被占着,这就会极大影响JVM内存容量,会导致JVM内存不足。此时服务降级就会让服务B调用的过程中不再去调用服务C,这样调用完服务B后,就直接返回响应结果,尽管响应的结果是不完整的,但是可以让程序可以成功的跑完,不会占用JVM内存。Hystrix就可以很好的去判断任何一个服务是否可以正常的运行,不可以正常运行的就会去做服务降级。
导入Hystrix依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
配置yml文件
feign:
client:
# 配置
config:
#default为全局配置,如需对指定服务记录日志,换成服务名即可
default:
loggerLevel: FULL
hystrix:
enabled: true
circuitbreaker:
enabled: true
添加@EnableHystrix注解
在启动类的上方添加@EnableHystrix注解,开启服务降级。
处理调用的服务的接口
创建一个类实现OrderFeign中的所有接口,对其中的所有接口进行处理,当接口出现异常时,做出一些提示信息或者返回一些特定数据。并且要使用@Component注解将这个类加入IOC容器,交给Spring管理。
@Component
public class OrderHystrix implements OrderFeign {
@Override
public String hello() {
return "服务器异常,请稍后重试!";
}
@Override
public Result queryAll() {
return Result.fail("服务器异常,请稍后重试!");
}
}
指定fallback的类
在OrderFeign接口上方的@FeignClient注解中fallback里面指定Class类型的类。
可以自行测试,当关闭order服务后,当调用order中的接口时,会返回OrderHystrix类中的对应的接口的提示信息,实现服务降级。
使用gateway提供统一的请求入口
想要在项目中使用gateway,我们可以创建一个子模块,专门用来配置gateway,并且需要将这个子模块,加入注册中心交给SpringCloud管理。
第一步,创建一个mall-gateway的子模块,将他交给父项目管理,并且要进注册中心
第二步,导入gateway的依赖 ,pom文件中不能有spring-boot-web-starter依赖,因为gateway依赖中自带webflux依赖,如果两个都有会冲突
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
第三步,配置application.yml文件,设置路由、断言、过滤器(gateway是cloud的子级,与consul同级)
gateway:
routes:
- id: provider-web #服务id,根据需要随意起名
uri: lb://web # lb://服务名 服务名是spring.application.name值
predicates:
- Path=/web/** #当gateway接收以web开头的请求后会将请求转发给http://localhost:8082
filters:
- StripPrefix=1 #去除前缀 原始请求路径:/web/subpath 去除后将请求拼接成
这里我们需要配置两个路由,分别为mall-product和mall-order服务,需要做的就是将原本web的地方换成对应的服务名,这样gateway就可以去注册中心找和服务名对应的url进行字符串拼接,就可以做到请求转发了。uri中的lb:是负载均衡的意思,lb是loadbalance的缩写,添加了lb:就说明使用负载均衡。
什么是负载均衡:负载均衡可以提高我们服务的可用性,让我们的服务更稳定。那为什么可以提高呢,是因为我们可以写多个除了端口号不一样外一模一样的服务(这样的多个服务,也叫做服务集群),当有一天某一个服务坏了,另一个服务可以顶上,而不是服务直接没办法用了。
路由中的断言predicates和过滤器filters,读者可以自行前往官网阅读。SpringCloud-gateway