From ba3e36dca9e5ddf8194e3b3b506f4f910d3c6c5f Mon Sep 17 00:00:00 2001 From: luoxiang <2806718453@qq.com> Date: Sun, 25 Aug 2019 17:44:01 +0800 Subject: [PATCH] spring boot --- .../spring-boot-session/README.md | 319 ++++---- distributed-solution/spring-session/README.md | 392 +++++----- spring-boot/spring-boot-actuator/README.md | 641 ++++++++------- spring-boot/spring-boot-base/README.md | 502 ++++++------ spring-boot/spring-boot-data-jpa/README.md | 405 +++++----- .../spring-boot-druid-mybatis/README.md | 412 +++++----- spring-boot/spring-boot-dubbo/README.md | 613 ++++++--------- spring-boot/spring-boot-jsp/README.md | 175 ++--- spring-boot/spring-boot-kafka/README.md | 728 ++++++++--------- spring-boot/spring-boot-memcached/README.md | 310 ++++---- spring-boot/spring-boot-mongodb/README.md | 386 +++++---- spring-boot/spring-boot-mybatis/README.md | 510 ++++++------ spring-boot/spring-boot-rabbitmq/README.md | 185 +---- spring-boot/spring-boot-redis/README.md | 735 ++++++------------ spring-boot/spring-boot-servlet/README.md | 477 ++++++------ spring-boot/spring-boot-swagger2/README.md | 522 ++++++------- spring-boot/spring-boot-tomcat/README.md | 226 +++--- spring-boot/spring-boot-websocket/README.md | 388 ++++----- spring-boot/spring-boot-yml-profile/README.md | 371 +++++---- .../springboot-druid-mybatis-multi/README.md | 123 ++- 20 files changed, 3896 insertions(+), 4524 deletions(-) diff --git a/distributed-solution/spring-boot-session/README.md b/distributed-solution/spring-boot-session/README.md index 14eb459..ccbe95d 100644 --- a/distributed-solution/spring-boot-session/README.md +++ b/distributed-solution/spring-boot-session/README.md @@ -1,163 +1,160 @@ -# spring boot 实现分布式 session - -## 目录
+# Spring Boot 实现分布式 Session + -## 一、项目结构 - -
- - - -## 二、分布式session的配置 - -#### 2.1 引入依赖 - -```xml - - - org.springframework.boot - spring-boot-starter-data-redis - - - org.springframework.session - spring-session-data-redis - -``` - -#### 2.2 Redis配置 - -```yaml -spring: - redis: - host: 127.0.0.1 - port: 6379 - jedis: - pool: - # 连接池最大连接数,使用负值表示无限制。 - max-active: 8 - # 连接池最大阻塞等待时间,使用负值表示无限制。 - max-wait: -1s - # 连接池最大空闲数,使用负值表示无限制。 - max-idle: 8 - # 连接池最小空闲连接,只有设置为正值时候才有效 - min-idle: 1 - timeout: 300ms - session: - # session 存储方式 支持 redis、mongo、jdbc、hazelcast - store-type: redis - -# 如果是集群节点 采用如下配置指定节点 -#spring.redis.cluster.nodes - -``` - -有两点需要特别说明: - -1. spring-session 不仅提供了 redis 作为公共 session 存储的方案,同时也支持 jdbc、mongodb、Hazelcast 等作为公共 session 的存储,可以用 session.store-type 指定; -2. 对于 redis 存储方案而言,官方也提供了不止一种整合方式,这里我们选取的整合方案是 jedis 客户端作为连接,当然也可以使用 Lettuce 作为客户端连接。 - -#### 2.3 启动类上添加@EnableRedisHttpSession 注解开启 spring-session-redis 整合方案的自动配置 - -```java -@SpringBootApplication -@EnableRedisHttpSession(maxInactiveIntervalInSeconds= 1800) //开启 redis session 支持,并配置 session 过期时间 -public class SpringBootSessionApplication { - - public static void main(String[] args) { - SpringApplication.run(SpringBootSessionApplication.class, args); - } - -} -``` - - - -## 三、验证分布式session - -#### 3.1 创建测试controller和测试页面 - -```java -@Controller -public class LoginController { - - @RequestMapping - public String index() { - return "index"; - } - - @RequestMapping("home") - public String home() { - return "home"; - } - - @PostMapping("login") - public String login(User user, HttpSession session) { - // 随机生成用户 id - user.setUserId(Math.round(Math.floor(Math.random() * 10 * 1000))); - // 将用户信息保存到 id 中 - session.setAttribute("USER", user); - return "home"; - } - -} -``` - -登录页面 index.ftl: - -```jsp - - - - 登录页面 - - -
- 用户:
- 密码:
- -
- - -``` - -session 信息展示页面 home.ftl: - -```jsp - - - - 主页面 - - -
登录用户: ${Session["USER"].username}
-
用户编号: ${Session["USER"].userId}
- - -``` - -#### 3.2 启动项目 - -由于我们这里采用的是 spring boot 的内置容器作为 web 容器,所以直接启动两个实例测试即可。 - -应用 1 启动配置: - -
- -应用 2 启动配置,需要用 `--server.port ` 指定不同的端口号: - -
- -**测试结果:** - -
- -
+## 一、项目结构 + +
+ + +## 二、实现分布式 Session + +### 2.1 基本依赖 + +```xml + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.session + spring-session-data-redis + +``` + +### 2.2 实现原理 + +Spring 通过将 Session 信息存储到公共容器中,这样不同的 Web 服务就能共享到相同的 Session 信息,从而实现分布式 Session。Spring 支持使用 Redis, Jdbc,mongodb,Hazelcast 等作为公共的存储容器,可以在配置文件中使用参数 session.store-type 进行指定。 + +这里我们以 Redis 作为公共的存储容器,配置如下。同时对于 Redis 存储方案,官方提供了 Jedis 和 Lettuce 两种客户端连接,这里我们选用的是 Jedis 连接: + +```yaml +spring: + redis: + host: 127.0.0.1 + port: 6379 + jedis: + pool: + # 连接池最大连接数,使用负值表示无限制。 + max-active: 8 + # 连接池最大阻塞等待时间,使用负值表示无限制。 + max-wait: -1s + # 连接池最大空闲数,使用负值表示无限制。 + max-idle: 8 + # 连接池最小空闲连接,只有设置为正值时候才有效 + min-idle: 1 + timeout: 300ms + session: + # session 存储方式 支持 redis、mongo、jdbc、hazelcast + store-type: redis + +# 如果是集群节点 采用如下配置指定节点 +#spring.redis.cluster.nodes +``` + +### 2.3 自动配置 + +在启动类上添加 @EnableRedisHttpSession 开启 spring-session-redis 整合方案的自动配置: + +```java +@SpringBootApplication +@EnableRedisHttpSession(maxInactiveIntervalInSeconds= 1800) //开启 redis session 支持,并配置 session 过期时间 +public class SpringBootSessionApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootSessionApplication.class, args); + } + +} +``` + + + +## 三、验证分布式 Session + +### 3.1 测试准备 + +创建测试接口和测试页面: + +```java +@Controller +public class LoginController { + + @RequestMapping + public String index() { + return "index"; + } + + @RequestMapping("home") + public String home() { + return "home"; + } + + @PostMapping("login") + public String login(User user, HttpSession session) { + // 随机生成用户 id + user.setUserId(Math.round(Math.floor(Math.random() * 10 * 1000))); + // 将用户信息保存到 id 中 + session.setAttribute("USER", user); + return "home"; + } + +} +``` + +登录页面 index.ftl: + +```jsp + + + + 登录页面 + + +
+ 用户:
+ 密码:
+ +
+ + +``` + +Session 信息展示页面 home.ftl: + +```jsp + + + + 主页面 + + +
登录用户: ${Session["USER"].username}
+
用户编号: ${Session["USER"].userId}
+ + +``` + +### 3.2 测试结果 + +这里我采用的是 Spring Boot 的内置的 Web 容器,直接启动两个实例测试即可: + +应用 1 启动配置: + +
+应用 2 启动配置,需要用 `--server.port ` 指定不同的端口号: + +
+**测试结果:** + +
+
diff --git a/distributed-solution/spring-session/README.md b/distributed-solution/spring-session/README.md index 9dda11c..4ad7f09 100644 --- a/distributed-solution/spring-session/README.md +++ b/distributed-solution/spring-session/README.md @@ -1,200 +1,196 @@ -# spring session 实现分布式 session - -## 目录
+# Spring 实现分布式 Session + -## 一、项目结构 - -分布式 session 主要配置文件为 spring-session.xml 和 web.xml,其他的配置为标准的 web 工程的配置。 - -
- -## 二、分布式session的配置 - -#### 2.1 引入依赖 - -```xml - - - org.springframework.data - spring-data-redis - 2.1.3.RELEASE - - - redis.clients - jedis - 2.9.0 - - - org.springframework.session - spring-session-data-redis - 2.1.3.RELEASE - -``` - -#### 2.2 在web.xml中配置session拦截器 - -```xml - - - springSessionRepositoryFilter - org.springframework.web.filter.DelegatingFilterProxy - - - springSessionRepositoryFilter - /* - - -``` - -#### 2.3 创建配置文件spring- session.xml,配置redis连接 - -有两点需要特别说明: - -1. spring-session 不仅提供了 redis 作为公共 session 存储的方案,同时也支持 jdbc、mongodb、Hazelcast 等作为公共 session 的存储; -2. 对于 redis 存储方案而言,官方也提供了不止一种整合方式,这里我们选取的整合方案是 jedis 客户端作为连接,当然也可以使用 Lettuce 作为客户端连接。 - -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -## 三、验证分布式session - -#### 3.1 创建测试controller和测试页面 - -```java -@Controller -public class LoginController { - - @RequestMapping - public String index(){ - return "index"; - } - - @RequestMapping("home") - public String home(){ - return "home"; - } - - @PostMapping("login") - public String login(User user, HttpSession session, HttpServletRequest request, Model model){ - // 随机生成用户 id - user.setUserId(Math.round(Math.floor(Math.random() *10*1000))); - // 将用户信息保存到 id 中 - session.setAttribute("USER",user); - return "redirect:home"; - } - -} -``` - -登录页面: - -```jsp -<%@ page contentType="text/html;charset=UTF-8" language="java" %> - - - 登录页面 - - -
服务器:<%=request.getServerName()+":"+request.getServerPort()%>
-
- 用户:
- 密码:
- -
- - -``` - -session 信息展示页面 (home.jsp): - -```jsp -<%@ page contentType="text/html;charset=UTF-8" language="java" %> - - - 主页面 - - -
服务器:<%=request.getServerName()+":"+request.getServerPort()%>
-
登录用户: ${sessionScope.USER.username}
-
用户编号: ${sessionScope.USER.userId}
- - -``` - -#### 3.2 启动项目 - -这里我们采用两个 tomcat 分别启动项目,在第一个项目 index.jsp 页面进行登录,第二个项目不登录,直接访问 session 展示页(home.jsp) - -tomcat 1 配置: - -
- -tomcat 2 配置: - -
- -**测试结果:** - -
- -
+## 一、项目结构 + +分布式 Session 主要配置文件为 spring-session.xml 和 web.xml,其他的配置为标准的 web 工程的配置: + +
+## 二、实现分布式 Session + +### 2.1 基本依赖 + +```xml + + + org.springframework.data + spring-data-redis + 2.1.3.RELEASE + + + redis.clients + jedis + 2.9.0 + + + org.springframework.session + spring-session-data-redis + 2.1.3.RELEASE + +``` + +### 2.2 Session 拦截器 + +在 web.xml 中配置 Session 拦截器: + +```xml + + + springSessionRepositoryFilter + org.springframework.web.filter.DelegatingFilterProxy + + + springSessionRepositoryFilter + /* + + +``` + +### 2.3 实现原理 + +Spring 通过将 Session 信息存储到公共容器中,这样不同的 Web 服务就能共享到相同的 Session 信息,从而实现分布式 Session。Spring 支持使用 Redis, Jdbc,mongodb,Hazelcast 等作为公共的存储容器。这里我们以 Redis 作为公共的存储容器,需要创建配置文件 spring- session.xml,内容如下: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +## 三、验证分布式 Session + +### 3.1 测试准备 + +创建测试接口和测试页面: + +```java +@Controller +public class LoginController { + + @RequestMapping + public String index(){ + return "index"; + } + + @RequestMapping("home") + public String home(){ + return "home"; + } + + @PostMapping("login") + public String login(User user, HttpSession session, HttpServletRequest request, Model model){ + // 随机生成用户 id + user.setUserId(Math.round(Math.floor(Math.random() *10*1000))); + // 将用户信息保存到 id 中 + session.setAttribute("USER",user); + return "redirect:home"; + } + +} +``` + +登录页面: + +```jsp +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + 登录页面 + + +
服务器:<%=request.getServerName()+":"+request.getServerPort()%>
+
+ 用户:
+ 密码:
+ +
+ + +``` + +session 信息展示页面 (home.jsp): + +```jsp +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + 主页面 + + +
服务器:<%=request.getServerName()+":"+request.getServerPort()%>
+
登录用户: ${sessionScope.USER.username}
+
用户编号: ${sessionScope.USER.userId}
+ + +``` + +### 3.2 测试结果 + +这里采用两个 Tomcat 分别启动项目,在第一个项目的 index.jsp 页面进行登录;第二个项目不登录,直接访问 Session 展示页home.jsp : + +Tomcat 1 配置: + +
+Tomcat 2 配置: + +
+**测试结果:** + +
+
diff --git a/spring-boot/spring-boot-actuator/README.md b/spring-boot/spring-boot-actuator/README.md index ae448a1..84494b8 100644 --- a/spring-boot/spring-boot-actuator/README.md +++ b/spring-boot/spring-boot-actuator/README.md @@ -1,329 +1,320 @@ -# spring boot actuator - -## 目录
-一、用例涉及到的概念综述
-    1.1 端点
-    1.2 启用端点
-    1.3 暴露端点
-    1.4 健康检查信息
+# Spring Boot Actuator + + + +## 一、相关概念 + +### 1.1 执行端点 + +Spring Boot 提供了很多执行器端点(endpoints)用于监控应用的运行情况以及与应用进行交互,并支持将这些端点按需暴露给外部使用。 端点暴露的方式取决于你采用的技术类型,通常可以端点的 ID 映射到一个 URL,从而可以将端口暴露为 HTTP 服务。例如,将health 端点默认映射到 /health。Spring Boot 内置的常用端点如下: + +| ID | 描述 | 是否敏感 | +| ----------- | ------------------------------------------------------------ | -------- | +| actuator | 为其他端点提供基于超文本的导航页面,需要添加 Spring HATEOAS 依赖 | true | +| autoconfig | 显示一个自动配置类的报告,该报告展示所有自动配置候选者及它们被应用或未被应用的原因 | true | +| beans | 显示一个应用中所有 Spring Beans 的完整列表 | true | +| configprops | 显示一个所有 @ConfigurationProperties 的集合列表 | true | +| dump | 执行一个线程转储 | true | +| env | 暴露来自 Spring ConfigurableEnvironment 的属性 | true | +| flyway | 显示数据库迁移路径,如果有的话 | true | +| health | 展示应用的健康信息(当使用一个未认证连接访问时显示一个简单的 'status',使用认证连接访问则显示全部信息详情) | false | +| info | 显示任意的应用信息 | false | +| liquibase | 展示任何 Liquibase 数据库迁移路径,如果有的话 | true | +| metrics | 展示当前应用的 'metrics' 信息 | true | +| mappings | 显示一个所有 @RequestMapping 路径的集合列表 | true | +| shutdown | 允许应用以优雅的方式关闭(默认情况下不启用) | true | +| trace | 显示 trace 信息(默认为最新的 100 条 HTTP 请求) | true | + +如果使用了 Spring MVC,还有以下额外的端点: + +| ID | 描述 | 是否敏感 | +| -------- | ------------------------------------------------------------ | -------- | +| docs | 展示 Actuator 的文档,包括示例请求和响应,需添加 spring-boot-actuator-docs 依赖 | false | +| heapdump | 返回一个 GZip 压缩的 hprof 堆转储文件 | true | +| jolokia | 通过 HTTP 暴露 JMX beans(依赖 Jolokia) | true | +| logfile | 返回日志文件内容(如果设置 logging.file 或 logging.path 属性),支持使用 HTTP Range 头接收日志文件内容的部分信息 | | + +端点按照安全属性可以分为敏感和非敏感两类,在启用 Web 安全服务后,访问敏感端点时需要提供用户名和密码,如果没有启用 web 安全服务,Spring Boot 可能会直接禁止访问该端点。 + + + +### 1.2 启用端点 + +默认情况下,除了 shutdown 以外的所有端点都已启用。端点的启停可以使用 management.endpoint.\.enabled 属性来进行控制,示例如下: + +```properties +management.endpoint.shutdown.enabled = true +``` + + + +### 1.3 暴露端点 + +由于端点可能包含敏感信息,因此应仔细考虑后再决定是否公开。下表显示了内置端点的默认公开情况: + +| ID | JMX | Web | +| -------------- | ----- | ---- | +| auditevents | 是 | 没有 | +| beans | 是 | 没有 | +| conditions | 是 | 没有 | +| configprops | 是 | 没有 | +| env | 是 | 没有 | +| flyway | 是 | 没有 | +| health | 是 | 是 | +| heapdump | N / A | 没有 | +| httptrace | 是 | 没有 | +| info | 是 | 是 | +| jolokia | N / A | 没有 | +| logfile | N / A | 没有 | +| loggers | 是 | 没有 | +| liquibase | 是 | 没有 | +| metrics | 是 | 没有 | +| mappings | 是 | 没有 | +| prometheus | N / A | 没有 | +| scheduledtasks | 是 | 没有 | +| sessions | 是 | 没有 | +| shutdown | 是 | 没有 | +| threaddump | 是 | 没有 | + +可以选择是否暴露端点(include)或者排除端点(exclude),其中排除属性优先于暴露属性: + +| 属性 | 默认 | +| ----------------------------------------- | ------------ | +| management.endpoints.jmx.exposure.exclude | | +| management.endpoints.jmx.exposure.include | * | +| management.endpoints.web.exposure.exclude | | +| management.endpoints.web.exposure.include | info, health | + + + +### 1.4 健康检查 + +health 端点用于暴露程序运行的健康状态,暴露的信息的详细程度由 management.endpoint.health.show-details 来控制,它具有以下三个可选值: + +| 名称 | 描述 | +| --------------- | ------------------------------------------------------------ | +| never | 细节永远不会显示。 | +| when-authorized | 详细信息仅向授权用户显示。授权角色可以使用配置 management.endpoint.health.roles。 | +| always | 详细信息显示给所有用户。 | + + + +## 二、项目说明 + +### 2.1 项目结构 + +- **CustomHealthIndicator** 自定义健康指标; +- **CustomHealthAggregator**:自定义健康状态聚合规则; +- **CustomEndPoint**:自定义端点。 + +
+### 2.2 主要依赖 + +```xml + + org.springframework.boot + spring-boot-starter-actuator + +``` + +### 2.3 项目配置 + +```yaml +management: + endpoints: + web: + exposure: + # 这里用* 代表暴露所有端点只是为了观察效果,实际中按照需进行端点暴露 + include: "*" + endpoint: + health: + # 详细信息显示给所有用户。 + show-details: always + health: + status: + http-mapping: + # 自定义健康检查返回状态码对应的 http 状态码 + FATAL: 503 +``` + +### 2.4 监控状态 + +导入 Actuator 的 starter 并进行配置后,访问 http://127.0.0.1:8080/actuator/health 就可以看到对应的项目监控状态。 + +
+健康指标 HealthIndicators 由 Spring Boot 自动配置,因此这里显示监控信息是由项目所使用的技术栈而决定的: + +| 名称 | 描述 | +| ------------------------------------------------------------ | -------------------------------- | +| [CassandraHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraHealthIndicator.java) | 检查 Cassandra 数据库是否启动。 | +| [DiskSpaceHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/system/DiskSpaceHealthIndicator.java) | 检查磁盘空间不足。 | +| [DataSourceHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jdbc/DataSourceHealthIndicator.java) | 检查是否可以获得连接 DataSource。 | +| [ElasticsearchHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchHealthIndicator.java) | 检查 Elasticsearch 集群是否启动。 | +| [InfluxDbHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/influx/InfluxDbHealthIndicator.java) | 检查 InfluxDB 服务器是否启动。 | +| [JmsHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jms/JmsHealthIndicator.java) | 检查 JMS 代理是否启动。 | +| [MailHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/mail/MailHealthIndicator.java) | 检查邮件服务器是否启动。 | +| [MongoHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/mongo/MongoHealthIndicator.java) | 检查 Mongo 数据库是否启动。 | +| [Neo4jHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicator.java) | 检查 Neo4j 服务器是否启动。 | +| [RabbitHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/amqp/RabbitHealthIndicator.java) | 检查 Rabbit 服务器是否启动。 | +| [RedisHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisHealthIndicator.java) | 检查 Redis 服务器是否启动。 | +| [SolrHealthIndicator](https://github.com/spring-projects/spring-boot/tree/v2.0.1.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/solr/SolrHealthIndicator.java) | 检查 Solr 服务器是否已启动。 | + + + +## 三、自定义健康检查指标 + +```java +/** + * @description : 自定义健康检查指标 + */ +@Component +public class CustomHealthIndicator implements HealthIndicator { + + @Override + public Health health() { + double random = Math.random(); + // 这里用随机数模拟健康检查的结果 + if (random > 0.5) { + return Health.status("FATAL").withDetail("error code", "某健康专项检查失败").build(); + } else { + return Health.up().withDetail("success code", "自定义检查一切正常").build(); + } + + } +} +``` + +自定义检查通过的情况下: + +
+自定义检查失败的情况: + +
+ +自定义检查不论是否通过都不会影响整体的 status,因此两种情况下的 status 值都是 `up`。如果想通过自定义检查去影响整体的检查结果,比如健康检查针对的是支付业务,在支付业务的不可用的情况下,我们就应该认为整个服务是不可用的,这个时候就需要通过自定义健康状态的聚合规则来实现。 + + + +## 四、自定义健康状态聚合规则 + +```java +/** + * @description : 对所有的自定义健康指标进行聚合,按照自定义规则返回总的健康状态 + */ +@Component +public class CustomHealthAggregator implements HealthAggregator { + + @Override + public Health aggregate(Map healths) { + for (Health health : healths.values()) { + // 聚合规则可以自定义,这里假设我们自定义的监控状态中有一项 FATAL,就认为整个服务都是不可用的,否则认为整个服务是可用的 + if (health.getStatus().getCode().equals("FATAL")) { + return Health.status("FATAL").withDetail("error code", "综合判断后服务宕机").build(); + } + } + return Health.up().build(); + } +} +``` + +当我们自定义健康检查项不通过时候的结果如下: + +
+这里需要注意的是返回自定义的聚合状态时,状态码也变成了 503,这是我们在配置文件中进行定义的: + +```properties +management.health.status.http-mapping.FATAL = 503 +``` + +下表显示了内置状态的默认映射: + +| Status | Mapping | +| -------------- | -------------------------------------------- | +| DOWN | SERVICE_UNAVAILABLE (503) | +| OUT_OF_SERVICE | SERVICE_UNAVAILABLE (503) | +| UP | No mapping by default, so http status is 200 | +| UNKNOWN | No mapping by default, so http status is 200 | + + + +## 五、自定义端点 + +### 5.1 Hyperic Sigar + +Spring Boot 支持使用 `@Endpoint` 来自定义端点暴露信息,这里以暴露服务所在硬件的监控信息为例。想要获取服务器信息需要通过第三方工具来实现,这里我们使用的是 Sigar。Sigar 是 Hyperic HQ 下的数据收集组件,其底层采用 C 语言进行编写,它通过本地方法调用操作系统的 API 来获取系统相关数据 ,其 JAR 包的下载地址为:https://sourceforge.net/projects/sigar/ 。 + +Sigar 为不同平台提供了不同的库文件,下载后需要将库文件放到服务所在主机的对应位置: + +- **Windows** :根据操作系统版本选择 sigar-amd64-winnt.dll 或 sigar-x86-winnt.dll 并拷贝到 C:\Windows\System32 下; +- **Linux**:将 libsigar-amd64-linux.so 或 libsigar-x86-linux.so 拷贝以下任意目录:`/usr/lib64` , `/lib64` ,`/lib` , `/usr/lib` ,如果不起作用,还需要通过 `sudo chmod 744` 命令修改 libsigar-amd64-linux.so 的文件权限。 + +### 5.2 自定义端点 + +```java +@Endpoint(id = "customEndPoint") +@Component +public class CustomEndPoint { + + @ReadOperation + public Map getCupInfo() throws SigarException { + + Map cupInfoMap = new LinkedHashMap<>(); + + Sigar sigar = new Sigar(); + + CpuInfo infoList[] = sigar.getCpuInfoList(); + CpuPerc[] cpuList = sigar.getCpuPercList(); + + for (int i = 0; i < infoList.length; i++) { + CpuInfo info = infoList[i]; + cupInfoMap.put("CPU " + i + " 的总量 MHz", info.getMhz()); // CPU 的总量 MHz + cupInfoMap.put("CPU " + i + " 生产商", info.getVendor()); // 获得 CPU 的生产商,如:Intel + cupInfoMap.put("CPU " + i + " 类别", info.getModel()); // 获得 CPU 的类别,如:Core + cupInfoMap.put("CPU " + i + " 缓存数量", info.getCacheSize()); // 缓冲存储器数量 + cupInfoMap.put("CPU " + i + " 用户使用率", CpuPerc.format(cpuList[i].getUser())); // 用户使用率 + cupInfoMap.put("CPU " + i + " 系统使用率", CpuPerc.format(cpuList[i].getSys())); // 系统使用率 + cupInfoMap.put("CPU " + i + " 当前等待率", CpuPerc.format(cpuList[i].getWait())); // 当前等待率 + cupInfoMap.put("CPU " + i + " 当前错误率", CpuPerc.format(cpuList[i].getNice())); // 当前错误率 + cupInfoMap.put("CPU " + i + " 当前空闲率", CpuPerc.format(cpuList[i].getIdle())); // 当前空闲率 + cupInfoMap.put("CPU " + i + " 总的使用率", CpuPerc.format(cpuList[i].getCombined()));// 总的使用率 + } + return cupInfoMap; + } + +} +``` + +可用的方法注解由 HTTP 操作所决定: + +| operation | HTTP 方法 | +| ---------------- | -------- | +| @ReadOperation | GET | +| @WriteOperation | POST | +| @DeleteOperation | DELETE | + +### 5.3 访问自定义端点 + +地址为:http://127.0.0.1:8080/actuator/customEndPoint : + +
+ +关于 Sigar 的更多监控参数可以参考博客:[java 读取计算机 CPU、内存等信息(Sigar 使用)](https://blog.csdn.net/wudiazu/article/details/73829324) 或 Sigar 下载包中的用例: +
\ No newline at end of file diff --git a/spring-boot/spring-boot-base/README.md b/spring-boot/spring-boot-base/README.md index 8d0e719..d09963f 100644 --- a/spring-boot/spring-boot-base/README.md +++ b/spring-boot/spring-boot-base/README.md @@ -1,262 +1,250 @@ -# spring-boot 基础 - -## 目录
-一、说明
-        1.1 项目结构说明
-        1.2 项目依赖
-二、spring boot 主启动类
-三、开箱即用的web工程
+# Spring-Boot 基础 + + - - - -## 一、说明 - -#### 1.1 项目结构说明 - -1. 本项目搭建一个简单的 hello spring 的 web 工程,简单说明 spring-boot 的开箱即用的特性; -2. 模板引擎采用 freemaker 和 thymeleaf 作为示例,分别对应模板文件 makershow.ftl 和 leafShow.html; -3. spring boot 2.x 默认是不支持 jsp 的,需要额外的配置,关于使用 jsp 的整合可以参考[spring-boot-jsp](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-jsp) 项目。 - -
- -#### 1.2 项目依赖 - -导入相关的 starter(启动器) - -```xml - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.1.1.RELEASE - - - com.heibaiying - spring-boot-base - 0.0.1-SNAPSHOT - spring-boot-base - Demo project for Spring Boot - - - 1.8 - - - - - - org.springframework.boot - spring-boot-starter-freemarker - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - - org.springframework.boot - spring-boot-starter-web - - - - org.projectlombok - lombok - true - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - -``` - -1. spring boot 项目默认继承自 spring-boot-starter-parent,而 spring-boot-starter-parent 继承自 spring-boot-dependencies, spring-boot-dependencies 中定义了关于 spring boot 依赖的各种 jar 包的版本,是 spring boot 的版本管理中心。 - -
- -2. 关于 spring boot 2.x 官方支持的所有 starter 可以参见官方文档 [Table 13.1. Spring Boot application starters](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#using-boot-starter) - - - -## 二、spring boot 主启动类 - - 如果采用 IDEA 或者 Spring Tool Suite (STS) 等开发工具创建的 spring boot 工程,会默认创建启动类,如果没有创建,需要手动创建启动类 - -```java -package com.heibaiying.springbootbase; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class SpringBootBaseApplication { - - // 启动类默认开启包扫描,扫描与主程序所在包及其子包,对于本工程而言 默认扫描 com.heibaiying.springbootbase - public static void main(String[] args) { - SpringApplication.run(SpringBootBaseApplication.class, args); - } - -} -``` - -@SpringBootApplication 注解是一个复合注解,里面包含了@ComponentScan 注解,默认开启包扫描,扫描与主程序所在包及其子包,对于本工程而言 默认扫描 com.heibaiying.springbootbase - -```java -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Inherited -@SpringBootConfiguration -@EnableAutoConfiguration -@ComponentScan(excludeFilters = { - @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), - @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) -public @interface SpringBootApplication { - ... -} -``` - - - -## 三、开箱即用的web工程 - -在 springbootBaseApplication.java 的同级目录创建 controller 文件夹,并在其中创建 RestfulController.java,启动项目访问 localhost:8080/restful/programmers 即可看到项目搭建成功。 - -```java -/** - * @author : heibaiying - * @description : restful 控制器 - */ -@RestController -@RequestMapping("restful") -public class RestfulController { - - @GetMapping("programmers") - private List getProgrammers() { - List programmers = new ArrayList<>(); - programmers.add(new Programmer("xiaoming", 12, 100000.00f, LocalDate.of(2019, Month.AUGUST, 2))); - programmers.add(new Programmer("xiaohong", 23, 900000.00f, LocalDate.of(2013, Month.FEBRUARY, 2))); - return programmers; - } -} -``` - -这里之所以能够开箱即用,是因为我们在项目中导入 spring-boot-starter-web 启动器,而@SpringBootApplication 复合注解中默认开启了@EnableAutoConfiguration 注解允许开启自动化配置,spring 在检查导入 starter-web 的依赖后就会开启 web 的自动化配置。 - - - -## 四、模板引擎 - -这里我们在一个项目中同时导入了 freemaker 和 thymeleaf 的 starter(虽然并不推荐,但是在同一个项目中是可以混用这两种模板引擎的)。 - -#### 4.1 freemarker - -```java -/** - * @author : heibaiying - * @description : 跳转渲染模板引擎 默认模板的存放位置为 classpath:templates - */ -@Controller -@RequestMapping("freemarker") -public class FreeMarkerController { - - @RequestMapping("show") - private String programmerShow(ModelMap modelMap){ - List programmerList=new ArrayList<>(); - programmerList.add(new Programmer("xiaoming",12,100000.00f,LocalDate.of(2019,Month.AUGUST,2))); - programmerList.add(new Programmer("xiaohong",23,900000.00f,LocalDate.of(2013,Month.FEBRUARY,2))); - modelMap.addAttribute("programmers",programmerList); - return "markerShow"; - } -} - -``` - -```html - - - - - freemarker 模板引擎 - - -
    - <#list programmers as programmer> -
  • 姓名: ${programmer.name} 年龄: ${programmer.age}
  • - -
- - -``` - -#### 4.2 thymeleaf - -```java -/** - * @author : heibaiying - * @description : 跳转渲染模板引擎 默认模板的存放位置为 classpath:templates - */ -@Controller -@RequestMapping("thymeleaf") -public class ThymeleafController { - - @RequestMapping("show") - private String programmerShow(ModelMap modelMap) { - List programmerList = new ArrayList<>(); - programmerList.add(new Programmer("xiaoming", 12, 100000.00f, LocalDate.of(2019, Month.AUGUST, 2))); - programmerList.add(new Programmer("xiaohong", 23, 900000.00f, LocalDate.of(2013, Month.FEBRUARY, 2))); - modelMap.addAttribute("programmers", programmerList); - return "leafShow"; - } -} - -``` - -```html - - - - - thymeleaf 模板引擎 - - -
    -
  • - 姓名: - 薪水: -
  • -
- - -``` - -#### 4.3 文档说明 - -freemarker:提供了完善的中文文档,地址 http://freemarker.foofun.cn/ - -thymeleaf:官方英文文档地址:[thymeleaf 3.0.11RELEASE](https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.pdf) - -注:我在本仓库中也上传了一份[thymeleaf 中文文档(gangzi828(刘明刚 译)](https://github.com/heibaiying/spring-samples-for-all/tree/master/referenced%20documents),翻译的版本为 3.0.5RELEASE +## 一、项目说明 + +### 1.1 项目结构 + +1. 本项目搭建一个简单的 hello spring 的 Web 工程,简单说明 Spring-Boot 的开箱即用的特性; +2. 模板引擎采用 freemaker 和 thymeleaf 作为示例,分别对应模板文件 makershow.ftl 和 leafShow.html; +3. Spring Boot 2.x 默认不支持 Jsp ,需要额外的配置,关于使用 jsp 的整合可以参考 [spring-boot-jsp](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-jsp) 项目。 + +
+### 1.2 基本依赖 + +导入相关的 starter (启动器): + +```xml + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.1.RELEASE + + + com.heibaiying + spring-boot-base + 0.0.1-SNAPSHOT + spring-boot-base + Demo project for Spring Boot + + + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-freemarker + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.boot + spring-boot-starter-web + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` + ++ Spring Boot 项目默认继承自 spring-boot-starter-parent,而 spring-boot-starter-parent 则继承自 spring-boot-dependencies,spring-boot-dependencies 中定义了关于 spring boot 依赖的各种 jar 包的版本,它是 Spring Boot 的版本管理中心。 + +
++ 关于Spring Boot 2.x 官方支持的所有 starter 可以参见官方文档:[Table 13.1. Spring Boot application starters](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#using-boot-starter) + + + +## 二、主启动类 + + 如果采用 IDEA 或者 Spring Tool Suite (STS) 等开发工具创建的 Spring Boot 工程,会默认创建启动类,如果没有创建,需要手动创建启动类: + +```java +package com.heibaiying.springbootbase; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootBaseApplication { + + // 启动类默认开启包扫描,扫描与主程序所在包及其子包,对于本工程而言 默认扫描 com.heibaiying.springbootbase + public static void main(String[] args) { + SpringApplication.run(SpringBootBaseApplication.class, args); + } + +} +``` + +@SpringBootApplication 是一个复合注解,里面包含了 @ComponentScan 注解,即默认开启包扫描,扫描主程序所在包及其子包,对于本工程而言,默认扫描 com.heibaiying.springbootbase: + +```java +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@SpringBootConfiguration +@EnableAutoConfiguration +@ComponentScan(excludeFilters = { + @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), + @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) +public @interface SpringBootApplication { + ... +} +``` + + + +## 三、开箱即用 + +采用 Spring Boot 构建的 Web 项目具备开箱即用的特性,不需要做任何额外的配置就可以正常使用。这里我们在 springbootBaseApplication 的同级目录创建 controller 文件夹,并在其中创建 RestfulController 控制器,之后启动项目访问 `localhost:8080/restful/programmers` ,即可看到返回相关的信息。 + +```java +@RestController +@RequestMapping("restful") +public class RestfulController { + + @GetMapping("programmers") + private List getProgrammers() { + List programmers = new ArrayList<>(); + programmers.add(new Programmer("xiaoming", 12, 100000.00f, LocalDate.of(2019, Month.AUGUST, 2))); + programmers.add(new Programmer("xiaohong", 23, 900000.00f, LocalDate.of(2013, Month.FEBRUARY, 2))); + return programmers; + } +} +``` + +Spring Boot 之所以能够开箱即用,是因为我们在项目中导入 spring-boot-starter-web 启动器,而 @SpringBootApplication 复合注解中默认开启了 @EnableAutoConfiguration ,即允许开启自动化配置。 Spring Boot 检查到存在 starter-web 依赖后就会开启 Web 相关的自动化配置。 + + + +## 四、模板引擎 + +这里我们在一个项目中同时导入了 freemaker 和 thymeleaf 的 starter(虽然并不推荐,但是在同一个项目中是可以混用这两种模板引擎的): + +### 4.1 freemarker + +```java +/** + * @description : 跳转渲染模板引擎 默认模板的存放位置为 classpath:templates + */ +@Controller +@RequestMapping("freemarker") +public class FreeMarkerController { + + @RequestMapping("show") + private String programmerShow(ModelMap modelMap){ + List programmerList=new ArrayList<>(); + programmerList.add(new Programmer("xiaoming",12,100000.00f,LocalDate.of(2019,Month.AUGUST,2))); + programmerList.add(new Programmer("xiaohong",23,900000.00f,LocalDate.of(2013,Month.FEBRUARY,2))); + modelMap.addAttribute("programmers",programmerList); + return "markerShow"; + } +} + +``` + +```html + + + + + freemarker 模板引擎 + + +
    + <#list programmers as programmer> +
  • 姓名: ${programmer.name} 年龄: ${programmer.age}
  • + +
+ + +``` + +### 4.2 thymeleaf + +```java +/** + * @description : 跳转渲染模板引擎 默认模板的存放位置为 classpath:templates + */ +@Controller +@RequestMapping("thymeleaf") +public class ThymeleafController { + + @RequestMapping("show") + private String programmerShow(ModelMap modelMap) { + List programmerList = new ArrayList<>(); + programmerList.add(new Programmer("xiaoming", 12, 100000.00f, LocalDate.of(2019, Month.AUGUST, 2))); + programmerList.add(new Programmer("xiaohong", 23, 900000.00f, LocalDate.of(2013, Month.FEBRUARY, 2))); + modelMap.addAttribute("programmers", programmerList); + return "leafShow"; + } +} + +``` + +```html + + + + + thymeleaf 模板引擎 + + +
    +
  • + 姓名: + 薪水: +
  • +
+ + +``` + +### 4.3 文档说明 + +关于这两种模板引擎的语法可以参考其官方文档: + +- **freemarker**:提供了完善的中文文档,地址为: http://freemarker.foofun.cn/ 。 +- **thymeleaf**:官方英文文档地址:[thymeleaf 3.0.11RELEASE](https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.pdf) 。我在本仓库中上传了一份 [thymeleaf 中文文档(gangzi828(刘明刚 译)](https://github.com/heibaiying/spring-samples-for-all/tree/master/referenced%20documents),翻译的版本为 3.0.5RELEASE。 \ No newline at end of file diff --git a/spring-boot/spring-boot-data-jpa/README.md b/spring-boot/spring-boot-data-jpa/README.md index 8c0968b..b6dad10 100644 --- a/spring-boot/spring-boot-data-jpa/README.md +++ b/spring-boot/spring-boot-data-jpa/README.md @@ -1,206 +1,203 @@ -# spring boot data jpa - -## 目录
-一、说明
+# Spring Boot Data JPA + + +## 一、项目说明 + +### 1.1 项目结构 + +
+### 1.2 基本依赖 + +```xml + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + mysql + mysql-connector-java + 8.0.13 + + + + org.springframework.boot + spring-boot-starter-test + test + + +``` + +## 二、 使用 Data JPA + +### 2.1 数据源配置 + +在 application.yml 中配置数据源: + +```yaml +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false + username: root + password: root + driver-class-name: com.mysql.cj.jdbc.Driver + jpa: + hibernate: + ddl-auto: update + #Hibernate 默认创建的表是 myisam 引擎,可以用以下方式指定为使用 innodb 创建表 + database-platform: org.hibernate.dialect.MySQL57Dialect + show-sql: true +``` + +### 2.2 查询接口 + +```java +/** + * @description : 查询接口继承自 CrudRepository,CrudRepository 默认定义了部分增删改查方法 + */ +public interface ProgRepository extends CrudRepository { + + /* + * 方法名遵循命名规范的查询 更多命名规范可以参考官方文档所列出的这张表格 + */ + List findAllByName(String name); + + /* + *分页排序查询 + */ + Page findAll(Pageable pageable); + + + /* + * 占位符查询 + */ + @Query(value = "select u from Programmer u where u.name = ?1 or u.salary = ?2") + List findByConditionAndOrder(String name, float salary, Sort.Order order); + + + /* + * 传入参数名称 + */ + @Query("select u from Programmer u where u.name = :name or u.age = :age") + Programmer findByParam(@Param("name") String name, + @Param("age") int age); +} + +``` + +在使用 Spring Data JPA 时你甚至可以不用写 SQL 语句,只需要在定义方法名时满足 Spring 的规范即可,Spring 会自动将这些方法按照其命名转换为对应的 SQL 语句,以下是其转换对照表: + +| Keyword | Sample | JPQL snippet | +| ------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| `And` | `findByLastnameAndFirstname` | `… where x.lastname = ?1 and x.firstname = ?2` | +| `Or` | `findByLastnameOrFirstname` | `… where x.lastname = ?1 or x.firstname = ?2` | +| `Is,Equals` | `findByFirstname`,`findByFirstnameIs`,
`findByFirstnameEquals` | `… where x.firstname = ?1` | +| `Between` | `findByStartDateBetween` | `… where x.startDate between ?1 and ?2` | +| `LessThan` | `findByAgeLessThan` | `… where x.age < ?1` | +| `LessThanEqual` | `findByAgeLessThanEqual` | `… where x.age <= ?1` | +| `GreaterThan` | `findByAgeGreaterThan` | `… where x.age > ?1` | +| `GreaterThanEqual` | `findByAgeGreaterThanEqual` | `… where x.age >= ?1` | +| `After` | `findByStartDateAfter` | `… where x.startDate > ?1` | +| `Before` | `findByStartDateBefore` | `… where x.startDate < ?1` | +| `IsNull` | `findByAgeIsNull` | `… where x.age is null` | +| `IsNotNull,NotNull` | `findByAge(Is)NotNull` | `… where x.age not null` | +| `Like` | `findByFirstnameLike` | `… where x.firstname like ?1` | +| `NotLike` | `findByFirstnameNotLike` | `… where x.firstname not like ?1` | +| `StartingWith` | `findByFirstnameStartingWith` | `… where x.firstname like ?1`(parameter bound with appended `%`) | +| `EndingWith` | `findByFirstnameEndingWith` | `… where x.firstname like ?1`(parameter bound with prepended `%`) | +| `Containing` | `findByFirstnameContaining` | `… where x.firstname like ?1`(parameter bound wrapped in `%`) | +| `OrderBy` | `findByAgeOrderByLastnameDesc` | `… where x.age = ?1 order by x.lastname desc` | +| `Not` | `findByLastnameNot` | `… where x.lastname <> ?1` | +| `In` | `findByAgeIn(Collection ages)` | `… where x.age in ?1` | +| `NotIn` | `findByAgeNotIn(Collection ages)` | `… where x.age not in ?1` | +| `True` | `findByActiveTrue()` | `… where x.active = true` | +| `False` | `findByActiveFalse()` | `… where x.active = false` | +| `IgnoreCase` | `findByFirstnameIgnoreCase` | `… where UPPER(x.firstame) = UPPER(?1)` | + +### 2.3 单元测试 + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class DataJPATests { + + @Autowired + private ProgRepository repository; + + /** + * 保存数据测试 + */ + @Test + public void save() { + // 保存单条数据 + repository.save(new Programmer("pro01", 12, 2121.34f, new Date())); + // 保存多条数据 + List programmers = new ArrayList<>(); + programmers.add(new Programmer("pro02", 22, 3221.34f, new Date())); + programmers.add(new Programmer("pro03", 32, 3321.34f, new Date())); + programmers.add(new Programmer("pro04", 44, 4561.34f, new Date())); + programmers.add(new Programmer("pro01", 44, 4561.34f, new Date())); + repository.saveAll(programmers); + } + + + /** + * 查询数据测试 + */ + @Test + public void get() { + + // 遵循命名规范的查询 + List programmers = repository.findAllByName("pro01"); + programmers.forEach(System.out::println); + + // 传入参数名称 + Programmer param = repository.findByParam("pro02", 22); + System.out.println("findByParam:" + param); + + // 占位符查询 + List byCondition = repository.findByConditionAndOrder("pro03", 3321.34f, Sort.Order.asc("salary")); + System.out.println("byCondition:" + byCondition); + + //条件与分页查询 需要注意的是这里的页数是从第 0 页开始计算的 + Page page = repository.findAll(PageRequest.of(0, 10, Sort.Direction.DESC, "salary")); + page.get().forEach(System.out::println); + } + + + /** + * 更新数据测试 + */ + @Test + public void update() { + // 保存主键相同的数据就认为是更新操作 + repository.save(new Programmer(1, "updatePro01", 12, 2121.34f, new Date())); + Optional programmer = repository.findById(1); + Assert.assertEquals(programmer.get().getName(), "updatePro01"); + } + + /** + * 删除数据测试 + */ + @Test + public void delete() { + Optional programmer = repository.findById(2); + if (programmer.isPresent()) { + repository.deleteById(2); + } + Assert.assertFalse(programmer.isPresent()); + } +} +``` + diff --git a/spring-boot/spring-boot-druid-mybatis/README.md b/spring-boot/spring-boot-druid-mybatis/README.md index 028956a..538fd4a 100644 --- a/spring-boot/spring-boot-druid-mybatis/README.md +++ b/spring-boot/spring-boot-druid-mybatis/README.md @@ -1,210 +1,206 @@ -# spring boot 整合 druid+mybatis - -## 目录
-一、说明
+# Spring Boot 整合 Druid+Mybatis + + + + - - - -## 一、说明 - -#### 1.1 项目结构 - -1. 项目查询用的表对应的建表语句放置在 resources 的 sql 文件夹下; - -2. 为了使用 druid 控制台的功能,项目以 web 的方式构建。 - -
- -#### 1.2 项目主要依赖 - -需要说明的是按照 spring 官方对于自定义的 starter 命名规范的推荐: - -- 官方的 starter 命名:spring-boot-starter-XXXX -- 其他第三方 starter 命名:XXXX-spring-boot-starte - -所以 mybatis 的 starter 命名为 mybatis-spring-boot-starter,如果有自定义 starter 需求,也需要按照此命名规则进行命名。 - -```xml - - org.mybatis.spring.boot - mybatis-spring-boot-starter - 1.3.2 - - - - mysql - mysql-connector-java - 8.0.13 - - - - com.alibaba - druid-spring-boot-starter - 1.1.10 - -``` - -spring boot 与 mybatis 版本的对应关系: - -| MyBatis-Spring-Boot-Starter | [MyBatis-Spring](http://www.mybatis.org/spring/index.html#Requirements) | Spring Boot | -| --------------------------- | ------------------------------------------------------------ | ------------- | -| **1.3.x (1.3.1)** | 1.3 or higher | 1.5 or higher | -| **1.2.x (1.2.1)** | 1.3 or higher | 1.4 or higher | -| **1.1.x (1.1.1)** | 1.3 or higher | 1.3 or higher | -| **1.0.x (1.0.2)** | 1.2 or higher | 1.3 or higher | - - - -## 二、整合 druid + mybatis - -#### 2.1 在application.yml 中配置数据源 - -本用例采用 druid 作为数据库连接池,虽然 druid 性能略逊于 Hikari,但是提供了更为全面的监控管理,可以按照实际需求选用 druid 或者 Hikari。(关于 Hikari 数据源的配置可以参考[spring-boot-mybatis 项目](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-mybatis)) - -```yaml -spring: - datasource: - url: jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false - username: root - password: root - driver-class-name: com.mysql.cj.jdbc.Driver - - # 使用 druid 作为连接池 更多配置的说明可以参见 druid starter 中文文档 https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter - type: com.alibaba.druid.pool.DruidDataSource - druid: - # 初始化时建立物理连接的个数。初始化发生在显示调用 init 方法,或者第一次 getConnection 时 - initialSize: 5 - # 最小连接池数量 - minIdle: 5 - # 最大连接池数量 - maxActive: 10 - # 获取连接时最大等待时间,单位毫秒。配置了 maxWait 之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置 useUnfairLock 属性为 true 使用非公平锁。 - maxWait: 60000 - # Destroy 线程会检测连接的间隔时间,如果连接空闲时间大于等于 minEvictableIdleTimeMillis 则关闭物理连接。 - timeBetweenEvictionRunsMillis: 60000 - # 连接保持空闲而不被驱逐的最小时间 - minEvictableIdleTimeMillis: 300000 - # 用来检测连接是否有效的 sql 因数据库方言而差, 例如 oracle 应该写成 SELECT 1 FROM DUAL - validationQuery: SELECT 1 - # 建议配置为 true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行 validationQuery 检测连接是否有效。 - testWhileIdle: true - # 申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。 - testOnBorrow: false - # 归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。 - testOnReturn: false - # 是否自动回收超时连接 - removeAbandoned: true - # 超时时间 (以秒数为单位) - remove-abandoned-timeout: 180 - - # druid 监控的配置 如果不使用 druid 的监控功能的话 以下配置就不是必须的 - # 本项目监控台访问地址: http://localhost:8080/druid/login.html - - # WebStatFilter 用于采集 web-jdbc 关联监控的数据。 - # 更多配置可参见: https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_%E9%85%8D%E7%BD%AEWebStatFilter - web-stat-filter: - # 是否开启 WebStatFilter 默认是 true - enabled: true - # 需要拦截的 url - url-pattern: /* - # 排除静态资源的请求 - exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" - - # Druid 内置提供了一个 StatViewServlet 用于展示 Druid 的统计信息。 - # 更多配置可参见:https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE - stat-view-servlet: - #是否启用 StatViewServlet 默认值 true - enabled: true - # 需要拦截的 url - url-pattern: /druid/* - # 允许清空统计数据 - reset-enable: true - login-username: druid - login-password: druid - - - -# mybatis 相关配置 -mybatis: - configuration: - # 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 - # oracle 数据库建议配置为 JdbcType.NULL, 默认是 Other - jdbc-type-for-null: 'null' - # 是否打印 sql 语句 调试的时候可以开启 -log-impl: org.apache.ibatis.logging.stdout.StdOutImpl -``` - -#### 2.2 新建查询接口和controller - -```java -@Mapper -public interface ProgrammerDao { - - - @Select("select * from programmer") - List selectAll(); - - @Insert("insert into programmer (name, age, salary, birthday) VALUES (#{name}, #{age}, #{salary}, #{birthday})") - void save(Programmer programmer); - - @Select("select * from programmer where name = #{id}") - Programmer selectById(int id); - - @Update("update programmer set name=#{name},age=#{age},salary=#{salary},birthday=#{birthday} where id=#{id}") - int modify(Programmer programmer); - - @Delete(" delete from programmer where id = #{id}") - void delete(int id); -} -``` - -```xml -@RestController -public class ProgrammerController { - - @Autowired - private ProgrammerDao programmerDao; - - @GetMapping("/programmers") - public List get() { - return programmerDao.selectAll(); - } -} -``` - -#### 2.3 关于druid监控数据的外部化调用 - -```java -/** - * @author : heibaiying - * @description :在 Spring Boot 中可以通过 HTTP 接口将 Druid 监控数据以 JSON 的形式暴露出去, - * 实际使用中你可以根据你的需要自由地对监控数据、暴露方式进行扩展。 - */ - -@RestController -public class DruidStatController { - - @GetMapping("/stat") - public Object druidStat() { - // DruidStatManagerFacade#getDataSourceStatDataList 该方法可以获取所有数据源的监控数据 - return DruidStatManagerFacade.getInstance().getDataSourceStatDataList(); - } -} -``` - -
- - - -#### 2.4 druid 控制台的使用,默认访问地址 http://localhost:8080/druid/login.html - -
+## 一、项目说明 + +### 1.1 项目结构 + +1. 项目涉及表的建表语句放置在 resources 的 sql 文件夹下; + +2. 为了演示 Druid 控制台的功能,项目以 Web 的方式构建。 + +
+### 1.2 基本依赖 + +按照 Spring 官方对于自定义的 starter 命名规范的要求: + +- 官方的 starter 命名:spring-boot-starter-XXXX +- 其他第三方 starter 命名:XXXX-spring-boot-starter + +所以 Mybatis 的 starter 命名为 mybatis-spring-boot-starter,如果有自定义 starter 需求,也需要按照此命名规则进行命名。 + +```xml + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + + mysql + mysql-connector-java + 8.0.13 + + + + com.alibaba + druid-spring-boot-starter + 1.1.10 + +``` + +Spring Boot 与 Mybatis 版本的对应关系: + +| MyBatis-Spring-Boot-Starter | [MyBatis-Spring](http://www.mybatis.org/spring/index.html#Requirements) | Spring Boot | +| --------------------------- | ------------------------------------------------------------ | ------------- | +| **1.3.x (1.3.1)** | 1.3 or higher | 1.5 or higher | +| **1.2.x (1.2.1)** | 1.3 or higher | 1.4 or higher | +| **1.1.x (1.1.1)** | 1.3 or higher | 1.3 or higher | +| **1.0.x (1.0.2)** | 1.2 or higher | 1.3 or higher | + + + +## 二、整合 Druid + Mybatis + +### 2.1 配置数据源 + +本用例采用 Druid 作为数据库连接池,虽然 Druid 性能略逊于 Hikari,但提供了更为全面的监控管理,可以按照实际需求选用 Druid 或者 Hikari。(关于 Hikari 数据源的配置可以参考 [spring-boot-mybatis 项目](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-mybatis)) + +```yaml +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false + username: root + password: root + driver-class-name: com.mysql.cj.jdbc.Driver + + # 使用 druid 作为连接池 更多配置的说明可以参见 druid starter 中文文档 https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter + type: com.alibaba.druid.pool.DruidDataSource + druid: + # 初始化时建立物理连接的个数。初始化发生在显示调用 init 方法,或者第一次 getConnection 时 + initialSize: 5 + # 最小连接池数量 + minIdle: 5 + # 最大连接池数量 + maxActive: 10 + # 获取连接时最大等待时间,单位毫秒。配置了 maxWait 之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置 useUnfairLock 属性为 true 使用非公平锁。 + maxWait: 60000 + # Destroy 线程会检测连接的间隔时间,如果连接空闲时间大于等于 minEvictableIdleTimeMillis 则关闭物理连接。 + timeBetweenEvictionRunsMillis: 60000 + # 连接保持空闲而不被驱逐的最小时间 + minEvictableIdleTimeMillis: 300000 + # 用来检测连接是否有效的 sql 因数据库方言而差, 例如 oracle 应该写成 SELECT 1 FROM DUAL + validationQuery: SELECT 1 + # 建议配置为 true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行 validationQuery 检测连接是否有效。 + testWhileIdle: true + # 申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。 + testOnBorrow: false + # 归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。 + testOnReturn: false + # 是否自动回收超时连接 + removeAbandoned: true + # 超时时间 (以秒数为单位) + remove-abandoned-timeout: 180 + + # druid 监控的配置 如果不使用 druid 的监控功能的话 以下配置就不是必须的 + # 本项目监控台访问地址: http://localhost:8080/druid/login.html + + # WebStatFilter 用于采集 web-jdbc 关联监控的数据。 + # 更多配置可参见: https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_%E9%85%8D%E7%BD%AEWebStatFilter + web-stat-filter: + # 是否开启 WebStatFilter 默认是 true + enabled: true + # 需要拦截的 url + url-pattern: /* + # 排除静态资源的请求 + exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" + + # Druid 内置提供了一个 StatViewServlet 用于展示 Druid 的统计信息。 + # 更多配置可参见:https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE + stat-view-servlet: + #是否启用 StatViewServlet 默认值 true + enabled: true + # 需要拦截的 url + url-pattern: /druid/* + # 允许清空统计数据 + reset-enable: true + login-username: druid + login-password: druid + + + +# mybatis 相关配置 +mybatis: + configuration: + # 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 + # oracle 数据库建议配置为 JdbcType.NULL, 默认是 Other + jdbc-type-for-null: 'null' + # 是否打印 sql 语句 调试的时候可以开启 +log-impl: org.apache.ibatis.logging.stdout.StdOutImpl +``` + +### 2.2 整合查询 + +新建查询接口和测试 Controller: + +```java +@Mapper +public interface ProgrammerDao { + + + @Select("select * from programmer") + List selectAll(); + + @Insert("insert into programmer (name, age, salary, birthday) VALUES (#{name}, #{age}, #{salary}, #{birthday})") + void save(Programmer programmer); + + @Select("select * from programmer where name = #{id}") + Programmer selectById(int id); + + @Update("update programmer set name=#{name},age=#{age},salary=#{salary},birthday=#{birthday} where id=#{id}") + int modify(Programmer programmer); + + @Delete(" delete from programmer where id = #{id}") + void delete(int id); +} +``` + +```xml +@RestController +public class ProgrammerController { + + @Autowired + private ProgrammerDao programmerDao; + + @GetMapping("/programmers") + public List get() { + return programmerDao.selectAll(); + } +} +``` + +### 2.3 监控数据 + +在 Spring Boot 中可以通过 HTTP 接口将 Druid 的监控数据以 JSON 的形式暴露出去,可以用于健康检查等功能: + +```java +@RestController +public class DruidStatController { + + @GetMapping("/stat") + public Object druidStat() { + // DruidStatManagerFacade#getDataSourceStatDataList 该方法可以获取所有数据源的监控数据 + return DruidStatManagerFacade.getInstance().getDataSourceStatDataList(); + } +} +``` + +
+ +### 2.4 Druid 控制台 + +默认访问地址为 http://localhost:8080/druid/login.html : + +
diff --git a/spring-boot/spring-boot-dubbo/README.md b/spring-boot/spring-boot-dubbo/README.md index 7109a77..99d8b6a 100644 --- a/spring-boot/spring-boot-dubbo/README.md +++ b/spring-boot/spring-boot-dubbo/README.md @@ -1,365 +1,248 @@ -# spring boot 整合 dubbo - -## 目录
-一、 项目结构说明
-二、关键依赖
-三、公共模块(boot-dubbo-common)
-四、 服务提供者(boot-dubbo-provider)
-        4.1 提供方配置
-        4.2 使用注解@Service暴露服务
-五、服务消费者(boot-dubbo-consumer)
-        1.消费方的配置
-        2.使用注解@Reference引用远程服务
-六、项目构建的说明
-七、关于dubbo新版本管理控制台的安装说明
-## 正文
- - - - -## 一、 项目结构说明 - -1.1 按照 dubbo 文档推荐的服务最佳实践,建议将服务接口、服务模型、服务异常等均放在 API 包中,所以项目采用 maven 多模块的构建方式,在 spring-boot-dubbo 下构建三个子模块: - -1. boot-dubbo-common 是公共模块,用于存放公共的接口和 bean,被 boot-dubbo-provider 和 boot-dubbo-consumer 在 pom.xml 中引用; -2. boot-dubbo-provider 是服务的提供者,提供商品的查询服务; -3. boot-dubbo-consumer 是服务的消费者,调用 provider 提供的查询服务。 - -1.2 本项目 dubbo 的搭建采用 zookeeper 作为注册中心, 关于 zookeeper 的安装和基本操作可以参见我的手记 [Zookeeper 基础命令与 Java 客户端](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/ZooKeeper/ZooKeeper%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA%E4%B8%8EJava%E5%AE%A2%E6%88%B7%E7%AB%AF.md) - -
- - - -## 二、关键依赖 - -在父工程的项目中统一导入依赖 dubbo 的 starter,父工程的 pom.xml 如下 - -```xml - - - 4.0.0 - pom - - - boot-dubbo-common - boot-dubbo-consumer - boot-dubbo-provider - - - - org.springframework.boot - spring-boot-starter-parent - 2.1.1.RELEASE - - - com.heibaiying - spring-boot-dubbo - 0.0.1-SNAPSHOT - spring-boot-dubbo - Demo project for Spring Boot - - - 1.8 - - - - - org.springframework.boot - spring-boot-starter-freemarker - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-devtools - runtime - - - org.projectlombok - lombok - true - - - org.springframework.boot - spring-boot-starter-test - test - - - - com.alibaba.boot - dubbo-spring-boot-starter - 0.2.0 - - - - -``` - - - -## 三、公共模块(boot-dubbo-common) - -- api 下为公共的调用接口; -- bean 下为公共的实体类。 - -
- -## 四、 服务提供者(boot-dubbo-provider) - -
- -#### 4.1 提供方配置 - -```yaml -dubbo: - application: - name: boot-duboo-provider - # 指定注册协议和注册地址 dubbo 推荐使用 zookeeper 作为注册中心,并且在 start 依赖中引入了 zookeeper 的 java 客户端 Curator - registry: - protocol: zookeeper - address: 127.0.0.1:2181 - protocol.name: dubbo -``` - -#### 4.2 使用注解@Service暴露服务 - -需要注意的是这里的@Service 注解不是 spring 的注解,而是 dubbo 的注解 com.alibaba.dubbo.config.annotation.Service - -```java -package com.heibaiying.dubboprovider.service; - -import com.alibaba.dubbo.config.annotation.Service; -import com.heibaiying.api.IProductService; -import com.heibaiying.bean.Product; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -/** - * @author : heibaiying - * @description : 产品提供接口实现类 - */ -@Service(timeout = 5000) -public class ProductService implements IProductService { - - private static List productList = new ArrayList<>(); - - static { - for (int i = 0; i < 20; i++) { - productList.add(new Product(i, "产品" + i, i / 2 == 0, new Date(), 66.66f * i)); - } - } - - public Product queryProductById(int id) { - for (Product product : productList) { - if (product.getId() == id) { - return product; - } - } - return null; - } - - - public List queryAllProducts() { - return productList; - } -} - -``` - -## 五、服务消费者(boot-dubbo-consumer) - -
- -#### 1.消费方的配置 - -```yaml -dubbo: - application: - name: boot-duboo-provider - # 指定注册协议和注册地址 dubbo 推荐使用 zookeeper 作为注册中心,并且在 start 依赖中引入了 zookeeper 的 java 客户端 Curator - registry: - protocol: zookeeper - address: 127.0.0.1:2181 - protocol.name: dubbo - # 关闭所有服务的启动时检查 (没有提供者时报错)视实际情况设置 - consumer: - check: false -server: -port: 8090 -``` - -#### 2.使用注解@Reference引用远程服务 - -```java -package com.heibaiying.dubboconsumer.controller; - -import com.alibaba.dubbo.config.annotation.Reference; -import com.heibaiying.api.IProductService; -import com.heibaiying.bean.Product; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; - -import java.util.List; - -@Controller -@RequestMapping("sell") -public class SellController { - - // dubbo 远程引用注解 - @Reference - private IProductService productService; - - @RequestMapping - public String productList(Model model) { - List products = productService.queryAllProducts(); - model.addAttribute("products", products); - return "products"; - } - - @RequestMapping("product/{id}") - public String productDetail(@PathVariable int id, Model model) { - Product product = productService.queryProductById(id); - model.addAttribute("product", product); - return "product"; - } -} -``` - -## 六、项目构建的说明 - -因为在项目中,consumer 和 provider 模块均依赖公共模块,所以在构建 consumer 和 provider 项目前需要将 common 模块安装到本地仓库,**依次**对**父工程**和**common 模块**执行: - -```shell -mvn install -Dmaven.test.skip = true -``` - -consumer 中 pom.xml 如下 - -```xml - - - 4.0.0 - - - spring-boot-dubbo - com.heibaiying - 0.0.1-SNAPSHOT - - - boot-dubbo-consumer - 0.0.1-SNAPSHOT - boot-dubbo-consumer - dubbo project for Spring Boot - - - 1.8 - - - - - - com.heibaiying - boot-dubbo-common - 0.0.1-SNAPSHOT - compile - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - -``` - -provider 中 pom.xml 如下 - -```xml - - - 4.0.0 - - - spring-boot-dubbo - com.heibaiying - 0.0.1-SNAPSHOT - - - - boot-dubbo-provider - 0.0.1-SNAPSHOT - boot-dubbo-provider - dubbo project for Spring Boot - - - 1.8 - - - - - - com.heibaiying - boot-dubbo-common - 0.0.1-SNAPSHOT - compile - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - -``` - -## 七、关于dubbo新版本管理控制台的安装说明 - -安装: - -```sh -git clone https://github.com/apache/incubator-dubbo-ops.git /var/tmp/dubbo-ops -cd /var/tmp/dubbo-ops -mvn clean package -``` - -配置: - -```sh -配置文件为: -dubbo-admin-backend/src/main/resources/application.properties -主要的配置有 默认的配置就是 127.0.0.1:2181: -dubbo.registry.address=zookeeper://127.0.0.1:2181 -``` - -启动: - -```sh -mvn --projects dubbo-admin-backend spring-boot:run -``` - -访问: - -``` -http://127.0.0.1:8080 -``` +# Spring Boot 整合 Dubbo + + + +## 一、 项目结构 + +按照 Dubbo 文档推荐的服务最佳化实践的要求,建议将服务接口、服务模型、服务异常等均放在 API 包中,所以项目采用 Maven 多模块的构建方式,在 spring-boot-dubbo 下构建三个子模块: + +- **boot-dubbo-common** :是公共模块,用于存放公共的接口和 Java Bean,被 boot-dubbo-provider 和 boot-dubbo-consumer 在 pom.xml 中引用; +- **boot-dubbo-provider** :服务的提供者,提供商品的查询服务; +- **boot-dubbo-consumer** :是服务的消费者,调用 provider 提供的查询服务。 + +
+ +## 二、基本依赖 + +在父工程的项目中统一导入依赖 Dubbo 的 starter,父工程的 pom.xml 如下: + +```xml + + + 4.0.0 + pom + + + boot-dubbo-common + boot-dubbo-consumer + boot-dubbo-provider + + + + org.springframework.boot + spring-boot-starter-parent + 2.1.1.RELEASE + + + com.heibaiying + spring-boot-dubbo + 0.0.1-SNAPSHOT + spring-boot-dubbo + Demo project for Spring Boot + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-freemarker + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + com.alibaba.boot + dubbo-spring-boot-starter + 0.2.0 + + + + +``` + + + +## 三、公共模块 + +- api 下为公共的调用接口; +- bean 下为公共的实体类。 + +
+## 四、服务提供者 + +
+### 4.1 提供者配置 + +```yaml +dubbo: + application: + name: boot-duboo-provider + # 指定注册协议和注册地址 dubbo 推荐使用 zookeeper 作为注册中心,并且在 start 依赖中引入了 zookeeper 的 java 客户端 Curator + registry: + protocol: zookeeper + address: 127.0.0.1:2181 + protocol.name: dubbo +``` + +### 4.2 暴露服务 + + 使用注解 @Service 暴露服务,需要注意的是这里的 @Service 不是 Spring 的注解,而是 Dubbo 的注解: + +```java +import com.alibaba.dubbo.config.annotation.Service; + +/** + * @description : 产品提供接口实现类 + */ +@Service(timeout = 5000) +public class ProductService implements IProductService { + + private static List productList = new ArrayList<>(); + + static { + for (int i = 0; i < 20; i++) { + productList.add(new Product(i, "产品" + i, i / 2 == 0, new Date(), 66.66f * i)); + } + } + + public Product queryProductById(int id) { + for (Product product : productList) { + if (product.getId() == id) { + return product; + } + } + return null; + } + + + public List queryAllProducts() { + return productList; + } +} + +``` + +## 五、服务消费者 + +
+### 5.1 消费者配置 + +```yaml +dubbo: + application: + name: boot-duboo-provider + # 指定注册协议和注册地址 dubbo 推荐使用 zookeeper 作为注册中心,并且在 start 依赖中引入了 zookeeper 的 java 客户端 Curator + registry: + protocol: zookeeper + address: 127.0.0.1:2181 + protocol.name: dubbo + # 关闭所有服务的启动时检查 (没有提供者时报错)视实际情况设置 + consumer: + check: false +server: +port: 8090 +``` + +### 5.2 调用服务 + +使用 @Reference 注解引用远程服务: + +```java +import com.alibaba.dubbo.config.annotation.Reference; + +@Controller +@RequestMapping("sell") +public class SellController { + + // dubbo 远程引用注解 + @Reference + private IProductService productService; + + @RequestMapping + public String productList(Model model) { + List products = productService.queryAllProducts(); + model.addAttribute("products", products); + return "products"; + } + + @RequestMapping("product/{id}") + public String productDetail(@PathVariable int id, Model model) { + Product product = productService.queryProductById(id); + model.addAttribute("product", product); + return "product"; + } +} +``` + +## 六、项目构建 + +因为在项目中,consumer 和 provider 模块均依赖公共模块,所以在构建 consumer 和 provider 项目前需要将 common 模块安装到本地仓库,依次对父工程和 common 模块执行: + +```shell +mvn install -Dmaven.test.skip = true +``` + +## 七、Dubbo 控制台 + +Dubbo 新版本管理控制台的安装步骤如下: + +```sh +git clone https://github.com/apache/incubator-dubbo-ops.git /var/tmp/dubbo-ops +cd /var/tmp/dubbo-ops +mvn clean package +``` + +配置: + +```properties +# 配置文件为: +dubbo-admin-backend/src/main/resources/application.properties + +# 可以在其中修改zookeeper的地址 +dubbo.registry.address=zookeeper://127.0.0.1:2181 +``` + +启动: + +```sh +mvn --projects dubbo-admin-backend spring-boot:run +``` + +访问: + +```shell +http://127.0.0.1:8080 +``` diff --git a/spring-boot/spring-boot-jsp/README.md b/spring-boot/spring-boot-jsp/README.md index 7eedca1..b3c141d 100644 --- a/spring-boot/spring-boot-jsp/README.md +++ b/spring-boot/spring-boot-jsp/README.md @@ -1,97 +1,80 @@ -# spring boot 内置容器 整合 jsp - -## 目录
-一、说明
-        1.1 项目结构
-        1.2 项目主要依赖
-二、整合 jsp
-        2.1 导入整合的依赖
-        2.2 在application.yml 中指定访问视图文件的前缀和后缀
-        2.3 新建controller和show.jsp 测试整合是否成功
-## 正文
- - - - -## 一、说明 - -#### 1.1 项目结构 - -
- -#### 1.2 项目主要依赖 - -```xml - - - org.apache.tomcat.embed - tomcat-embed-jasper - provided - - - - javax.servlet - jstl - -``` - -## 二、整合 jsp - -#### 2.1 导入整合的依赖 - -```xml - - - org.apache.tomcat.embed - tomcat-embed-jasper - provided - - - - javax.servlet - jstl - -``` - -#### 2.2 在application.yml 中指定访问视图文件的前缀和后缀 - -```yml -spring: - mvc: - view: - prefix: /WEB-INF/jsp/ - suffix: .jsp -``` - -#### 2.3 新建controller和show.jsp 测试整合是否成功 - -```java -@Controller -@RequestMapping("index") -public class JspController { - - @RequestMapping - public String jsp(Model model){ - Programmer programmer = new Programmer("heibai", 21, 1298.31f, LocalDate.now()); - model.addAttribute("programmer",programmer); - return "show"; - } -} -``` - -```jsp -<%@ page contentType="text/html;charset=UTF-8" language="java" %> - - - programmer - - - -
    -
  • 姓名: ${programmer.name}
  • -
  • 年龄: ${programmer.age}
  • -
- - -``` +# Spring Boot 整合 JSP + +## 一、项目说明 + +
+ + +## 二、整合 JSP + +### 2.1 基本依赖 + +导入整合所需的依赖: + +```xml + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + javax.servlet + jstl + +``` + +### 2.2 配置视图 + +在 application.yml 中指定访问视图文件的前缀和后缀 : + +```yml +spring: + mvc: + view: + prefix: /WEB-INF/jsp/ + suffix: .jsp +``` + +### 2.3 整合测试 + +新建 controller 和 show.jsp 测试整合是否成功: + +```java +@Controller +@RequestMapping("index") +public class JspController { + + @RequestMapping + public String jsp(Model model){ + Programmer programmer = new Programmer("heibai", 21, 1298.31f, LocalDate.now()); + model.addAttribute("programmer",programmer); + return "show"; + } +} +``` + +```jsp +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + programmer + + + +
    +
  • 姓名: ${programmer.name}
  • +
  • 年龄: ${programmer.age}
  • +
+ + +``` + diff --git a/spring-boot/spring-boot-kafka/README.md b/spring-boot/spring-boot-kafka/README.md index 97b1791..d552274 100644 --- a/spring-boot/spring-boot-kafka/README.md +++ b/spring-boot/spring-boot-kafka/README.md @@ -1,399 +1,337 @@ -# spring boot 整合 kafka - -## 目录
-一、kafka的相关概念:
-    1.主题和分区
-    2.分区复制
-    3. 生产者
-    4. 消费者
-    5.broker和集群
-二、项目说明
-        1.1 项目结构说明
+# Spring Boot 整合 Kafka + + +## 一、项目说明 + +### 1.1 项目结构 + + 本项目提供 Kafka 发送简单消息、对象消息、和多消费者组消费消息三种情况下的 sample: + +- **kafkaSimpleConsumer** :用于普通消息的监听; +- **kafkaBeanConsumer** :用于对象消息的监听; +- **kafkaGroupConsumer** :用于多消费者组和多消费者对主题分区消息监听的情况。 + + + +
+### 1.2 主要依赖 + +```xml + + org.springframework.kafka + spring-kafka + + + org.springframework.kafka + spring-kafka-test + test + +``` + + + +## 二、 整合 Kafka + +### 2.1 基本配置 + +```yaml +spring: + kafka: + # 以逗号分隔的地址列表,用于建立与 Kafka 集群的初始连接 (kafka 默认的端口号为 9092) + bootstrap-servers: 127.0.0.1:9092 + producer: + # 发生错误后,消息重发的次数。 + retries: 0 + #当有多个消息需要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次可以使用的内存大小,按照字节数计算。 + batch-size: 16384 + # 设置生产者内存缓冲区的大小。 + buffer-memory: 33554432 + # 键的序列化方式 + key-serializer: org.apache.kafka.common.serialization.StringSerializer + # 值的序列化方式 + value-serializer: org.apache.kafka.common.serialization.StringSerializer + # acks=0 : 生产者在成功写入消息之前不会等待任何来自服务器的响应。 + # acks=1 : 只要集群的首领节点收到消息,生产者就会收到一个来自服务器成功响应。 + # acks=all :只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。 + acks: 1 + consumer: + # 自动提交的时间间隔 在 spring boot 2.X 版本中这里采用的是值的类型为 Duration 需要符合特定的格式,如 1S,1M,2H,5D + auto-commit-interval: 1S + # 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理: + # latest(默认值)在偏移量无效的情况下,消费者将从最新的记录开始读取数据(在消费者启动之后生成的记录) + # earliest :在偏移量无效的情况下,消费者将从起始位置读取分区的记录 + auto-offset-reset: earliest + # 是否自动提交偏移量,默认值是 true,为了避免出现重复数据和数据丢失,可以把它设置为 false,然后手动提交偏移量 + enable-auto-commit: true + # 键的反序列化方式 + key-deserializer: org.apache.kafka.common.serialization.StringDeserializer + # 值的反序列化方式 + value-deserializer: org.apache.kafka.common.serialization.StringDeserializer + listener: + # 在侦听器容器中运行的线程数。 + concurrency: 5 + +``` + +在 Spring Boot 2.x 后 auto-commit-interval(自动提交的时间间隔)采用的是值的类型为 Duration ,Duration 是 JDK 1.8 后引入的类,在其源码中我们可以看到对于其字符串的表达需要符合一定的规范,即数字 + 单位,如下的写法 1s ,1.5s, 0s, 0.001S ,1h, 2d 都是有效的。如果传入无效的字符串,则 Spring Boot 在启动阶段解析配置文件时就会抛出异常。 + +```java +public final class Duration + implements TemporalAmount, Comparable, Serializable { + + /** + * The pattern for parsing. + */ + private static final Pattern PATTERN = + Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)D)?" + + "(T(?:([-+]?[0-9]+)H)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)(?:[.,]([0-9]{0,9}))?S)?)?", Pattern.CASE_INSENSITIVE); + + ........ + +} +``` + +### 2.2 消息发送 + +使用 KafkaTemplate 来发送消息: + +```java +@Component +@Slf4j +public class KafKaCustomrProducer { + + @Autowired + private KafkaTemplate kafkaTemplate; + + public void sendMessage(String topic, Object object) { + + /* + * 这里的 ListenableFuture 类是 spring 对 java 原生 Future 的扩展增强,是一个泛型接口,用于监听异步方法的回调 + * 而对于 kafka send 方法返回值而言,这里的泛型所代表的实际类型就是 SendResult,而这里 K,V 的泛型实际上 + * 被用于 ProducerRecord producerRecord,即生产者发送消息的 key,value 类型 + */ + ListenableFuture> future = kafkaTemplate.send(topic, object); + + future.addCallback(new ListenableFutureCallback>() { + @Override + public void onFailure(Throwable throwable) { + log.info("发送消息失败:" + throwable.getMessage()); + } + + @Override + public void onSuccess(SendResult sendResult) { + System.out.println("发送结果:" + sendResult.toString()); + } + }); + } +} + +``` + +### 2.3 消息监听 + +使用 @KafkaListener 注解来实现消息的监听: + +```java +@Component +@Slf4j +public class KafkaSimpleConsumer { + + // 简单消费者 + @KafkaListener(groupId = "simpleGroup", topics = Topic.SIMPLE) + public void consumer1_1(ConsumerRecord record, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic, Consumer consumer) { + System.out.println("消费者收到消息:" + record.value() + "; topic:" + topic); + /* + * 如果需要手工提交异步 consumer.commitSync(); + * 手工同步提交 consumer.commitAsync() + */ + } +} +``` + +### 2.4 整合测试 + +```java +@Slf4j +@RestController +public class SendMsgController { + + @Autowired + private KafKaCustomrProducer producer; + @Autowired + private KafkaTemplate kafkaTemplate; + + /*** + * 发送消息体为基本类型的消息 + */ + @GetMapping("sendSimple") + public void sendSimple() { + producer.sendMessage(Topic.SIMPLE, "hello spring boot kafka"); + } +} +``` + + + +## 三、多消费者组测试 + +### 3.1 创建多分区主题 + +```java +@Configuration +public class KafkaConfig { + + @Bean + public NewTopic groupTopic() { + // 指定主题名称,分区数量,和复制因子 + return new NewTopic(Topic.GROUP, 10, (short) 2); + } +} +``` + +### 3.2 消息监听 + +创建多消费者,并监听同一主题的不同分区: + +- 消费者 1-1 监听主题的 0、1 分区 +- 消费者 1-2 监听主题的 2、3 分区 +- 消费者 1-3 监听主题的 0、1 分区 +- 消费者 2-1 监听主题的所有分区 + +```java +@Component +@Slf4j +public class KafkaGroupConsumer { + + // 分组 1 中的消费者 1 + @KafkaListener(id = "consumer1-1", groupId = "group1", topicPartitions = + {@TopicPartition(topic = Topic.GROUP, partitions = {"0", "1"}) + }) + public void consumer1_1(ConsumerRecord record) { + System.out.println("consumer1-1 收到消息:" + record.value()); + } + + // 分组 1 中的消费者 2 + @KafkaListener(id = "consumer1-2", groupId = "group1", topicPartitions = + {@TopicPartition(topic = Topic.GROUP, partitions = {"2", "3"}) + }) + public void consumer1_2(ConsumerRecord record) { + System.out.println("consumer1-2 收到消息:" + record.value()); + } + + // 分组 1 中的消费者 3 + @KafkaListener(id = "consumer1-3", groupId = "group1", topicPartitions = + {@TopicPartition(topic = Topic.GROUP, partitions = {"0", "1"}) + }) + public void consumer1_3(ConsumerRecord record) { + System.out.println("consumer1-3 收到消息:" + record.value()); + } + + // 分组 2 中的消费者 + @KafkaListener(id = "consumer2-1", groupId = "group2", topics = Topic.GROUP) + public void consumer2_1(ConsumerRecord record) { + System.err.println("consumer2-1 收到消息:" + record.value()); + } +} + +``` + +### 3.3 消息发送 + +发送消息时候指定主题的具体分区: + +```java +@GetMapping("sendGroup") +public void sendGroup() { + for (int i = 0; i < 4; i++) { + // 第二个参数指定分区,第三个参数指定消息键 分区优先 + ListenableFuture> future = kafkaTemplate.send(Topic.GROUP, i % 4, "key", "hello group " + i); + future.addCallback(new ListenableFutureCallback>() { + @Override + public void onFailure(Throwable throwable) { + log.info("发送消息失败:" + throwable.getMessage()); + } + + @Override + public void onSuccess(SendResult sendResult) { + System.out.println("发送结果:" + sendResult.toString()); + } + }); + } +} +``` + +测试结果: + +```yaml +# 主要看每次发送结果中的 partition 属性,代表四次消息分别发送到了主题的0,1,2,3分区 +发送结果:SendResult [producerRecord=ProducerRecord(topic=spring.boot.kafka.newGroup, partition=1, headers=RecordHeaders(headers = [], isReadOnly = true), key=key, value=hello group 1, timestamp=null), recordMetadata=spring.boot.kafka.newGroup-1@13] +发送结果:SendResult [producerRecord=ProducerRecord(topic=spring.boot.kafka.newGroup, partition=0, headers=RecordHeaders(headers = [], isReadOnly = true), key=key, value=hello group 0, timestamp=null), recordMetadata=spring.boot.kafka.newGroup-0@19] +发送结果:SendResult [producerRecord=ProducerRecord(topic=spring.boot.kafka.newGroup, partition=3, headers=RecordHeaders(headers = [], isReadOnly = true), key=key, value=hello group 3, timestamp=null), recordMetadata=spring.boot.kafka.newGroup-3@13] +发送结果:SendResult [producerRecord=ProducerRecord(topic=spring.boot.kafka.newGroup, partition=2, headers=RecordHeaders(headers = [], isReadOnly = true), key=key, value=hello group 2, timestamp=null), recordMetadata=spring.boot.kafka.newGroup-2@13] +# 消费者组2 接收情况 +consumer2-1 收到消息:hello group 1 +consumer2-1 收到消息:hello group 0 +consumer2-1 收到消息:hello group 2 +consumer2-1 收到消息:hello group 3 +# 消费者1-1接收情况 +consumer1-1 收到消息:hello group 1 +consumer1-1 收到消息:hello group 0 +# 消费者1-3接收情况 +consumer1-3 收到消息:hello group 1 +consumer1-3 收到消息:hello group 0 +# 消费者1-2接收情况 +consumer1-2 收到消息:hello group 3 +consumer1-2 收到消息:hello group 2 +``` + +### 3.4 测试结果 + +- 和 Kafka 原本的机制一样,多消费者组之间对于同一个主题的消费彼此之间互不影响; +- 和 Kafka 原本机制不一样的是,这里我们消费者 1-1 和消费 1-3 共同属于同一个消费者组,并且监听同样的分区,按照 Kafka 原本的机制,群组保证每个分区只能被同一个消费者组的一个消费者使用,但是按照 Spring 的方式实现消息监听后,被两个消费者都监听到了。 + + + +## 四、序列化与反序列化 + +用例采用的是第三方 fastjson 将实体类序列化为 Json 后发送。实现如下: + +```java +/*** + * 发送消息体为 bean 的消息 + */ +@GetMapping("sendBean") +public void sendBean() { + Programmer programmer = new Programmer("xiaoming", 12, 21212.33f, new Date()); + producer.sendMessage(Topic.BEAN, JSON.toJSON(programmer).toString()); +} + +``` + +```java +@Component +@Slf4j +public class KafkaBeanConsumer { + + @KafkaListener(groupId = "beanGroup",topics = Topic.BEAN) + public void consumer(ConsumerRecord record) { + System.out.println("消费者收到消息:" + JSON.parseObject(record.value().toString(), Programmer.class)); + } +} +``` + diff --git a/spring-boot/spring-boot-memcached/README.md b/spring-boot/spring-boot-memcached/README.md index 1996007..d8a72f6 100644 --- a/spring-boot/spring-boot-memcached/README.md +++ b/spring-boot/spring-boot-memcached/README.md @@ -1,164 +1,152 @@ -# spring boot 整合 mecached - -## 目录
-一、说明
-    1.1 XMemcached客户端说明
-    1.2 项目结构说明
-    1.3 主要依赖
-二、spring boot 整合 memcached
+# Spring Boot 整合 Memcached + + + + - - - -## 一、说明 - -### 1.1 XMemcached客户端说明 - -spring boot 官方并没有提供关于 memcached 的 starter,所以我们这里还是采用 XMemcached 作为客户端进行整合。 XMemcached 是基于 java nio 的 memcached 高性能客户端,支持完整的 memcached 协议,支持客户端分布并且提供了一致性哈希 (consistent hash) 算法的实现。 - -### 1.2 项目结构说明 - -memcached 的整合配置位于 config 文件夹下。 - -
- -### 1.3 主要依赖 - -```xml - - - com.googlecode.xmemcached - xmemcached - 2.4.5 - -``` - - - -## 二、spring boot 整合 memcached - -#### 2.1 单机配置 - -```java -@Bean -public MemcachedClient memcachedClient() { - XMemcachedClientBuilder builder = new XMemcachedClientBuilder("192.168.200.201:11211"); - MemcachedClient memcachedClient = null; -try { - memcachedClient = builder.build(); - } catch (IOException e) { - e.printStackTrace(); -} - return memcachedClient; -} -``` - -#### 2.2 集群配置 - -```java -@Bean -public MemcachedClient memcachedClientForCluster() { - - List addressList = new ArrayList(); - addressList.add(new InetSocketAddress("192.168.200.201", 11211)); - addressList.add(new InetSocketAddress("192.168.200.201", 11212)); - // 赋予权重 - int[] weights = {1, 2}; - XMemcachedClientBuilder builder = new XMemcachedClientBuilder(addressList, weights); - // 设置连接池大小 - builder.setConnectionPoolSize(10); - // 协议工厂 - builder.setCommandFactory(new TextCommandFactory()); - // 分布策略,一致性哈希 KetamaMemcachedSessionLocator 或者 ArraySessionLocator(默认) - builder.setSessionLocator(new KetamaMemcachedSessionLocator()); - // 设置序列化器 - builder.setTranscoder(new SerializingTranscoder()); - MemcachedClient memcachedClient = null; - try { - memcachedClient = builder.build(); - } catch (IOException e) { - e.printStackTrace(); - } - return memcachedClient; -} -``` - -#### 2.3 存储基本类型测试用例 - -xmemcached 单机版本和集群版本注入的实例是相同的。 - -```java -/** - * @author : heibaiying - * @description : Memcached 操作基本对象 - */ -@RunWith(SpringRunner.class) -@SpringBootTest -public class MemSamples { - - @Autowired - private MemcachedClient memcachedClient; - - @Test - public void operate() throws InterruptedException, MemcachedException, TimeoutException { - memcachedClient.set("hello", 0, "Hello,cluster xmemcached"); - String value = memcachedClient.get("hello"); - System.out.println("hello=" + value); - memcachedClient.delete("hello"); - value = memcachedClient.get("hello"); - System.out.println("hello=" + value); - } -} -``` - -#### 2.5 存储实体对象测试用例 - -```java -/** - * @author : heibaiying - * @description :Memcached 序列化与反序列化 - */ -@RunWith(SpringRunner.class) -@SpringBootTest -public class MemObjectSamples { - - @Autowired - private MemcachedClient memcachedClient; - - @Test - public void operate() throws InterruptedException, MemcachedException, TimeoutException { - memcachedClient.set("programmer", 0, new Programmer("xiaoming", 12, 5000.21f, new Date())); - Programmer programmer = memcachedClient.get("programmer"); - System.out.println("hello ," + programmer.getName()); - memcachedClient.delete("programmer"); - programmer = memcachedClient.get("programmer"); - Assert.assertNull(programmer); - } -} - -``` - - - -## 附:memcached 基本命令 - -| 命令 | 格式 | 说明 | -| --------------- | -------------------------------------------------- | ------------------------------------- | -| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 | -| 新增 add | add key flags exTime length -> value | 只有当 key 不存在的情况下,才可以插入 | -| 替换 replace | replace key flags exTime length -> value | 只修改已存在 key 的 value 值 | -| 追加内容 append | append key flags exTime length -> value | length 表示追加的长度而不是总长度 | -| 前面追加 prepend | prepend key flags exTime length -> value | length 表示追加的长度而不是总长度 | -| 查询操作 get | get key | | -| 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 | -| 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 | -| 删除 delete | delete key | 将数据打一个删除标记 | -| 自增 incr | incr key 增加偏移量 | incr 和 decr 只能操作能转换为数字的 Value | -| 自减 decr | decr key 减少偏移量 | desr 不能将数字减少至 0 以下 | -| 清库 | flush_all | | +## 一、项目说明 + +### 1.1 XMemcached + +Spring 官方并没有提供关于 Memcached 的 starter,所以我们还是采用 XMemcached 作为客户端进行整合。XMemcached 是基于 Java NIO 的 Memcached 高性能客户端,支持完整的 Memcached 协议,支持客户端分布并且提供了一致性哈希 (consistent hash) 算法的实现。 + +### 1.2 项目结构 + +Memcached 的整合配置位于 config 文件夹下: + +
+### 1.3 基本依赖 + +```xml + + + com.googlecode.xmemcached + xmemcached + 2.4.5 + +``` + + + +## 二、整合 Memcached + +### 2.1 单机配置 + +```java +@Bean +public MemcachedClient memcachedClient() { + XMemcachedClientBuilder builder = new XMemcachedClientBuilder("192.168.200.201:11211"); + MemcachedClient memcachedClient = null; +try { + memcachedClient = builder.build(); + } catch (IOException e) { + e.printStackTrace(); +} + return memcachedClient; +} +``` + +### 2.2 集群配置 + +```java +@Bean +public MemcachedClient memcachedClientForCluster() { + + List addressList = new ArrayList(); + addressList.add(new InetSocketAddress("192.168.200.201", 11211)); + addressList.add(new InetSocketAddress("192.168.200.201", 11212)); + // 赋予权重 + int[] weights = {1, 2}; + XMemcachedClientBuilder builder = new XMemcachedClientBuilder(addressList, weights); + // 设置连接池大小 + builder.setConnectionPoolSize(10); + // 协议工厂 + builder.setCommandFactory(new TextCommandFactory()); + // 分布策略,一致性哈希 KetamaMemcachedSessionLocator 或者 ArraySessionLocator(默认) + builder.setSessionLocator(new KetamaMemcachedSessionLocator()); + // 设置序列化器 + builder.setTranscoder(new SerializingTranscoder()); + MemcachedClient memcachedClient = null; + try { + memcachedClient = builder.build(); + } catch (IOException e) { + e.printStackTrace(); + } + return memcachedClient; +} +``` + +### 2.3 存储基本类型 + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class MemSamples { + + @Autowired + private MemcachedClient memcachedClient; + + @Test + public void operate() throws InterruptedException, MemcachedException, TimeoutException { + memcachedClient.set("hello", 0, "Hello,cluster xmemcached"); + String value = memcachedClient.get("hello"); + System.out.println("hello=" + value); + memcachedClient.delete("hello"); + value = memcachedClient.get("hello"); + System.out.println("hello=" + value); + } +} +``` + +### 2.4 存储实体对象 + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class MemObjectSamples { + + @Autowired + private MemcachedClient memcachedClient; + + @Test + public void operate() throws InterruptedException, MemcachedException, TimeoutException { + memcachedClient.set("programmer", 0, new Programmer("xiaoming", 12, 5000.21f, new Date())); + Programmer programmer = memcachedClient.get("programmer"); + System.out.println("hello ," + programmer.getName()); + memcachedClient.delete("programmer"); + programmer = memcachedClient.get("programmer"); + Assert.assertNull(programmer); + } +} + +``` + + + +## 附:Memcached 基本命令 + +| 命令 | 格式 | 说明 | +| --------------- | -------------------------------------------------- | ------------------------------------- | +| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 | +| 新增 add | add key flags exTime length -> value | 只有当 key 不存在的情况下,才可以插入 | +| 替换 replace | replace key flags exTime length -> value | 只修改已存在 key 的 value 值 | +| 追加内容 append | append key flags exTime length -> value | length 表示追加的长度而不是总长度 | +| 前面追加 prepend | prepend key flags exTime length -> value | length 表示追加的长度而不是总长度 | +| 查询操作 get | get key | | +| 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 | +| 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 | +| 删除 delete | delete key | 将数据打一个删除标记 | +| 自增 incr | incr key 增加偏移量 | incr 和 decr 只能操作能转换为数字的 Value | +| 自减 decr | decr key 减少偏移量 | desr 不能将数字减少至 0 以下 | +| 清库 | flush_all | | diff --git a/spring-boot/spring-boot-mongodb/README.md b/spring-boot/spring-boot-mongodb/README.md index fc66938..b46f5cb 100644 --- a/spring-boot/spring-boot-mongodb/README.md +++ b/spring-boot/spring-boot-mongodb/README.md @@ -1,195 +1,193 @@ -# spring boot 整合 mongodb - -## 目录
-一、说明
-        1.1 用例结构
-        1.2 项目主要依赖
-二、整合 mongodb
-        2.1 在application.yml 中配置mongodb数据源
-        2.2 基于MongoTemplate实现对mongodb的操作
-        2.3 使用 data jpa 方式操作mongodb (推荐使用)
-## 正文
- - - - -## 一、说明 - -#### 1.1 用例结构 - -1. 本用例提供 mongdb 的简单整合用例; -2. 提供用 MongoTemplate 的方式操作 mongdb,见测试用例 MongoOriginalTests.java -3. 提供基于 spring data jpa 的方式操作 mongodb(推荐),见测试用例 MongoJPATests.java - -
- -#### 1.2 项目主要依赖 - -```xml - - org.springframework.boot - spring-boot-starter-data-mongodb - -``` - - - -## 二、整合 mongodb - -#### 2.1 在application.yml 中配置mongodb数据源 - -```yaml -spring: - data: - mongodb: - database: spring - uri: mongodb://192.168.0.108:27017 -``` - -#### 2.2 基于MongoTemplate实现对mongodb的操作 - -```java -@RunWith(SpringRunner.class) -@SpringBootTest -public class MongoOriginalTests { - - @Autowired - private MongoTemplate mongoTemplate; - - @Test - public void insert() { - // 单条插入 - mongoTemplate.insert(new Programmer("xiaoming", 12, 5000.21f, new Date())); - List programmers = new ArrayList(); - // 批量插入 - programmers.add(new Programmer("xiaohong", 21, 52200.21f, new Date())); - programmers.add(new Programmer("xiaolan", 34, 500.21f, new Date())); - mongoTemplate.insert(programmers, Programmer.class); - } - - // 条件查询 - @Test - public void select() { - Criteria criteria = new Criteria(); - criteria.andOperator(where("name").is("xiaohong"), where("age").is(21)); - Query query = new Query(criteria); - Programmer one = mongoTemplate.findOne(query, Programmer.class); - System.out.println(one); - } - - - // 更新数据 - @Test - public void MUpdate() { - UpdateResult updateResult = mongoTemplate.updateMulti(query(where("name").is("xiaoming")), update("age", 35), Programmer.class); - System.out.println("更新记录数:" + updateResult.getModifiedCount()); - } - - // 删除指定数据 - @Test - public void delete() { - DeleteResult result = mongoTemplate.remove(query(where("name").is("xiaolan")), Programmer.class); - System.out.println("影响记录数:" + result.getDeletedCount()); - System.out.println("是否成功:" + result.wasAcknowledged()); - } - -} -``` - -#### 2.3 使用 data jpa 方式操作mongodb (推荐使用) - -1.新建查询结构,查询方法按照支持的关键字命名 - -```java -public interface ProgrammerRepository extends MongoRepository { - - void deleteAllByName(String name); - - Programmer findAllByName(String names); - - Programmer findByNameAndAge(String name, int age); - -} -``` - -2.测试 - -```java -@RunWith(SpringRunner.class) -@SpringBootTest -public class MongoJPATests { - - @Autowired - private ProgrammerRepository repository; - - @Test - public void insert() { - // 单条插入 - repository.save(new Programmer("python", 23, 21832.34f, new Date())); - // 批量插入 - List programmers = new ArrayList(); - programmers.add(new Programmer("java", 21, 52200.21f, new Date())); - programmers.add(new Programmer("Go", 34, 500.21f, new Date())); - repository.saveAll(programmers); - } - - // 条件查询 - @Test - public void select() { - Programmer java = repository.findByNameAndAge("java", 21); - Assert.assertEquals(java.getSalary(), 52200.21f, 0.01); - } - - - // 更新数据 - @Test - public void MUpdate() { - repository.save(new Programmer("Go", 8, 500.21f, new Date())); - Programmer go = repository.findAllByName("Go"); - Assert.assertEquals(go.getAge(), 8); - } - - // 删除指定数据 - @Test - public void delete() { - repository.deleteAllByName("python"); - Optional python = repository.findById("python"); - Assert.assertFalse(python.isPresent()); - } - -} -``` - -查询方法支持的关键字如下,更多命名规范可以参见 Spring Data MongoDB 官方文档[Query Methods](https://docs.spring.io/spring-data/mongodb/docs/2.1.3.RELEASE/reference/html/#mongodb.repositories.queries): - -| Keyword | Sample | Logical result | -| ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | -| `After` | `findByBirthdateAfter(Date date)` | `{"birthdate" : {"$gt" : date}}` | -| `GreaterThan` | `findByAgeGreaterThan(int age)` | `{"age" : {"$gt" : age}}` | -| `GreaterThanEqual` | `findByAgeGreaterThanEqual(int age)` | `{"age" : {"$gte" : age}}` | -| `Before` | `findByBirthdateBefore(Date date)` | `{"birthdate" : {"$lt" : date}}` | -| `LessThan` | `findByAgeLessThan(int age)` | `{"age" : {"$lt" : age}}` | -| `LessThanEqual` | `findByAgeLessThanEqual(int age)` | `{"age" : {"$lte" : age}}` | -| `Between` | `findByAgeBetween(int from, int to)` | `{"age" : {"$gt" : from, "$lt" : to}}` | -| `In` | `findByAgeIn(Collection ages)` | `{"age" : {"$in" : [ages…]}}` | -| `NotIn` | `findByAgeNotIn(Collection ages)` | `{"age" : {"$nin" : [ages…]}}` | -| `IsNotNull`, `NotNull` | `findByFirstnameNotNull()` | `{"firstname" : {"$ne" : null}}` | -| `IsNull`, `Null` | `findByFirstnameNull()` | `{"firstname" : null}` | -| `Like`, `StartingWith`, `EndingWith` | `findByFirstnameLike(String name)` | `{"firstname" : name} (name as regex)` | -| `NotLike`, `IsNotLike` | `findByFirstnameNotLike(String name)` | `{"firstname" : { "$not" : name }} (name as regex)` | -| `Containing` on String | `findByFirstnameContaining(String name)` | `{"firstname" : name} (name as regex)` | -| `NotContaining` on String | `findByFirstnameNotContaining(String name)` | `{"firstname" : { "$not" : name}} (name as regex)` | -| `Containing` on Collection | `findByAddressesContaining(Address address)` | `{"addresses" : { "$in" : address}}` | -| `NotContaining` on Collection | `findByAddressesNotContaining(Address address)` | `{"addresses" : { "$not" : { "$in" : address}}}` | -| `Regex` | `findByFirstnameRegex(String firstname)` | `{"firstname" : {"$regex" : firstname }}` | -| `(No keyword)` | `findByFirstname(String name)` | `{"firstname" : name}` | -| `Not` | `findByFirstnameNot(String name)` | `{"firstname" : {"$ne" : name}}` | -| `Near` | `findByLocationNear(Point point)` | `{"location" : {"$near" : [x,y]}}` | -| `Near` | `findByLocationNear(Point point, Distance max)` | `{"location" : {"$near" : [x,y], "$maxDistance" : max}}` | -| `Near` | `findByLocationNear(Point point, Distance min, Distance max)` | `{"location" : {"$near" : [x,y], "$minDistance" : min, "$maxDistance" : max}}` | -| `Within` | `findByLocationWithin(Circle circle)` | `{"location" : {"$geoWithin" : {"$center" : [ [x, y], distance]}}}` | -| `Within` | `findByLocationWithin(Box box)` | `{"location" : {"$geoWithin" : {"$box" : [ [x1, y1], x2, y2]}}}` | -| `IsTrue`, `True` | `findByActiveIsTrue()` | `{"active" : true}` | -| `IsFalse`, `False` | `findByActiveIsFalse()` | `{"active" : false}` | -| `Exists` | `findByLocationExists(boolean exists)` | `{"location" : {"$exists" : exists }}` | +# Spring Boot 整合 MongoDB + + +## 一、项目说明 + +### 1.1 项目结构 + +- 本用例提供 MongoDB 的简单整合用例; +- 提供基于 MongoTemplate 的方式操作 MongoDB,见测试用例 MongoOriginalTests; +- 提供基于 Spring Data JPA 的方式操作 MongoDB (推荐),见测试用例 MongoJPATests。 + +
+### 1.2 基本依赖 + +```xml + + org.springframework.boot + spring-boot-starter-data-mongodb + +``` + + + +## 二、整合 MongoDB + +### 2.1 配置数据源 + +```yaml +spring: + data: + mongodb: + database: spring + uri: mongodb://192.168.0.108:27017 +``` + +### 2.2 MongoTemplate + +基于 MongoTemplate 实现对 MongoDB 的操作: + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class MongoOriginalTests { + + @Autowired + private MongoTemplate mongoTemplate; + + @Test + public void insert() { + // 单条插入 + mongoTemplate.insert(new Programmer("xiaoming", 12, 5000.21f, new Date())); + List programmers = new ArrayList(); + // 批量插入 + programmers.add(new Programmer("xiaohong", 21, 52200.21f, new Date())); + programmers.add(new Programmer("xiaolan", 34, 500.21f, new Date())); + mongoTemplate.insert(programmers, Programmer.class); + } + + // 条件查询 + @Test + public void select() { + Criteria criteria = new Criteria(); + criteria.andOperator(where("name").is("xiaohong"), where("age").is(21)); + Query query = new Query(criteria); + Programmer one = mongoTemplate.findOne(query, Programmer.class); + System.out.println(one); + } + + + // 更新数据 + @Test + public void MUpdate() { + UpdateResult updateResult = mongoTemplate.updateMulti(query(where("name").is("xiaoming")), update("age", 35), Programmer.class); + System.out.println("更新记录数:" + updateResult.getModifiedCount()); + } + + // 删除指定数据 + @Test + public void delete() { + DeleteResult result = mongoTemplate.remove(query(where("name").is("xiaolan")), Programmer.class); + System.out.println("影响记录数:" + result.getDeletedCount()); + System.out.println("是否成功:" + result.wasAcknowledged()); + } + +} +``` + +### 2.3 Spring Data JPA + +使用 Spring Data JPA 时,只需要将查询方法按照 Spring 的规范命令即可: + +```java +public interface ProgrammerRepository extends MongoRepository { + + void deleteAllByName(String name); + + Programmer findAllByName(String names); + + Programmer findByNameAndAge(String name, int age); + +} +``` + +单元测试: + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class MongoJPATests { + + @Autowired + private ProgrammerRepository repository; + + @Test + public void insert() { + // 单条插入 + repository.save(new Programmer("python", 23, 21832.34f, new Date())); + // 批量插入 + List programmers = new ArrayList(); + programmers.add(new Programmer("java", 21, 52200.21f, new Date())); + programmers.add(new Programmer("Go", 34, 500.21f, new Date())); + repository.saveAll(programmers); + } + + // 条件查询 + @Test + public void select() { + Programmer java = repository.findByNameAndAge("java", 21); + Assert.assertEquals(java.getSalary(), 52200.21f, 0.01); + } + + + // 更新数据 + @Test + public void MUpdate() { + repository.save(new Programmer("Go", 8, 500.21f, new Date())); + Programmer go = repository.findAllByName("Go"); + Assert.assertEquals(go.getAge(), 8); + } + + // 删除指定数据 + @Test + public void delete() { + repository.deleteAllByName("python"); + Optional python = repository.findById("python"); + Assert.assertFalse(python.isPresent()); + } + +} +``` + +查询方法所有支持的关键字如下,更多命名规范可以参见 Spring Data MongoDB 官方文档 [Query Methods](https://docs.spring.io/spring-data/mongodb/docs/2.1.3.RELEASE/reference/html/#mongodb.repositories.queries): + +| Keyword | Sample | Logical result | +| ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| `After` | `findByBirthdateAfter(Date date)` | `{"birthdate" : {"$gt" : date}}` | +| `GreaterThan` | `findByAgeGreaterThan(int age)` | `{"age" : {"$gt" : age}}` | +| `GreaterThanEqual` | `findByAgeGreaterThanEqual(int age)` | `{"age" : {"$gte" : age}}` | +| `Before` | `findByBirthdateBefore(Date date)` | `{"birthdate" : {"$lt" : date}}` | +| `LessThan` | `findByAgeLessThan(int age)` | `{"age" : {"$lt" : age}}` | +| `LessThanEqual` | `findByAgeLessThanEqual(int age)` | `{"age" : {"$lte" : age}}` | +| `Between` | `findByAgeBetween(int from, int to)` | `{"age" : {"$gt" : from, "$lt" : to}}` | +| `In` | `findByAgeIn(Collection ages)` | `{"age" : {"$in" : [ages…]}}` | +| `NotIn` | `findByAgeNotIn(Collection ages)` | `{"age" : {"$nin" : [ages…]}}` | +| `IsNotNull`, `NotNull` | `findByFirstnameNotNull()` | `{"firstname" : {"$ne" : null}}` | +| `IsNull`, `Null` | `findByFirstnameNull()` | `{"firstname" : null}` | +| `Like`, `StartingWith`, `EndingWith` | `findByFirstnameLike(String name)` | `{"firstname" : name} (name as regex)` | +| `NotLike`, `IsNotLike` | `findByFirstnameNotLike(String name)` | `{"firstname" : { "$not" : name }} (name as regex)` | +| `Containing` on String | `findByFirstnameContaining(String name)` | `{"firstname" : name} (name as regex)` | +| `NotContaining` on String | `findByFirstnameNotContaining(String name)` | `{"firstname" : { "$not" : name}} (name as regex)` | +| `Containing` on Collection | `findByAddressesContaining(Address address)` | `{"addresses" : { "$in" : address}}` | +| `NotContaining` on Collection | `findByAddressesNotContaining(Address address)` | `{"addresses" : { "$not" : { "$in" : address}}}` | +| `Regex` | `findByFirstnameRegex(String firstname)` | `{"firstname" : {"$regex" : firstname }}` | +| `(No keyword)` | `findByFirstname(String name)` | `{"firstname" : name}` | +| `Not` | `findByFirstnameNot(String name)` | `{"firstname" : {"$ne" : name}}` | +| `Near` | `findByLocationNear(Point point)` | `{"location" : {"$near" : [x,y]}}` | +| `Near` | `findByLocationNear(Point point, Distance max)` | `{"location" : {"$near" : [x,y], "$maxDistance" : max}}` | +| `Near` | `findByLocationNear(Point point, Distance min, Distance max)` | `{"location" : {"$near" : [x,y], "$minDistance" : min, "$maxDistance" : max}}` | +| `Within` | `findByLocationWithin(Circle circle)` | `{"location" : {"$geoWithin" : {"$center" : [ [x, y], distance]}}}` | +| `Within` | `findByLocationWithin(Box box)` | `{"location" : {"$geoWithin" : {"$box" : [ [x1, y1], x2, y2]}}}` | +| `IsTrue`, `True` | `findByActiveIsTrue()` | `{"active" : true}` | +| `IsFalse`, `False` | `findByActiveIsFalse()` | `{"active" : false}` | +| `Exists` | `findByLocationExists(boolean exists)` | `{"location" : {"$exists" : exists }}` | + diff --git a/spring-boot/spring-boot-mybatis/README.md b/spring-boot/spring-boot-mybatis/README.md index 1126da4..e792146 100644 --- a/spring-boot/spring-boot-mybatis/README.md +++ b/spring-boot/spring-boot-mybatis/README.md @@ -1,262 +1,252 @@ -# spring boot 整合 mybatis - -## 目录
-一、说明
+# Spring Boot 整合 Mybatis + + +## 一、项目说明 + +### 1.1 项目结构 + +- 项目涉及表的建表语句放置在 resources 的 sql 文件夹下; + +- 关于 Mybatis SQL 的写法提供两种方式: + + **xml 写法**:对应的类为 ProgrammerMapper.java 和 programmerMapper.xml,用 MybatisXmlTest 进行测试; + + **注解写法**:对应的类为 Programmer.java ,用 MybatisAnnotationTest 进行测试。 + +
+### 1.2 主要依赖 + +按照 Spring 官方对于自定义的 starter 命名规范的要求: + +- 官方的 starter 命名:spring-boot-starter-XXXX +- 其他第三方 starter 命名:XXXX-spring-boot-starte + +所以 Mybatis 的 starter 命名为 mybatis-spring-boot-starter,如果有自定义 starter 需求,也需要按照此命名规则进行命名。 + +```xml + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + + mysql + mysql-connector-java + 8.0.13 + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + +``` + +spring boot 与 mybatis 版本的对应关系: + +| MyBatis-Spring-Boot-Starter | [MyBatis-Spring](http://www.mybatis.org/spring/index.html#Requirements) | Spring Boot | +| --------------------------- | ------------------------------------------------------------ | ------------- | +| **1.3.x (1.3.1)** | 1.3 or higher | 1.5 or higher | +| **1.2.x (1.2.1)** | 1.3 or higher | 1.4 or higher | +| **1.1.x (1.1.1)** | 1.3 or higher | 1.3 or higher | +| **1.0.x (1.0.2)** | 1.2 or higher | 1.3 or higher | + +## 二、整合 Mybatis + +### 2.1 配置数据源 + +Spring Boot 2.x 版本默认采用 Hikari 作为数据库连接池,Hikari 是目前 Java 平台性能最好的连接池,性能好于 druid: + +```yaml +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false + username: root + password: root + driver-class-name: com.mysql.cj.jdbc.Driver + + # 如果不想配置对数据库连接池做特殊配置的话,以下关于连接池的配置就不是必须的 + # spring-boot 2 默认采用高性能的 Hikari 作为连接池 更多配置可以参考 https://github.com/brettwooldridge/HikariCP#configuration-knobs-baby + type: com.zaxxer.hikari.HikariDataSource + hikari: + # 池中维护的最小空闲连接数 + minimum-idle: 10 + # 池中最大连接数,包括闲置和使用中的连接 + maximum-pool-size: 20 + # 此属性控制从池返回的连接的默认自动提交行为。默认为 true + auto-commit: true + # 允许最长空闲时间 + idle-timeout: 30000 + # 此属性表示连接池的用户定义名称,主要显示在日志记录和 JMX 管理控制台中,以标识池和池配置。 默认值:自动生成 + pool-name: custom-hikari + #此属性控制池中连接的最长生命周期,值 0 表示无限生命周期,默认 1800000 即 30 分钟 + max-lifetime: 1800000 + # 数据库连接超时时间,默认 30 秒,即 30000 + connection-timeout: 30000 + # 连接测试 sql 这个地方需要根据数据库方言差异而配置 例如 oracle 就应该写成 select 1 from dual + connection-test-query: SELECT 1 + +# mybatis 相关配置 +mybatis: + # 指定 sql xml 文件的位置 + mapper-locations: classpath*:mappers/*.xml + configuration: + # 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 + # oracle 数据库建议配置为 JdbcType.NULL, 默认是 Other + jdbc-type-for-null: 'null' + # 是否打印 sql 语句 调试的时候可以开启 + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl +``` + +### 2.2 XML 方式 + +新建 ProgrammerMapper.java 和 programmerMapper.xml,及其测试类: + +```java +@Mapper +public interface ProgrammerMapper { + + void save(Programmer programmer); + + Programmer selectById(int id); + + int modify(Programmer programmer); + + void delete(int id); +} +``` + +```xml + + + + + + insert into programmer (name, age, salary, birthday) VALUES (#{name}, #{age}, #{salary}, #{birthday}) + + + + + + update programmer set name=#{name},age=#{age},salary=#{salary},birthday=#{birthday} where id=#{id} + + + + delete from programmer where id = #{id} + + + +``` + +测试类: + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class MybatisXmlTest { + + @Autowired + private ProgrammerMapper mapper; + + @Test + public void save() { + mapper.save(new Programmer("xiaominng", 12, 3467.34f, new Date())); + mapper.save(new Programmer("xiaominng", 12, 3467.34f, new Date())); + } + + @Test + public void modify() { + mapper.modify(new Programmer(1, "xiaohong", 112, 347.34f, new Date())); + } + + @Test + public void selectByCondition() { + Programmer programmers = mapper.selectById(1); + System.out.println(programmers); + } + + @Test + public void delete() { + mapper.delete(2); + Programmer programmers = mapper.selectById(2); + Assert.assertNull(programmers); + } +} +``` + +### 2.3 注解方式 + +```java +@Mapper +public interface ProgrammerDao { + + @Insert("insert into programmer (name, age, salary, birthday) VALUES (#{name}, #{age}, #{salary}, #{birthday})") + void save(Programmer programmer); + + @Select("select * from programmer where name = #{id}") + Programmer selectById(int id); + + @Update("update programmer set name=#{name},age=#{age},salary=#{salary},birthday=#{birthday} where id=#{id}") + int modify(Programmer programmer); + + @Delete(" delete from programmer where id = #{id}") + void delete(int id); +} +``` + +测试类: + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class MybatisAnnotationTest { + + @Autowired + private ProgrammerDao programmerDao; + + @Test + public void save() { + programmerDao.save(new Programmer("xiaominng", 12, 3467.34f, new Date())); + programmerDao.save(new Programmer("xiaominng", 12, 3467.34f, new Date())); + } + + @Test + public void modify() { + programmerDao.modify(new Programmer(1, "xiaolan", 21, 347.34f, new Date())); + } + + @Test + public void selectByCondition() { + Programmer programmers = programmerDao.selectById(1); + System.out.println(programmers); + } + + @Test + public void delete() { + programmerDao.delete(3); + Programmer programmers = programmerDao.selectById(3); + Assert.assertNull(programmers); + } +} + +``` + diff --git a/spring-boot/spring-boot-rabbitmq/README.md b/spring-boot/spring-boot-rabbitmq/README.md index f2d8eb7..597d619 100644 --- a/spring-boot/spring-boot-rabbitmq/README.md +++ b/spring-boot/spring-boot-rabbitmq/README.md @@ -1,39 +1,32 @@ -# spring boot 整合 rabbitmq - -## 目录
-一、 项目结构说明
-二、关键依赖
-三、公共模块(rabbitmq-common)
-四、服务消费者(rabbitmq-consumer)
-        4.1 消息消费者配置
-        4.2 使用注解@RabbitListener和@RabbitHandler创建消息监听者
-五、 消息生产者(rabbitmq-producer)
-        5.1 消息生产者配置
-        5.2 创建消息生产者
-        5.3 以单元测试的方式发送消息
-六、项目构建的说明
-## 正文
+# Spring Boot 整合 RabbitMQ + +## 一、 项目结构 -## 一、 项目结构说明 +之前关于 Spring 整合 RabbitMQ 我们采用的是单项目的方式,为了使得用例更具有实际意义,这里采用 Maven 多模块的构建方式,在 spring-boot-rabbitmq 下构建三个子模块: -1.1 之前关于 spring 整合 rabbitmq 我们采用的是单项目的方式,为了使得用例更具有实际意义,这里采用 maven 多模块的构建方式,在 spring-boot-rabbitmq 下构建三个子模块: - -1. rabbitmq-common 是公共模块,用于存放公共的接口、配置和 bean,被 rabbitmq-producer 和 rabbitmq-consumer 在 pom.xml 中引用; -2. rabbitmq-producer 是消息的生产者模块; -3. rabbitmq-consumer 是消息的消费者模块。 - -1.2 关于 rabbitmq 安装、交换机、队列、死信队列等基本概念可以参考我的手记[《RabbitMQ 实战指南》读书笔记](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/RabbitMQ/%E3%80%8ARabbitMQ%E5%AE%9E%E6%88%98%E6%8C%87%E5%8D%97%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.md),里面有详细的配图说明。 +- **rabbitmq-common** :公共模块,用于存放公共的接口、配置和 Java Bean,被 rabbitmq-producer 和 rabbitmq-consumer 在 pom.xml 中引用; +- **rabbitmq-producer** :消息的生产者模块; +- **rabbitmq-consumer** :是消息的消费者模块。
+## 二、主要依赖 - - -## 二、关键依赖 - -在父工程的项目中统一导入依赖 rabbitmq 的 starter(spring-boot-starter-amqp),父工程的 pom.xml 如下 +在父工程的项目中统一导入依赖 RabbitMQ 的 starter,父工程的 pom.xml 如下: ```xml @@ -86,26 +79,19 @@ test - + ``` -## 三、公共模块(rabbitmq-common) +## 三、公共模块 - bean 下为公共的实体类。 -- constant 下为公共配置,用静态常量引用。(这里我使用静态常量是为了方便引用,实际中也可以按照情况,抽取为公共配置文件) +- constant 下为公共配置,用静态常量进行引用。这里我使用静态常量是为了方便引用,实际中也可以按照情况,抽取为公共的配置文件。
- ```java -package com.heibaiying.constant; - -/** - * @author : heibaiying - * @description : rabbit 公用配置信息 - */ public class RabbitInfo { // queue 配置 @@ -124,11 +110,10 @@ public class RabbitInfo { -## 四、服务消费者(rabbitmq-consumer) +## 四、消息消费者
- -#### 4.1 消息消费者配置 +### 4.1 消费者配置 ```yaml spring: @@ -149,17 +134,11 @@ spring: max-concurrency: 50 ``` -#### 4.2 使用注解@RabbitListener和@RabbitHandler创建消息监听者 +### 4.2 创建监听者 -1. 使用注解创建的交换机、队列、和绑定关系会在项目初始化的时候自动创建,但是不会重复创建; -2. 这里我们创建两个消息监听器,分别演示消息是基本类型和消息是对象时的配置区别。 +使用注解 @RabbitListener 和 @RabbitHandler 创建消息的监听者,使用注解创建的交换机、队列、和绑定关系会在项目初始化的时候自动创建,但是不会重复创建。这里我们创建两个消息监听器,分别演示消息是基本类型和消息是对象时区别: ```java -/** - * @author : heibaiying - * @description : 消息是对象的消费者 - */ - @Component @Slf4j public class RabbitmqBeanConsumer { @@ -204,11 +183,10 @@ public class RabbitmqConsumer { -## 五、 消息生产者(rabbitmq-producer) +## 五、消息生产者
- -#### 5.1 消息生产者配置 +### 5.1 生产者配置 ```yaml spring: @@ -230,13 +208,9 @@ server: port: 8090 ``` -#### 5.2 创建消息生产者 +### 5.2 创建生产者 ```java -/** - * @author : heibaiying - * @description : 消息生产者 - */ @Component @Slf4j public class RabbitmqProducer { @@ -272,7 +246,9 @@ public class RabbitmqProducer { } ``` -#### 5.3 以单元测试的方式发送消息 +### 5.3 单元测试 + +以单元测试的方式发送消息: ```java @RunWith(SpringRunner.class) @@ -308,100 +284,11 @@ public class RabbitmqProducerTests { } ``` +## 六、项目构建 - -## 六、项目构建的说明 - -因为在项目中,consumer 和 producer 模块均依赖公共模块,所以在构建 consumer 和 producer 项目前需要将 common 模块安装到本地仓库,**依次**对**父工程**和**common 模块**执行: +因为在项目中,consumer 和 producer 模块均依赖公共模块,所以在构建 consumer 和 producer 项目前需要将 common 模块安装到本地仓库,依次对父工程和 common 模块执行: ```shell mvn install -Dmaven.test.skip = true ``` -consumer 中 pom.xml 如下 - -```xml - - - 4.0.0 - - - com.heibaiying - spring-boot-rabbitmq - 0.0.1-SNAPSHOT - - - rabbitmq-consumer - 0.0.1-SNAPSHOT - rabbitmq-consumer - RabbitMQ consumer project for Spring Boot - - - 1.8 - - - - - com.heibaiying - rabbitmq-common - 0.0.1-SNAPSHOT - compile - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - -``` - -producer 中 pom.xml 如下 - -```xml - - - 4.0.0 - - - com.heibaiying - spring-boot-rabbitmq - 0.0.1-SNAPSHOT - - - rabbitmq-producer - 0.0.1-SNAPSHOT - rabbitmq-producer - RabbitMQ producer project for Spring Boot - - - 1.8 - - - - - com.heibaiying - rabbitmq-common - 0.0.1-SNAPSHOT - compile - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - -``` diff --git a/spring-boot/spring-boot-redis/README.md b/spring-boot/spring-boot-redis/README.md index 0ffe330..37d836c 100644 --- a/spring-boot/spring-boot-redis/README.md +++ b/spring-boot/spring-boot-redis/README.md @@ -1,479 +1,260 @@ -# spring boot 整合 redis - -## 目录
-一、说明
+# Spring Boot 整合 Redis + +