Spring Cloud微服务实战 打造企业级优惠券系统(zui新14z)

208 阅读5分钟

Spring Cloud微服务实战 打造企业级优惠券系统

//xia栽ke呈 lexuecode点c0m

微服务实战 java 数据库连接池

  1. 什么是数据库连接池? 数据库连接池是一种维护数据库连接的技术,它允许应用程序在需要时从池中获取数据库连接,并在不需要连接时将其释放回池中。这种技术的主要目的是减少每次访问数据库时都要创建和销毁连接的开销,从而提高性能和资源利用率。

  2. 为什么需要数据库连接池? 数据库连接的创建和销毁是一项资源密集型操作,它涉及到网络通信和权限验证等操作,因此开销较大。在高并发的应用中,频繁地创建和销毁连接会导致系统性能下降,甚至引发连接泄漏等问题。数据库连接池的引入可以解决这些问题,具体好处包括:

资源重用:连接池可以重复使用现有的连接,避免了频繁创建和销毁连接的开销。 减少连接等待时间:连接池通常会预先创建一些连接,当应用程序需要连接时,可以立即获取可用连接,降低了连接等待时间。 连接管理:连接池负责连接的管理,包括连接的创建、销毁、超时检测等,减轻了开发人员的工作负担。 性能提升:通过连接池可以控制并发连接数,避免了数据库服务器被大量连接请求压垮。 3. JDBC 数据库连接池的实现 JDBC 数据库连接池通常由以下几个关键组件构成:

连接池管理器:用于管理连接的创建、分配、释放等操作。 连接池:实际存放数据库连接的容器。 连接对象:表示一个数据库连接的对象,包括连接信息、状态等。 连接池配置:包括最大连接数、最小连接数、连接超时时间等参数。 一些常见的 JDBC 数据库连接池实现包括 HikariCP、C3P0、DBCP 等。本文将以 HikariCP 为例进行介绍。

微服务实战 SpringBoot 配置注入的方式

Spring Boot配置文件和属性值注入

  • 内容输出来源:拉勾教育Java高新训练营

1. 全局配置文件

Spring Boot支持 application*.propertiesapplication*.yamlapplication*.yml三种拓展名结尾的全局配置文件;

  • 优先级:
    application*.properties>application*.yaml>application*.yml.

口说无凭,有源码为证,

在pom.xml文件中,点进去spring-boot-starter-parent,会发现这样的配置:

xml
复制代码
    <resource>
        <filtering>true</filtering>
        <directory>${basedir}/src/main/resources</directory>
        <includes>
          <include>**/application*.yml</include>
          <include>**/application*.yaml</include>
          <include>**/application*.properties</include>
        </includes>
      </resource>

1.1. application.properties配置文件

语法:

properties
复制代码
#配置数字
person.id=1
#配置字符串
person.name=tom
#配置List集合
person.hoby=吃饭,睡觉,打豆豆
#配置String[]数组
person.family=father,mother
#配置map集合
person.map.k1=v1
person.map.k2=v2
#配置对象type属性
person.pet.type=dog
#配置对象name属性
person.pet.name=旺财

1.2. application.y(a)ml配置文件

YAML文件格式是Spring Boot支持的一种JSON超集文件格式,相较于传统的Properties配置文件,YAML以数据为核心,是一种更为直观且更容易被电脑识别的数据序列化格式,application.yml的工作原理和application.properties是一样的。

  • YAML文件的拓展名可以是.yml或者.yaml
  • application.yml配置文件使用key:(空格)value格式配置属性,使用缩进控制层级关系
  1. value值为普通数据类型(例如:数字、字符串、布尔)
yml
复制代码
server:
  port: 8081
  path: /hello
  1. value值为数组或单列集合

    主要有两种写法:缩进式写法行内式写法;其中缩进式写法又有两种写法:

  • 缩进式写法一:
yml
复制代码
person:
  hobby:
    - play
    - read
    - sleep
  • 缩进式写法二:
yml
复制代码
person:
  hobby:
    play,
    read,
    sleep
  • 行内式写法:
yml
复制代码
person:
  hobby: [play,read,sleep]
  1. value值为Map或对象
  • 缩进式写法:
yml
复制代码
person:
  map:
    k1: v1
    k2: v2
  • 行内式写法:
yml
复制代码
person:
  map: {k1: v1, k2: v2}

2. 配置文件属性值注入

使用Spring Boot全局配置文件设置属性时,

  • 如果配置的属性是已有属性,例如服务端口server.port,那么Spring Boot会扫描并读取这些配置属性,覆盖已有的默认配置;
  • 如果配置的是自定义属性,则还需要在程序中注入这些配置属性方可生效。

Spring Boot提供了多种属性注入的方式,我下面来简单介绍两种,分别是是@ConfigurationProperties@Values注解。下面我们来分别使用这两种属性注入方式验证一下上文中的配置。

2.1. @ConfigurationProperties属性注入

@ConfigurationProperties注解可以将配置文件中的自定义属性批量的注入到某个Bean对象的多个对应的属性中。

!
复制代码
需要注意的是使用@ConfigurationProperties注解进行批量注入时,被注入的Bean比提供公有的setter方法,这是因为@ConfigurationProperties注解底层是使用setter方法进行赋值的。
    1. 编写需要被注入的类

    • Pet类
    java
    复制代码
    package com.lzx.springboot01demo.pojo;
    
    public class Pet {
        private String type;
        private String name;
     
        // 省略getter/setter方法
        // 省略toString方法
    }
    
    • Person类
    java
    复制代码
    package com.lzx.springboot01demo.pojo;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    
    @Component  // 将Person类作为bean注入到Spring容器中
    @ConfigurationProperties(prefix = "person")     // 将配置文件中以person开头的属性注入到该类中
    public class Person {
        private int id;
        private String name;
        private List hobby;
        private String[] family;
        private Map map;
        private Pet pet;
        
       	// 省略getter/setter方法
       	// 省略toString方法
    }
    
    1. 编写全局配置文件
properties
复制代码
#配置数字
person.id=1
#配置字符串
person.name=tom
#配置List集合
person.hobby=吃饭,睡觉,打豆豆
#配置String[]数组
person.family=father,mother
#配置map集合
person.map.k1=v1
person.map.k2=v2
#配置对象type属性
person.pet.type=dog
#配置对象name属性
person.pet.name=旺财

编写配置文件过程中发现,不会有任何的提示,这是因为编写的配置属性都是我们自定义的,要想使我们这些自定义的配置也有提示,需要引入一个Spring Boot提供的配置处理器依赖:

xml
复制代码
<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
    1. 总结

    • @ConfigurationProperties(prefix = "person")的作用是将配置文件中以person开头的属性注入到该类对应的属性中
    • @Component的作业是将Person类作为bean注入到Spring容器中,只有这样,才能被@ConfigurationProperties注解进行赋值

2.2. @Values属性注入

@ValueSpring框架提供的注解,用来读取配置文件中的属性并逐个注入到Bean对象对应的属性中,Spring Boot框架Spring框架@Value注解进行了默认继承。

@Value注解不仅可以将配置文件中的属性注入到对应的类的属性中,还可以直接给属性赋值,当然,这种方式一般情况下不常用,应为我们可以直接将属性初始化,何必用@Value注入多此一举呢。

!
复制代码
使用@Value注解注入不需要提供setter方法。
  • 使用@Value注入
java
复制代码
package com.lzx.springboot01demo.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Student {

    @Value("${person.id}")
    private int id;
    @Value("${person.name}")
    private String name;

    // 省略toString()
}
  • 测试
java
复制代码
package com.lzx.springboot01demo;

import com.lzx.springboot01demo.pojo.Student;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
class Springboot01demoApplicationTests {

    @Autowired
    private Student student;

    @Test
    public void testValue() {
        System.out.println(student);
    }
}
  • 测试打印结果
text
复制代码
Student{id=1, name='tom'}

3. 自定义配置

3.1. 使用@PropertySource加载配置文件

  • 自定义配置文件
properties
复制代码
test.id=100
test.name=lucy
  • 自定义属性注入类
java
复制代码
package com.lzx.springboot01demo.pojo;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration		// 自定义配置类
@PropertySource("classpath:test.properties") 	// 指定自定义配置文件位置和名称
@EnableConfigurationProperties(MyProperties.class)		// 开启对应配置类的属性注入功能
@ConfigurationProperties(prefix = "test")		// 指定配置文件注入属性前缀
public class MyProperties {

    private Integer id;
    private String name;

    // 省略getter/setter方法
    // 省略toString()方法
}
  • 测试
java
复制代码
	@Autowired
    private MyProperties myProperties;

    @Test
    public void testMyProperties() {
        System.out.println(myProperties);
    }
  • 打印结果
text
复制代码
MyProperties{id=100, name='lucy'}
  • 总结

    • @Configuration注解表示当前类是一个自定义配置类,并添加为Spring容器的组件,也可使用传统的@Component注解
    • @PropertySource("classpath:test.properties")指定自定义配置文件位置和名称
    • @ConfigurationProperties(prefix = "test")指定将配置文件中前缀为test的属性注入到配置类的属性中
    • @EnableConfigurationProperties(MyProperties.class)表示开启对应配置类的属性注入功能,如果配置类上使用的是@Component注解而非@Configuration@EnableConfigurationProperties(MyProperties.class)注解可以省略

3.2. 使用@Configuration编写自定义配置类

  • 编写自定义配置类
java
复制代码
package com.lzx.springboot01demo.config;

public class MyService {
}
java
复制代码
package com.lzx.springboot01demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {

    @Bean
    public MyService myService() {
        return new MyService();
    }
}
  • 测试
java
复制代码
	@Autowired
    private ApplicationContext applicationContext;

    @Test
    public void iocTest() {
        System.out.println(applicationContext.containsBean("myService"));
    }
  • 打印结果
text
复制代码
true
  • 总结

    • @Configuration注解表示这是一个自定义配置类,该配置类会被Spring Boot自动扫描识别
    • @Bean注解作用在myService()方法上,会将返回值new MyService()对象作为组件添加到Spring容器中,默认ID是方法名myService

4. 随机数设置和参数间引用

4.1. 随机数设置

Spring Boot配置文件中,可以使用Spring Boot内嵌的RandomValuePropertySource类进行随机值注入。

其语法为${random.xx}

properties
复制代码
# 配置随机值
my.secret=${random.value}
# 配置随机整数
my.number=${random.int}
# 配置随机long类型的整数
my.bigbumber=${random.long}
# 配置uuid
my.uuid=${random.uuid}
# 配置小于10的整数
my.number.less.than.ten=${random.int(10)}
# 配置范围在[1024,65536]的随机整数
my.number.in.range=${random.int[1024,65536]}

4.2. 参数间引用

Spring Boot配置文件中还可以进行参数间引用,也就是后一个属性配置,可以直接引用之前已经定义过的属性。

参数间引用的语法格式为${xx},其中xx便是之前定义好的属性名。

优惠券模板微服务业务思想

搭建项目结构

搭建项目结构 我把整个优惠券平台项目从Maven模块管理的角度划分为了多个模块。

在顶层项目geekbang-coupon之下有四个子模块,我先来分别解释下它们的功能:

coupon-template-serv: 创建、查找、克隆、删除优惠券模板; coupon-calculation-serv:计算优惠后的订单价格、试算每个优惠券的优惠幅度; coupon-customer-serv:通过调用template和calculation服务,实现用户领取优惠券、模拟计算最优惠的券、删除优惠券、下订单等操作; middleware:存放一些与业务无关的平台类组件。 在大型的微服务项目里,每一个子模块通常都存放在独立的Git仓库中,为了方便你下载代码,我把所有模块的代码都打包放到了这个代码仓库里,你可以在这里找到课程各阶段对应的源代码。

在每一个以“-serv”结尾的业务子模块中,我从内部分层的角度对其做了进一步拆分,以我们今天要搭建的coupon-template-serv为例,它内部包含了三个子模块:

coupon-template-api:存放公共POJO类或者对外接口的子模块; coupon-template-dao:存放数据库实体类和Dao层的子模块; coupon-template-impl:核心业务逻辑的实现层,对外提供REST API。 你会发现,我把coupon-template-api作为一个单独的模块,这样做的好处是:当某个上游服务需要获取coupon-template-serv的接口参数时,只要导入轻量级的coupon-template-api模块,就能够获取接口中定义的Request和Response的类模板,不需要引入多余的依赖项(比如Dao层或者Service层)。

这就是开闭原则的应用,它使各个模块间的职责和边界划分更加清晰,降低耦合的同时也更加利于依赖管理。

搭建好项目的结构之后,接下来我们借助Maven工具将需要的依赖包导入到项目中。

添加Maven依赖项 这里你要注意一下,添加Maven依赖项需要遵循“从上到下”的原则,也就是从顶层项目geekbang-coupon开始,顺藤摸瓜直到coupon-template-serv下的子模块。首先,我们来看看顶层geekbang-coupon依赖项的编写。

编写geekbang-coupon依赖项 geekbang-coupon是整个实战项目的顶层项目,它不用操心具体的业务逻辑,只用完成一个任务:管理子模块和定义Maven依赖项的版本。这就像一个公司的大boss一样,只用制定方向战略,琐碎的业务就交给下面人(子模块)来办就好了。