5. SpringCloudAlibaba 实践笔记:项目微服务开发
设计完毕后,对各服务进行业务实现
用户微服务开发
引入依赖
在 shop-user 的 pom.xml 文件里添加如下 shop-bean 依赖,shop-bean 模块包含着微服务的各种实体类。
<dependencies>
<dependency>
<groupId>io.binghe.shop</groupId>
<artifactId>shop-bean</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
配置应用
在项目的 resources 目录下创建 application.yml 文件,并在 application.yml 文件中添加如下配置。
server:
port: 8060
servlet:
context-path: /user
spring:
application:
name: server-user
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/shop?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: root
platform: mysql
type: com.alibaba.druid.pool.DruidDataSource
# 下面为连接池的补充设置,应用到上面所有数据源中
# 初始化大小,最小,最大
initialSize: 10
minIdle: 5
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 3600000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 3600000
validationQuery: select 1 from dual
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
maxOpenPreparedStatements: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat
http:
encoding:
enabled: true
charset: UTF-8
force: true
mybatis-plus:
global-config:
db-config:
id-type: auto
field-strategy: not-empty
table-underline: true
db-type: oracle
logic-delete-value: 1
logic-not-delete-value: 0
mapper-locations: classpath:/mapper/*.xml
configuration:
jdbc-type-for-null: 'null'
持久层开发
package com.shop.user.mapper;
public interface UserMapper extends BaseMapper<User> {
}
在resources目录下创建mapper目录里创建UserMapper.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="me.chan.shop.user.mapper.UserMapper">
</mapper>
- UserMapper.xml 文件与 UserMapper 接口是一一对应的,UserMapper 中定义的接口方法,都会在 UserMapper.xml 文件中写对应的 SQL 语句。
业务逻辑层开发
创建 UserService 接口
package com.shop.user.service;
public interface UserService {
/**
* 根据id获取用户信息
*/
User getUserById(Long userId);
}
创建 UserServiceImpl 类,实现 UserService 接口
package com.shop.user.service.impl;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User getUserById(Long userId) {
return userMapper.selectById(userId);
}
}
接口层开发
创建 UserController 类
package com.shop.user.controller;
@Slf4j
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping(value = "/get/{uid}")
public User getUser(@PathVariable("uid") Long uid){
User user = userService.getUserById(uid);
log.info("获取到的用户信息为:{}", JSONObject.toJSONString(user));
return user;
}
}
服务启动类
package com.shop.user;
@SpringBootApplication
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan(value = { "me.chan.shop.user.mapper" })
public class UserStarter {
public static void main(String[] args){
SpringApplication.run(UserStarter.class, args);
}
}
商品微服务开发
引入依赖
在 shop-product 的 pom.xml 文件里添加如下 shop-bean 依赖,shop-bean 模块包含着微服务的各种实体类。
<dependencies>
<dependency>
<groupId>io.binghe.shop</groupId>
<artifactId>shop-bean</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
配置应用
在项目的 resources 目录下创建 application.yml 文件,并在 application.yml 文件中添加如下配置。
以下是对上述配置文件添加参数注释后的内容:
```yaml
server:
# 应用服务端口号
port: 8070
servlet:
# 应用的上下文路径
context-path: /product
spring:
application:
# 应用名称
name: server-product
datasource:
# 数据库驱动类名
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据库连接地址
url: jdbc:mysql://localhost:3306/shop?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
# 数据库用户名
username: root
# 数据库密码
password: root
# 数据库平台类型
platform: mysql
# 数据源类型
type: com.alibaba.druid.pool.DruidDataSource
# 下面为连接池的补充设置,应用到上面所有数据源中
# 连接池初始化大小
initialSize: 10
# 连接池最小空闲连接数
minIdle: 5
# 连接池最大活动连接数
maxActive: 20
# 配置获取连接等待超时的时间(单位:毫秒)
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接(单位:毫秒)
timeBetweenEvictionRunsMillis: 3600000
# 配置一个连接在池中最小生存的时间(单位:毫秒)
minEvictableIdleTimeMillis: 3600000
# 用于检测连接是否有效的查询语句
validationQuery: select 1 from dual
# 在空闲时是否检测连接
testWhileIdle: true
# 在获取连接时是否检测连接
testOnBorrow: false
# 在归还连接时是否检测连接
testOnReturn: false
# 是否打开PSCache(预编译语句缓存),并指定每个连接上PSCache的大小
poolPreparedStatements: true
# 每个连接上PSCache的大小
maxPoolPreparedStatementPerConnectionSize: 20
# 最大打开的预编译语句数量
maxOpenPreparedStatements: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat
http:
encoding:
# 是否启用编码
enabled: true
# 编码字符集
charset: UTF-8
# 是否强制使用指定编码
force: true
mybatis-plus:
global-config:
db-config:
# 实体类中主键的生成策略
id-type: auto
# 字段验证策略
field-strategy: not-empty
# 是否开启驼峰转下划线
table-underline: true
# 数据库类型
db-type: oracle
# 逻辑删除值
logic-delete-value: 1
# 逻辑未删除值
logic-not-delete-value: 0
mapper-locations: classpath:/mapper/*.xml
configuration:
# 当属性为null时,映射的JDBC类型
jdbc-type-for-null: 'null'
持久层开发
创建 ProductMapper 接口
package com.shop.product.mapper;
public interface ProductMapper extends BaseMapper<User> {
}
在 resources 目录下创建 mapper 目录里创建 ProductMapper.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.shop.product.mapper.ProductMapper">
</mapper>
- ProductMapper.xml 文件与 ProductMapper 接口是一一对应的,ProductMapper 中定义的接口方法,都会在 ProductMapper.xml 文件中写对应的 SQL 语句。
业务逻辑层开发
创建 ProductService 接口
package com.shop.product.service;
public interface ProductService{
/**
* 根据商品id获取商品信息
*/
Product getProductById(Long pid);
/**
* 扣减商品库存
*/
int updateProductStockById(Integer count, Long id);
}
创建 ProductServiceImpl 类,实现 ProductService 接口
@Service
package com.shop.product.service.impl;
public class ProductServiceImpl implements ProductService{
@Autowired
private ProductMapper productMapper;
@Override
public Product getProductById(Long pid) {
return productMapper.selectById(pid);
}
@Override
public int updateProductStockById(Integer count, Long id) {
return productMapper.updateProductStockById(count, id);
}
}
接口层开发
package com.shop.product.controller;
@RestController
@Slf4j
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping(value = "/get/{pid}")
public Product getProduct(@PathVariable("pid") Long pid){
Product product = productService.getProductById(pid);
log.info("获取到的商品信息为:{}", JSONObject.toJSONString(product));
return product;
}
@GetMapping(value = "/update_count/{pid}/{count}")
public Result<Integer> updateCount(@PathVariable("pid") Long pid, @PathVariable("count") Integer count){
log.info("更新商品库存传递的参数为: 商品id:{}, 购买数量:{} ", pid, count);
int updateCount = productService.updateProductStockById(count, pid);
Result<Integer> result = new Result<>(HttpCode.SUCCESS, "执行成功", updateCount);
return result;
}
}
服务启动类
package com.shop.product;
@SpringBootApplication
@MapperScan(value = { "me.chan.shop.product.mapper" })
@EnableTransactionManagement(proxyTargetClass = true)
public class ProductStarter {
public static void main(String[] args){
SpringApplication.run(ProductStarter.class, args);
}
}
订单微服务开发
引入依赖
在 shop-order 的 pom.xml 文件里添加如下 shop-bean 依赖,shop-bean 模块包含着微服务的各种实体类。
<dependencies>
<dependency>
<groupId>me.chan</groupId>
<artifactId>shop-bean</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
配置应用
在项目的 resources 目录下创建 application.yml 文件,并在 application.yml 文件中添加如下配置。
server:
port: 8080
servlet:
context-path: /order
spring:
application:
name: server-user
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/shop?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: root
platform: mysql
type: com.alibaba.druid.pool.DruidDataSource
# 下面为连接池的补充设置,应用到上面所有数据源中
# 初始化大小,最小,最大
initialSize: 10
minIdle: 5
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 3600000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 3600000
validationQuery: select 1 from dual
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
maxOpenPreparedStatements: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat
http:
encoding:
enabled: true
charset: UTF-8
force: true
mybatis-plus:
global-config:
db-config:
id-type: auto
field-strategy: not-empty
table-underline: true
db-type: oracle
logic-delete-value: 1
logic-not-delete-value: 0
mapper-locations: classpath:/mapper/*.xml
configuration:
jdbc-type-for-null: 'null'
持久层开发
创建 OrderMapper 和 OrderItemMapper 接口
public interface OrderMapper extends BaseMapper<Order> {
}
public interface OrderItemMapper extends BaseMapper<OrderItem> {
}
在 resources 目录下创建 mapper 目录里创建 OrderMapper.xml 文件和 OrderItemMapper.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.shop.order.mapper.OrderMapper">
</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.shop.order.mapper.OrderItemMapper">
</mapper>
业务逻辑层开发
创建OrderService接口:
package com.shop.order.service;
public interface OrderService {
/**
* 保存订单
*/
void saveOrder(OrderParams orderParams);
}
创建 OrderServiceImpl 类,实现 OrderService 接口
package com.shop.order.service.impl;
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Autowired
private RestTemplate restTemplate;
@Override
@Transactional(rollbackFor = Exception.class)
public void saveOrder(OrderParams orderParams) {
if (orderParams.isEmpty()){
throw new RuntimeException("参数异常: " + JSONObject.toJSONString(orderParams));
}
User user = restTemplate.getForObject("http://localhost:8060/user/get/" + orderParams.getUserId(), User.class);
if (user == null){
throw new RuntimeException("未获取到用户信息: " + JSONObject.toJSONString(orderParams));
}
Product product = restTemplate.getForObject("http://localhost:8070/product/get/" + orderParams.getProductId(), Product.class);
if (product == null){
throw new RuntimeException("未获取到商品信息: " + JSONObject.toJSONString(orderParams));
}
if (product.getProStock() < orderParams.getCount()){
throw new RuntimeException("商品库存不足: " + JSONObject.toJSONString(orderParams));
}
Order order = new Order();
order.setAddress(user.getAddress());
order.setPhone(user.getPhone());
order.setUserId(user.getId());
order.setUsername(user.getUsername());
order.setTotalPrice(product.getProPrice().multiply(BigDecimal.valueOf(orderParams.getCount())));
orderMapper.insert(order);
OrderItem orderItem = new OrderItem();
orderItem.setNumber(orderParams.getCount());
orderItem.setOrderId(order.getId());
orderItem.setProId(product.getId());
orderItem.setProName(product.getProName());
orderItem.setProPrice(product.getProPrice());
orderItemMapper.insert(orderItem);
Result<Integer> result = restTemplate.getForObject("http://localhost:8070/product/update_count/" + orderParams.getProductId() + "/" + orderParams.getCount(), Result.class);
if (result.getCode() != HttpCode.SUCCESS){
throw new RuntimeException("库存扣减失败");
}
log.info("库存扣减成功");
}
}
在saveOrder()方法的实现中,实现的主要逻辑如下。
- 判断orderParams封装的参数是否为空,如果参数为空,则抛出参数异常。
- 通过RestTemplate调用用户微服务获取用户的基本信息,如果获取的用户信息为空,则抛出未获取到用户信息的异常。
- 通过RestTemplate调用商品微服务获取商品的基本信息,如果获取的商品信息为空,则抛出未获取到商品信息的异常。
- 判断商品的库存是否小于待扣减的商品数量,如果商品的库存小于待扣减的商品数量,则抛出商品库存不足的异常。
- 如果orderParams封装的参数不为空,并且获取的用户信息和商品信息不为空,同时商品的库存充足,则创建订单对象保存订单信息,创建订单条目对象,保存订单条目信息。
- 调用商品微服务的接口扣减商品库存。
接口层开发
创建 OrderController 类
package com.shop.order.controller;
@Slf4j
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping(value = "/submit_order")
public String submitOrder(@RequestBody OrderParams orderParams){
log.info("提交订单时传递的参数:{}", JSONObject.toJSONString(orderParams));
orderService.saveOrder(orderParams);
return "success";
}
}
服务启动类
package com.shop.order;
@SpringBootApplication
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan(value = { "me.chan.shop.order.mapper" })
public class OrderStarter {
public static void main(String[] args){
SpringApplication.run(OrderStarter.class, args);
}
}
配置类
由于订单服务需要调用用户微服务和商品微服务,所以需要添加 RestTemplate 来进行远程服务调用,所以需要在配置类中添加 RestTemplate Bean,通过依赖注入方式注入到 OrderService,供 OrderService 调用其他微服务。
package com.shop.order.config
@Configuration
public class LoadBalanceConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}

浙公网安备 33010602011771号