文章目录
前言
以下内容是我在学习周阳老师的2020最新版 Spring Cloud(H版&alibaba)时的笔记,其中并整合了之前学习G版时的相关笔记。如有错误请指正,谢谢。
大纲
Spring Cloud
从最原始的开发开始,全部代码都放在一个 .java 文件下,到后来分三层架构 MVC 开发;再后来为了简化不同架构里面的开发从而有了框架:Spring、Spring MVC、Mybatis;再后来 Spring Boot 作为新一代的 Java EE 开发标准,有自动装配、开箱即用的优点。但是模块化的开发本质上还是 all in one 的开发。
微服务因此而出现,微服务提倡将单一的应用程序划分成一组小的服务,每个服务运行在其独立的自己的进程中,服务之间互相协调,互相配置,为用户提供最终价值。微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地解耦,每个微服务提供单个业务功能地服务,一个服务做一件事情,从技术角度看就是一种小而独立地处理过程,类似进程的概念,能够自行单独启动或销毁,拥有独立地数据库。服务与服务间采用轻量级的通信机制互相协作(通常是基于 HTTP 协议的 RESTful API)。
但是微服务的架构有四个核心问题:
服务很多的情况下客户端该怎么访问?
服务与服务之间该如何通信?
服务应该如何治理?
服务挂了该怎么办?
而 Spring Cloud 是一站式解决方案,解决微服务架构里面的四个核心问题,为开发者提供了在分布式系统(配置管理,服务发现,熔断,路由,微代理,控制总线,一次性token,全局琐,leader选举,分布式session,集群状态)中快速构建的工具,使用Spring Cloud的开发者可以快速的启动服务或构建应用、同时能够快速和云平台资源进行对接。
父项目:cloud2020
pom.xml
<packaging>pom</packaging>
<!-- packaging pom 总工程-->
<!--统一管理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>5.1.47</mysql.version>
<druid.version>1.1.16</druid.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
</properties>
<!--子模块继承之后,提供作用:锁定版本+子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>
dependencyManagement & dependencies
这样做的好处就是:如果有多个子项目都引用同一样的依赖,则可以避免在每个使用的子项目里都声明一个版本号,这样想升级或切换到另一个版本时,只需在顶层父容器里更新,而不需要一个一个子项目的修改l;另外如果某个子项目需要另外的一个版本,只需声明version版本。dependencyManagement 里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。
支付模块
数据库建表语句
DROP TABLE IF EXISTS `payment`;
CREATE TABLE `payment` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`serial` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '支付流水号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '支付表' ROW_FORMAT = Dynamic;
PaymentMain8001
@SpringBootApplication
public class PaymentMain8001 {
public static void main(String[] args){
SpringApplication.run(PaymentMain8001.class,args);
}
}
Dao 层
@Mapper
public interface PaymentDao {
int create(Payment payment);
Payment getPaymentById(@Param("id") Long id);
}
<?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.hangzhou.springcloud.dao.PaymentDao">
<insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial) values (#{serial})
</insert>
<resultMap id="BaseResultMap" type="com.hangzhou.springcloud.entities.Payment">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="serial" property="serial" jdbcType="VARCHAR"/>
</resultMap>
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
select * from payment where id = #{id};
</select>
</mapper>
service 层
public interface PaymentService {
int create(Payment payment);
Payment getPaymentById(@Param("id") Long id);
}
@Service
public class PaymentServiceImpl implements PaymentService {
@Resource
private PaymentDao paymentDao;
@Override
public int create(Payment payment) {
return paymentDao.create(payment);
}
@Override
public Payment getPaymentById(Long id) {
return paymentDao.getPaymentById(id);
}
}
controller 层
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@PostMapping(value = "/payment/create")
public CommonResult create(Payment payment) {
int result = paymentService.create(payment);
log.info("-----create方法执行结果:"+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 payment = paymentService.getPaymentById(id);
log.info("-----getPaymentById方法执行结果:"+payment);
if(payment != null){
return new CommonResult(200,"查询成功",payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID:"+id,null);
}
}
}
@Repository & @Mapper
@Repository 需要使用 @MapperScan(“xxx.xxx.xxx.mapper”) 进行扫描,然后生成Dao层的Bean才能被注入到Service层中。
@Mapper 不需要配置扫描地址,通过xml里面的namespace里面的接口地址,生成了Bean后注入到Service层中。
@RestController
@RestController 注解相当于 @Controller + @ResponseBody 两个注解的结合,返回 json 数据不需要在方法前面加 @ResponseBody 注解了,但使用@RestController这个注解,就不能返回 jsp,html 页面,视图解析器无法解析 jsp,html 页面。
@Controller
@ResponseBody
public @interface RestController {
@Slf4j
等同于 private final Logger logger = LoggerFactory.getLogger(当前类名.class)
,可以在代码中直接引用 log.info( ) 打印日志,但是前提是需要安装 Lombok 插件或 pom 文件引入。
@Autowired & @Resoure
@Autowired & @Resource:自动装配
@Autowired 默认优先按照类型去容器中找对应的组件:applicationContext.getBean(xxxDao.class),如果找到多个相同类型的组件,再将属性名称组为组件的id去容器中查找:applicationContext.getBean(“xxxDao”)。并且默认 @Autowired(required = true),表示注入的时候该 bean 必须存在,否则就会注入失败。
@Resource 默认是按照组件名称进行装配的;但是没有@Qualifier和@Autowired(required = false)功能。
@PathVariable & @RequestParam
@PathVariable 只能用于接收url路径上的参数,例如/blogs/1;而 @RequestParam 只能用于接收请求带的params,例如blogs?blogId=1。
- 当URL指向的是某一具体业务资源(或资源列表)例如博客、用户时,使用@PathVariable
- 当URL需要对资源或者资源列表进行过滤,筛选时,用@RequestParam
例如我们会这样设计URL:
/blogs/{blogId}
/blogs?state=publish 而不是 /blogs/state/publish 来表示处于发布状态的博客文章
热部署
模块pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
父类pom.xml
<!--热启动插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
idea 设置中开启自动构建
Registry(mac快捷键option+command+shift+/)
消费者订单模块
pom.xml
<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>
OrderMain80
@SpringBootApplication
public class OrderMain80 {
public static void main(String[] args){
SpringApplication.run(OrderMain80.class,args);
}
}
注册 RestTemplate
@Configuration
public class ApplicationContextConfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
controller 层
@RestController
@Slf4j
public class OrderController {
private static final String PAYMENT_URL = "http://localhost:8001";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment){
return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
}
RestTemplate
RestTemplate 提供了多种便捷访问远程 Http 服务的方法,是一种简单便捷的访问 restful 服务模板类,是 Spring 提供的用于访问 Rest 服务的客户端模板工具集,实现80到8001的远程调用。
使用:使用 restTemplate 访问restful接口非常的简单粗暴,(url、requestMap、ResponseBean.class)这三个参数分别代表 REST 请求地址、请求参数、HTTP 响应转换被转换成的对象类型。
如果 8001 的PaymentController 请求中没有加@RequestBody注解
请求虽然返回插入数据成功,但是数据库中只有主键自增而没有实际值进入
构建 cloud-api-commons modules
将cloud2020中各个微服务组件中通用的entities部分进行重构
Lifecycle --> clean
Lifecycle --> install
MavenRepository
在 consumer-order80 和 provider-payment8001 的pom.xml文件中添加以下内容,然后删除entities包
<dependency>
<groupId>com.hangzhou.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
重启项目完成
开启 Run Dashboard 功能
idea 2019.3 版本中是 Services