Spring Cloud笔记-微服务架构编码构建(四)

我们要实现的功能是:订单-支付模块微服务,然后将上节课提到的技术挨个加进去。

我们遵循的原则是:约定>配置>编码。

1.IDEA新建Project工作空间

1.微服务Spring Cloud整体聚合父工程

新建Maven项目,选择org.apache.maven.archetypes:maven-archetype-site,GroupId为com.atguigu.springcloud,ArtifactId为cloud2020,指定自己的Maven,不要使用IDEA内置的。

修改项目的默认编码为UTF-8:Settings→Editor→File Encodings,将Global Encoding,Project Encoding,Default encoding for properties files都指定UTF-8,勾选“Transparent native-to-ascii conversion”,Create UTF-8 files选择“with NO BOM”。

注解生效激活:Settings→Build,Execution,Deployment→Compiler→Annotation Processors,勾选“Enable annotation processing”。

修改Java编译版本:Settings→Build,Execution,Deployment→Java Compiler,修改Target bytecode version为8。

文件过滤:Settings→Editor→File Types,在最下面添加*.idea;*.iml;,将某些文件和目录过滤掉。

2.父工程pom

修改pom.xml的打包方式为pom,删掉父工程的src目录,项目里只保留一个pom.xml文件。

<?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.atguigu.springcloud</groupId>
    <artifactId>cloud2020</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>8.0.18</mysql.version>
        <druid.verison>1.1.16</druid.verison>
        <mybatis.spring.boot.verison>1.3.0</mybatis.spring.boot.verison>
    </properties>
    <!-- 子模块继承之后,提供作用:锁定版本,子模块不用再写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 Alibaba 2.1.0.RELEASE-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--MySQL驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <!--Druid-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>${druid.verison}</version>
            </dependency>
            <!--mybatis-springboot整合-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.verison}</version>
            </dependency>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.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>
</project>

3.Maven工程落地细节复习

Maven使用dependencyManagement元素来提供一种管理依赖版本号的方式,通常会在一个组织或项目最顶层的父pom中看到dependencyManagement元素。使用pom.xml中的dependencyManagement元素能让所有在子项目中引用一个依赖而不用显式的列出版本号。Maven沿着父子层次向上找,知道找到一个拥有dependencyManagement元素的项目,然后使用dependencyManagement元素中指定的版本号。

这么做的好处是:如果多个子项目引用同样的依赖,可以避免在每个子项目中声明版本号,需要切换版本号的时候,只需要修改顶层父容器的版本号即可,不必修改所有子项目,如果子项目需要另外的版本号,只需要声明version即可。

dependencyManagement只是声明依赖,并不实现引入,因此子项目需要显式声明需要用的依赖。

所以说,如果上面的pom.xml报错,不要担心,因为在父pom中,仅仅是一个定义,一个规范。

如果不在子项目中声明依赖,是不会从父项目中继承下来的,只有子项目写了该依赖,并且没有指定具体版本号,才会从父项目中继承该项,并且version和scope都是从父pom中获取。

如果子项目指定了版本号,那么会使用子项目中指定的版本号。

在idea的Maven标签中,点击“Toggle 'Skip Tests' Mode”,用于开启/关闭测试,这里让其关闭测试。

4.父工程创建完成执行mvn:install,将父工程发布到仓库便于子工程继承

双击Lifecycle中的install将父工程打包安装到本地仓库中,如果出现BUILD SUCCESS,说明打包成功,如果不成功,clean一下再试试,如果还不行,看看报错信息进行解决。

2.Rest微服务工程构建

1.cloud-provider-payment8001微服务提供者支付模块

1.建Module

在父项目上右键,选择New Module,选择Maven,输入Module的名字cloud-provider-payment8001,因为是在父工程上创建的,所以GroupId和Version都是自动填充上的,我们只需要修改ArtifactId即可。

回到父工程的pom.xml查看,可以发现多了一个modules标签,它里面的module的值就是我们刚才创建的cloud-provider-payment8001这个module。

2.修改cloud-provider-payment8001的pom.xml

<?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>cloud2020</artifactId>
        <groupId>com.atguigu.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>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <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.在cloud-provider-payment8001的resources中创建application.yml文件

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/db2019?useUnicode=true&charcaterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai # 数据库连接
    username: root # 数据库用户名
    password: root # 数据库密码
mybatis:
  mapper-locations: classpath:mapper/*.xml # mapper文件的位置
  type-aliases-package: com.atguigu.springcloud.entities # 实体类所在包

4.创建Spring Boot主启动类

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class PaymentMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class, args);
    }
}

5.编写业务

1.建库建表

CREATE DATABASE db2019;
USE db2019;
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;

2.entity

Payment类,对应数据库中的字段,实现Serializable接口,用于后序的分布式部署。

package com.atguigu.springcloud.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
    private Long id;
    private String serial;
}

当实现前后端分离的时候,前后端的交互有通过统一的数据格式的,为此,创建CommonResult来规定具体格式。

package com.atguigu.springcloud.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
    private Integer code;
    private String message;
    private T data;

    public CommonResult(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

3.dao

创建dao接口。

package com.atguigu.springcloud.dao;

import com.atguigu.springcloud.entity.Payment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface PaymentDao {
    int create(Payment payment);

    Payment getPaymentById(@Param("id") Long id);
}

创建mapper.xml,根据yml的配置,我们的mapper.xml需要写在classpath:mapper/*.xml,于是,在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.atguigu.springcloud.dao.PaymentDao">
    <resultMap id="BaseResultMap" type="com.atguigu.springcloud.entities.Payment">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="serial" property="serial" jdbcType="VARCHAR"/>
    </resultMap>

    <insert id="create" parameterType="com.atguigu.springcloud.entities.Payment" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO payment(serial) VALUES (#{serial})
    </insert>

    <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
        SELECT * FROM payment WHERE id=#{id}
    </select>
</mapper>

4.service

编写PaymentService接口。

package com.atguigu.springcloud.service;

import com.atguigu.springcloud.entities.Payment;

public interface PaymentService {
    int create(Payment payment);

    Payment getPaymentById(Long id);
}

编写PaymentServiceImpl实现类,注入PaymentDao,通过dao操作数据库。

package com.atguigu.springcloud.service;

import com.atguigu.springcloud.dao.PaymentDao;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@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);
    }
}

5.controller

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class PaymentController {
    @Resource
    private PaymentService paymentService;

    @PostMapping("/payment/create")
    public CommonResult<Integer> create(Payment payment) {
        int result = paymentService.create(payment);
        log.info("插入结果:{}", result);
        if (result > 0) {
            return new CommonResult<>(200, "插入数据库成功", result);
        } else {
            return new CommonResult<>(500, "插入数据库失败", null);
        }
    }

    @GetMapping("/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
        Payment payment = paymentService.getPaymentById(id);
        log.info("查询结果:{}", payment);
        if (payment != null) {
            return new CommonResult<>(200, "查询成功", payment);
        } else {
            return new CommonResult<Payment>(400, "没有记录", null);
        }
    }
}

create()方法上有一个坑,Payment参数没有添加@RequestBody注解,可能导致Payment的属性都是NULL,也就是参数不能填充到bean中,当request的Content-Type是application/json的时候,要加上@RequestBody,才能将请求参数转换成Java对象。

6.测试

在payment数据库中插入一条记录用于测试。浏览器访问http://localhost:8001/payment/get/1,可以查询到结果。使用postman工具或者idea的REST Client工具发送一个post请求到http://localhost:8001/payment/create,带上请求参数,此时,就可以向数据库中插入一条记录了。

2.热部署Devtools

在cloud-provider-payment8001项目的pom.xml中加入devtools的坐标
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
在父项目的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>

开启自动编译:Settings→Build,Execution,Deployment→Compiler,勾选所有选项。

按下Ctrl+Shift+Alt+/,选择Register,将“compiler.automake.allow.when.app.running”和“actionSystem.assertFocusAccessFromEdt”勾选上。

重启IDEA测试热部署效果。

3.cloud-consumer-order80微服务消费者订单模块

在父工程下,创建cloud-consumer-order80的模块,修改pom.xml,注意,这里没有用到数据库相关操作,可以把mybatis,数据库驱动,druid的依赖去掉。

<?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>cloud2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>cloud-consumer-order80</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.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>

在resources中加入application.yml文件,通过server.port指定端口号为80。创建主启动类,将cloud-provider-payment8001的实体类复制到cloud-consumer-order80中。

RestTemplate提供了多种便捷访问远程HTTP服务的方法,是一种简单便捷的访问RESTful服务模块类,是Spring提供的用于访问REST服务的客户端模板工具集。使用RestTemplate访问RESTful接口非常方便,有3个参数:url,requestMap,ResponseBean.class,分别代表:请求地址,请求参数,响应体需要被转换成的对象类型。

编写配置类,并将RestTemplate加入到Spring容器中。

package com.atguigu.springcloud.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

创建controller类,使用RestTemplate调用cloud-provider-payment8001中的接口。

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
public class OrderController {
    public static final String PAYMENT_URL = "http://localhost:8001";
    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/consumer/payment/create")
    public CommonResult create(Payment payment) {
        return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id) {
        return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
    }
}

启动cloud-provider-payment8001和cloud-consumer-order80的主启动类,在idea右下角,可以看到一个提示信息,我们点击“Show run configuration in Run Dashboard”,发现窗口会有所改变,后序强烈推荐使用Run Dashboard。

正常情况下,idea检测到多个微服务,是会弹窗提示的,如果没有出现Run Dashboard相关的弹窗,double shift,输入Run Dashboard,就可以打开窗口了,也可以在View菜单下找一找,新版本的IDEA是Services,旧版本的IDEA是Run Dashboard。

通过浏览器访问http://localhost/consumer/payment/get/1,可以查询到结果。

再试一下插入数据:http://localhost/consumer/payment/create?serial=10003,提示插入成功,去数据库查看,但是发现serial的值为NULL。

解决方法是:在cloud-provider-payment8001的controller的create()方法中,给Payment参数加上@RequestBody注解。当request的Content-Type是application/json的时候,要加上@RequestBody,才能将请求参数转换成Java对象。

4.工程重构

通过观察,可以发现cloud-provider-payment8001和cloud-consumer-order80里有公共的entities包,这是冗余的代码,可以考虑将它提出来,做一下重构,后面公共的内容,也可以扔到这里面。

在父项目下,新建一个Maven模块cloud-api-commons。修改pom.xml,这里加入了一个hutool的工具包。

<?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>cloud2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>cloud-api-commons</artifactId>
    <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>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.0</version>
        </dependency>
    </dependencies>
</project>

将entities包拷贝到cloud-api-commons模块的java下。对cloud-api-commons模块,跳过测试,执行maven clean,maven install打包到本地仓库。

在cloud-provider-payment8001和cloud-consumer-order80中,将entities包删掉,在他们的pom.xml中引入如下依赖。

<dependency>
    <groupId>com.atguigu.springcloud</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>${project.version}</version>
</dependency>

启动两个模块进行测试,如果有错误,就修改错误,我这里碰到的错误是PaymentMapper.xml报错,提示entities找不到,修改了一下,就可以了。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值