SpringCloud
前言
什么是微服务架构:
SpringCloud 是微服务一站式服务解决方案,微服务全家桶。它是微服务开发的主流技术栈。它采用了名称,而非数字版本号。
springCloud 和 springCloud Alibaba 目前是最主流的微服务框架组合。
版本选择:
选用 springboot 和 springCloud 版本有约束,不按照它的约束会有冲突。
官方版本的对应信息:https://start.spring.io/actuator/info
本次学习的各种软件的版本:
技术 | 版本 |
---|---|
cloud | Hoxton |
boot | 2.2.2.RELEASE |
cloud alibaba | 2.1.0.RELEASE |
java | 1.8 |
Maven | 3.5.2 |
Mysql | 5.7及以上 |
Cloud升级:组件停更
听说第一季主角大部分都挂了,轻着也进icu了,阿里Nacos牛掰
参考资料,尽量去官网
https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/
工程建造
写一个下图的Hello World
构建父工程,后面的项目模块都在此工程中:
设置编码:Settings -> File Encodings
注解激活:
Java版本确定:
父工程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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.achang.springcloud</groupId>
<artifactId>cloud2021</artifactId>
<version>1.0-SNAPSHOT</version>
<!--第一步-->
<packaging>pom</packaging>
<!-- 统一管理 jar 包版本 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
<mysql.version>5.1.49</mysql.version>
<druid.version>1.1.17</druid.version>
<mybatis.spring.boot.version>2.0.5</mybatis.spring.boot.version>
</properties>
<!-- 子块基础之后,提供作用:锁定版本 + 子module不用写 groupId 和 version -->
<dependencyManagement>
<dependencies>
<!-- 下面三个基本是微服务架构的标配 -->
<!--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-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<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>
<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>
上面配置的解释:
首先要加 <packaging> pom </packaging>
这个。
聚合版本依赖,dependencyManagement
只声明依赖,并不实现引入
,所以子项目还需要写要引入的依赖。
==> dependencyManagement
和 dependency
的对比
前者的优点:
①子类如果不写版本,则引用他规定的父类版本;写了版本就用子类的(类似重写)
②需要升级或降级项目时,只需要改这里就好,不用每一个都要更改,便于整个工程的管理
他不会真正的引入依赖,而只是声明
后者:
会真正的引入依赖
==> 如何跳过maven的test
环节
通过这个闪电的标志跳过maven的test
环节
父工程创建完成通过 mvn:install
将父工程发布到仓库方便子工程继承
Rest微服务工程构建
最开始的订单模块
步骤:
- 建module
- 改pom
- 写yml
- 主启动
- 业务类
- 测试
1. 微服务提供者支付module模块cloud-provider-payment8001
1. 新建模块
子项目名:cloud-provider-payment8001
调整一下父项目pom文件
2. 在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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2021</artifactId>
<groupId>com.achang.springcloud</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>
3. 写yml
在resources目录下新建application.yml配置文件
#微服务建议一定要写服务端口号和微服务名称
server:
#端口号
port: 8001
spring:
application:
#微服务名称
name: cloud-provider-payment8001
#数据库配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: com.mysql.jdbc.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloud2021?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: "root"
password: "00000"
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.achang.springcloud.bean # 所有Bean 别名类所在包
阿昌在这里遇到的问题:
①问题:
Injection of resource dependencies failed
===> 找了好久 发现yaml配置文件下面的type-aliases-package
没有引用正确
②问题:
java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)
===> 一开始判断我密码是否输入正确,判断正确后;
===> 再判断数据库是否有权限
GRANT ALL ON cloud2021.* TO 'root'@'localhost' IDENTIFIED BY '00000';
===>还不行,就给用户名和密码添加"",双引号
!!!
4. 主启动
在java包下创建主启动类com.achang.springcloud.PaymentMain8001
@SpringBootApplication
public class PaymentMain8001 {
// 主启动类
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
5. 业务类
1、建表SQL
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
INSERT INTO payment(`serial`)VALUES("张三");
2、bean
在springcloud包下新建实体类bean.Payment
//这三个注解是lombok的,除了导入依赖,idea还需要安装插件(具体操作问度娘)
@Data //set/get方法
@AllArgsConstructor //有参构造器
@NoArgsConstructor //无参构造器
public class Payment implements Serializable {
private Long id;
private String serial;
}
在bean包下新建CommonResult(json封装体,传给前端的)
//返回给前端的通用json数据串
@Data //set/get方法
@AllArgsConstructor //有参构造器
@NoArgsConstructor //无参构造器
public class CommonResult<T> {
private Integer code;
private String msg;
private T data; //泛型,对应类型的json数据
//自定义两个参数的构造方法
public CommonResult(Integer code, String msg) {
this(code,msg,null);
}
}
3、dao
在springcloud包下新建dao.PaymentDao接口
@Mapper
public interface PaymentDao {
//增
public int add(Payment payment);
//查 加上@Param注解,mapper中就可以采用#{}的方式把@Param注解括号内的参数进行引用
public Payment getPaymentById(@Param("id")Long id);
//这里用增和改进行演示,有兴趣的可以自己加其他的方法
}
4、mapper
在resources目录下新建mapper目录,然后新建PaymentMapper.xml
<?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.achang.springcloud.dao.PaymentDao">
<!-- Payment标红了不用管,因为我们已经在yml文件中指定了Payment的位置了 -->
<insert id="add" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial) values(#{serial})
</insert>
<resultMap id="BaseResultMap" type="com.achang.springcloud.bean.Payment" >
<id column="id" property="id" jdbcType="BIGINT"/>
<id column="serial" property="serial" jdbcType="VARCHAR"/>
</resultMap>
<!--返回用resultMap,防止命名不规范-->
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
select * from payment where id = #{id}
</select>
</mapper>
5、service
在springcloud包下新建service.PaymentService接口
public interface PaymentService {
//增
public int add(Payment payment);
//查
public Payment getPaymentById(@Param("id")Long id);
}
在service包下新建impl.PaymentServiceIpml实现类
@Service
public class PaymentServiceImpl implements PaymentService {
//@Autowired也可以
@Resource
private PaymentDao paymentDao;
@Override
public int add(Payment payment) {
return paymentDao.add(payment);
}
@Override
public Payment getPaymentById(Long id) {
return paymentDao.getPaymentById(id);
}
}
6、controller
在springcloud包下新建controller.PaymentController
@Controller
@Slf4j //日志
public class PaymentController {
@Resource
private PaymentService paymentService;
//前后端分离,所以不能直接返回对象,数据要先经过CommonResult封装再返回
@PostMapping(value = "/payment/add")
public CommonResult add(Payment payment){
int result = paymentService.add(payment);
log.info("-----插入结果----: "+ result);
if (result > 0){
//插入成功
return new CommonResult(200,"插入数据库成功",result);
}else {
return new CommonResult(444,"插入数据库失败",null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id")Long id){
Payment result = paymentService.getPaymentById(id);
log.info("-----查询结果----: "+ result);
if (result != null){
//查询成功
return new CommonResult(200,"查询成功",result);
}else {
return new CommonResult(444,"没有对应记录,查询id: "+id,null);
}
}
}
6. 测试
启动项目
浏览器输入http://localhost:8001/payment/get/1
,查询成功。
因为浏览器一般不支持直接发送post请求,所以,需要使用工具进行测试。(我这里用的是Postman)
重新测试查询,没问题。
输入http://localhost:8001/payment/add?serial=achang21
发送post请求,往数据库中插入一条数据,需要把数据写到body中。
阿昌遇到的问题:
Invalid bound statement (not found)
===> 说我的mapper文件找不到,我根据网上找了半天,都无效,最后在PaymentService上不应该加@Mapper注解,所以才出错
===> 也要注意发送的请求不要拉写,写错
2. 热部署Devtools(只能在开发阶段使用)
1、在cloud-provider-payment8001项目中添加热部署依赖(已经在导入了,所以看看就行了,记一下步骤)
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
2、添加一个插件到父类总工程的pom.xml里(这一步之前也已经做了)
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
3、开启自动编译的选项
4、热注册开启
组合键Shift+Ctrl+Alt+/
(Mac系统的把Ctrl换成command键),选中Registry…
5、重启IDEA
开完之后感觉有点卡,虽然能自动部署,但是代码提示变慢了,等以后换了电脑再玩自动热部署,现在就不开了。
把第四步打上的勾去掉(原本打上的就不用去掉了),然后重启idea就可以了。
不使用自动热部署,也可以使用热部署,按这个绿色的锤子,重新启动,只编译改了的文件,所以比重启快一些。
3. 微服务消费者订单module模块cloud-consumer-order80
1. 新建模块
cloud-consumer-order80
2. 改pom
往pom中添加:
<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.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>
3. 写yml
在resources目录下新建application.yml文件
#访问一个网站时,默认是80端口,给用户80端口,用户就可以不用加端口直接访问页面
server:
port: 80
123
4. 主启动
com.angenin.springcloud.OrderMain80
@SpringBootApplication
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class, args);
}
}
5. 写业务
1、复制cloud-provider-payment8001项目里的bean(里面2个实体类)到本项目(cloud-consumer-order80)的springcloud包下。(在后面的工程重构,阳哥会把实体类抽取出来)
2、在springcloud包下新建config.ApplicationContextConfig
需要将RestTemplate加入到ioc容器中给spring管理,需要在配置类中配置加入容器
@Configuration
public class ApplicationContextConfig {
//往容器中添加一个RestTemplate
//RestTemplate提供了多种便捷访问远程http访问的方法
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
//applicationContext.xml <bean id="" class="">
3、在springcloud包下新建controller.OrderController
@RestController
@Slf4j
public class OrderController {
public static final String PAYMENT_URL ="http://localhost:8001";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/add")
public CommonResult<Payment> add(Payment payment){
log.info("********插入的数据:" + payment);
return restTemplate.postForObject(PAYMENT_URL+"/payment/add",payment,CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id")Long id){
log.info("********查询的id:" + id);
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
}
6. 测试
启动两个项目进行测试,两个都启动后,右下角会弹出个services提示,点击show。
然后会把运行的项目合并在一起显示:
阿昌遇到的问题:
Error creating bean with name ‘requestMappingHandlerMapping’ defined in class path resource
提示我说requestMappingHandlerMapping
出现问题,阿昌查了半天,发现都无效,添加新的springboot-cache依赖无效,后来发现OrderController中的@GetMapping("/consumer/payment/get/{id}")
中的最后为#{id}
出错了,应该为{id}
在浏览器中输入http://localhost/consumer/payment/get/1
成功查询到数据。
通过调用80中的controller中再调用8001中的get查询
发现了问题:插入的数据,有主键,没有内容,内容为null
解决办法:
在生产者的PaymentController里给添加的方法()中添加注解@PathVariable
上面↑↑↑:问题的分析,因为
===> 80调用8001的add过程中的payment对象丢失了,8001需要通过@RequestBody来获得80的payment对象
@PostMapping(value = "/payment/add")
public CommonResult add(@RequestBody Payment payment){
int result = paymentService.add(payment);
log.info("-----插入结果----: "+ result);
if (result > 0){
//插入成功
return new CommonResult(200,"插入数据库成功",result);
}else {
return new CommonResult(444,"插入数据库失败",null);
}
}
之后再调用服务即可调用成功!!!
4. 工程重构
观察问题:系统中有重复部分,重构。
1. 新建新工程:cloud-api-commons
2. 改pom
添加依赖:
<dependencies>
<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>
<!-- 一个Java工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.1.0</version>
</dependency>
</dependencies>
3. 拷贝bean到本项目中(路径需要一样,先在本项目中建包,然后在拷贝)
4. 对本项目进行打包
让他加入到本地maven库中
5. 删除另外两个项目的entities包。
6. 然后给两个项目引入打包后项目的坐标。
<!--引入自定义的api通用包,可以使用Payment支付bean-->
<dependency>
<groupId>com.achang.springcloud</groupId>
<artifactId>cloud-api-common</artifactId>
<version>${project.version}</version>
</dependency>
7.重新测试
http://localhost/consumer/payment/get/1
http://localhost/consumer/payment/add?serial=刘峰
目前工程样图:
感谢尚硅谷
https://www.bilibili.com/video/BV18E411x7eT?p=36&spm_id_from=pageDriver
P1-P14内容