在学习SpringCloud之前,我认为需要先去了解微服务的概念
参考:
目录
在学习SpringCloud之前,我认为需要先去了解微服务的概念
支付模块的构建 cloud-provider-payment8001
cloud-provider-payment8001 的测试
本次学习参考:尚硅谷2020最新版SpringCloud(H版&alibaba)框架开发教程全套完整版从入门到精通(大牛讲授spring cloud)
强烈推荐这个SpringCloud学习视频,深入浅出,细致入微。
本次学习线路图:
SpringCloud是什么?
Spring Cloud是一套完整的微服务解决方案,基于Spring Boot框架,准确的说,它不是一个框架而是一个容器,它将市面上较好的微服务框架集成进来,从而简化了开发者的代码量。
一个微服务系统,它所需要包含的功能,Spring Cloud都帮忙集成了。
SpringBoot、SpringCloud版本选择
SpringBoot的版本更迭非常快,在SpringBoot2.0发布以后官方就强烈建议从SpringBoot1.5 升级到2.X以上的版本了。
在2020年这一年如果在经历SpringBoot的开发的人会有很明显的感觉,2、3月份用的SpringBoot2.2.1,用着用着发现到现在发现版本就变成了2.3。
技术的选型不是非要用最新的版本,SpringCloud的版本和SpringBoot的版本有着密切关系。
SpringCloud的版本不是以数字编排的,而是使用了伦敦地铁站的站名,并且名称是按字母顺序排列的。比如Angel是第一个版本,Brixton是第二个版本。当SpringCloud的发布内容累计到临界点或者一个重大Bug被解决以后,会发布 "service release"版本,简称SRX版本。比如Greenwich.SR2就是SpringCloud发布的Greenwich版本的第二个SRX版本。
在写本文的时候,SpringCloud已更新到 HoxtonSR4的版本,SpringBoot更新到2.3.0的版本,那么都用最新的可以吗?
也不是那样的,SpringBoot和SpringCloud的版本有约束、有冲突,需要严格按照官网。
SpringCloud官方文档:这上面写了,如果是Hoxton的版本,那么最好是2.2.x的版本。
2.2.x的版本看起来太模糊了,这里还有更详细的版本对应查看方法:https://start.spring.io/actuator/info,这里面是Json格式的数据,需要通过Json解析出来。通过Json解析以后,可以看到更详细的版本对应。
目前的最新的版本是SpringCloud Hoxton.SR4的版本要求SpringBoot的版本大于等于2.2.0M4并且小于2.3.1.BUILD的版本。
为什么要这么严格呢?因为环境造成的问题总是让人头疼,浪费时间又难定位。所以最好按照官方的说明选择SpringCloud和SpringBoot的版本。
但这依旧不是最后决定选择的版本,在同时使用SpringBoot和SpringCloud的时,需要照顾Cloud的版本,由Cloud决定Boot的版本。
打开官网,查看文档:
按照目前最新的SpringCloud Hoxton.SR4的版本,给予最好支持的是SpringBoot 2.2.5.RELEASE的版本。
关于Cloud各种组件的停更/升级/替换
SpringCloud是微服务的集大成者,里面包含了很多技术,而现在SpringCloud进行了一次大更新,很多技术现在已经不再使用,有了别的替代方案。
红叉的技术几乎已经停止更新了,绿勾的技术是目前的替换方案,虽然现在看着这些技术非常陌生,但接下来都是学习的目标。
这里先仅作为了解。
微服务架构编码构建
IDEA创建project工作空间
微服务cloud整体聚合父工程Project,万物之初,环境得搞好,父工程步骤:
- New Project
- 聚合总工程名字
- Maven 选择版本
- 字符编码
- 注解生效激活
- java编译版本选择
- File Type过滤(可选)
新建一个Maven工程,这里选择的是 org.apache.maven.archetypes:maven-archetype-site 这个模板
确定好聚合总工程的名字
选择Maven版本,请选择3.5及以上的版本。
在Editor-File Encoding里确定好字符编码,这里统一为UTF-8
在Build,Execution,Deployment-compiler-Annotation Processors 把 Enable annotation processing打钩,使注解生效激活 。
(百度了一下这个设置主要是为lombok所使用)
java版本的选择,新建的工程这里默认为1.5的话,这里需要改成对应的版本。
File Types的设置,IDEA自动生成的.idea和.iml 文件看着很烦,用File types忽略掉它们。
这样,父工程就搭建好了。
父工程的Pom.xml的说明以及Maven细节复习
有几点需要注意:
- <packaging>pom</packaging>
- <packaging>pom</packaging>的意思是使用maven分模块管理,都会有一个父级项目,pom文件一个重要的属性就是packaging(打包类型),一般来说所有的父级项目的packaging都为pom,packaging默认类型jar类型,如果不做配置,maven会将该项目打成jar包。
- <properties>:
- 统一管理jar包版本
- <dependencyManagement>:
- dependencyManagement用在父工程,子模块继承之后,提供作用:锁定版本+子module不用再写groupId和version
-
dependencyManagement和dependencies的区别:dependencies即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承),dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
-
使用dependencyManagement的好处是:如果有多个子项目都引用同一样依赖,则可以避免在每个使用子项目里都声明一个版本号,这样当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要一个一个子项目的修改,另外如果一个子项目需要另外一个版本,只需要声明version即可。
-
org.springframework.boot、org.springframework.cloud、com.alibaba.cloud是搭建一个SpringCloud工程的必备依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
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.claw</groupId>
<artifactId>springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!--统一管理jar包版本-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>12</maven.compiler.source>
<maven.compiler.target>12</maven.compiler.target>
<junit.version>4.12</junit.version>
<lombok.version>1.18.10</lombok.version>
<log4j.version>1.2.17</log4j.version>
<mysql.version>8.0.18</mysql.version>
<druid.version>1.1.10</druid.version>
<mybatis.spring.boot.version>2.1.1</mybatis.spring.boot.version>
</properties>
<!--dependencyManagement用在父工程,子模块继承之后,提供作用:锁定版本+子module不用再写groupId和version-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</dependency>
<!--spring boot 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud Hoxton.SR1-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud 阿里巴巴-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
<!-- druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
</project>
ok,Project和父工程已经搭建完成,该编码辣。
Rest微服务工程构建
目标:使用订单模块调用支付模块
那么该如何构建呢?
大步骤:
- cloud-provider-payment8001微服务提供者支付Module模块
- 热部署Devtools
- cloud-consumer-order80微服务消费者订单Module模块
支付模块的构建 cloud-provider-payment8001
构建cloud-provider-payment8001的步骤
- 建立Module
- Pom文件的编写
- 配置文件 application.yaml的编写
- 主启动
- 业务类编写
接下来一一讲解:
在父工程的基础上新建一个module,就创建一个普通的Maven工程。
cloud-provider-payment8001 的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud</artifactId>
<groupId>com.claw</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-payment8001</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
配置文件 application.yaml
# 端口号
server:
port: 8001
spring:
application:
name: cloud-payment-service
# 数据库配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/eesy_mybatis?serverTimezone=GMT%2B8&userUnicode=true&characterEncoding=utf-8
username: root
password: password
# mybatis 配置
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.claw.springcloud.entities
主启动文件的编写,当然也可以直接创建一个SpringBoot,不知道为什么老师没有这样做。
/**
* @author Claw
*/
@SpringBootApplication
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
接下来该编写业务相关的类了,考虑到前后端分离的情况,我们最终传递给前端的是Json字符串,比如说CommonResult{200,success},200这个数字就是代表执行成功的code,success就是对这个code的补充说明。前端的事情我们暂且先不去管,专注后端的话,那么就是 controller-service-dao-mysql 这四个部分。
所以业务类的编写步骤为:
- 建表
- 实体类
- 主实体Payment
- Json封装体CommonResult
- Dao
- Service
- Controller
建表SQL:
CREATE TABLE payment
(
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
serial VARCHAR(200) DEFAULT '',
PRIMARY KEY (id)
) ENGINE = INNODB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8
实体类 - 主实体Payment
/**
* 实体类
* @author Claw
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment {
private Long id;
private String serial;
}
实体类 -Json封装类,我们前面已经说到了,考虑到前后端分离情况,我们需要告诉前端的只是结果,所以我们需要正ComonResult这个Json封装类。
/**
* Json封装
* @author Claw
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
private Integer code;
private String message;
/**
* 这里泛型的作用:显示我们传入类的信息,比如我们传入的Payment类 就能获取Payment类的信息
*/
private T data;
/**
* 两个参数的构造器
* @param code
* @param message
*/
public CommonResult(Integer code,String message){
this(code,message,null);
}
}
dao层:增删改查并不是本次学习的目的,所以现在暂时两个简单的方法,读取的操作和写入的操作
/**
* Dao 接口
* @author Claw
*/
@Mapper
public interface PaymentDao {
/**
* 读 操作
* @param payment
* @return
*/
public int create(Payment payment);
/**
* 写 操作
* @param id
* @return
*/
public Payment getPayment(@Param("id") Long id);
}
Mapper映射文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.claw.springcloud.dao.PaymentDao">
<insert id="create" parameterType="payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial)
values (#{serial})
</insert>
<!--使用结果集进行映射 确保真实开发中字段对应的准确性 -->
<resultMap id="BaseResultMap" type="com.claw.springcloud.entities.Payment">
<id column="id" property="id" jdbcType="BIGINT"></id>
<id column="serial" property="serial" jdbcType="VARCHAR"></id>
</resultMap>
<select id="getPayment" parameterType="Long" resultMap="BaseResultMap">
select *
from payment
where id = #{id}
</select>
</mapper>
Service层:
/**
*Service接口
* @author Claw
*/
public interface PaymentService {
/**
* 读 操作
* @param payment
* @return
*/
public int create(Payment payment);
/**
* 写 操作
* @param id
* @return
*/
public Payment getPayment(@Param("id") Long id);
}
Service实现类:
/**
* @author Claw
*/
@Service
public class PaymentServiceImpl implements PaymentService {
@Resource
private PaymentDao paymentDao;
@Override
public int create(Payment payment) {
return paymentDao.create(payment);
}
@Override
public Payment getPayment(@Param("id") Long id) {
return paymentDao.getPayment(id);
}
}
Controller层:
/**
* Controller层
* @author Claw
*/
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentServiceImpl paymentService;
@PostMapping("/payment/create")
public CommonResult create(Payment payment) {
int result = paymentService.create(payment);
log.info("----------结果:" + result);
if (result > 0) {
return new CommonResult(200, "插入数据成功", result);
} else {
return new CommonResult(444, "插入数据失败", null);
}
}
@GetMapping("/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id) {
Payment payment = paymentService.getPayment(id);
log.info("----------结果:" + payment);
if (payment != null) {
return new CommonResult(200, "查询成功", payment);
} else {
return new CommonResult(444, "查询失败", null);
}
}
}
cloud-provider-payment8001 的测试
启动启动程序,在浏览器输入访问路径,第一个方法 getPaymentById()已经成功拿到数据
因为create方法是post请求,所以使用了PostMan进行模拟,ok,也执行成功了。
现在我们第一个微服务就写完了~!