分析Java相关的代码片段、细节等。
1. Java,Spring,MySQL中的时间问题
1.1 Java
1.1.1 Java8之前
- java.util.Date
Date
如果不格式化,打印出的日期可读性差- 使用
SimpleDateFormat
对时间进行格式化,但SimpleDateFormat
是线程不安全的
- 案例
1 | import java.text.DateFormat; |
1.1.2 Java8及其之后(重点关注表示范围)
- java.time.LocalDate
Modifier and Type | Field and Description |
---|---|
static LocalDate |
MAX The maximum supported LocalDate , ‘+999999999-12-31’. |
static LocalDate |
MIN The minimum supported LocalDate , ‘-999999999-01-01’. |
- java.time.LocalTime
Modifier and Type | Field and Description |
---|---|
static LocalTime |
MAX The maximum supported LocalTime , ‘23:59:59.999999999’. |
static LocalTime |
MIDNIGHT The time of midnight at the start of the day, ‘00:00’. |
static LocalTime |
MIN The minimum supported LocalTime , ‘00:00’. |
static LocalTime |
NOON The time of noon in the middle of the day, ‘12:00’. |
- java.time.LocalDateTime
Modifier and Type | Field and Description |
---|---|
static LocalDateTime |
MAX The maximum supported LocalDateTime , ‘+999999999-12-31T23:59:59.999999999’. |
static LocalDateTime |
MIN The minimum supported LocalDateTime , ‘-999999999-01-01T00:00:00’. |
- java.time.Instant
Modifier and Type | Field and Description |
---|---|
static Instant |
EPOCH Constant for the 1970-01-01T00:00:00Z epoch instant. |
static Instant |
MAX The maximum supported Instant , ‘1000000000-12-31T23:59:59.999999999Z’. |
static Instant |
MIN The minimum supported Instant , ‘-1000000000-01-01T00:00Z’. |
- 案例
1 | //案例 |
1.1.3 xx天xx小时xx分钟
1 | public static void main(String[] args) { |
1.2 Spring
1.3 MySQL
1.3.1 支持的数据类型
- TIME(不常用)(hhh:mm:ss)
MySQL retrieves and displays TIME values in ‘hh:mm:ss’ format (or ‘hhh:mm:ss’ format for large hours values). TIME values may range from ‘-838:59:59’ to ‘838:59:59’.
- DATE(常用,精度到天,不保存时区信息)
The supported range is '1000-01-01' to '9999-12-31'
- DATETIME(常用,支持到了微秒级别,不保存时区信息)
The DATETIME type is used for values that contain both date and time parts. MySQL retrieves and displays DATETIME values in 'YYYY-MM-DD hh:mm:ss' format. The supported range is '1000-01-01 00:00:00' to '9999-12-31 23:59:59'.
- TIMESTAMP(常用,支持到了微秒级别,保存了时区信息)
The TIMESTAMP data type is used for values that contain both date and time parts. TIMESTAMP has a range of '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC.
自动更新
1
2
3
4
5
6
7
8
9CREATE TABLE t1 (
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
dt DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE t2 (
dt1 DATETIME ON UPDATE CURRENT_TIMESTAMP, -- default NULL
dt2 DATETIME NOT NULL ON UPDATE CURRENT_TIMESTAMP -- default 0
);
- YEAR(不常用)(
1901
to2155
, or 0000)
1.3.2 Java包
- java.sql.Date
- java.lang.Object
- java.util.Date
- java.sql.Date
- java.sql.Timestamp(和MySQL中的范围有区别)
Constructor and Description Timestamp(int year, int month, int date,int hour,int minute,int second,int nano)
Deprecated. instead use the constructorTimestamp(long millis)
Timestamp(long time)
Constructs aTimestamp
object using a milliseconds time value.
1.4 参考资料
1.5 问题
- 精度(尤其是在使用MySQL时候作为判断条件时)
- 线程安全性
- 时区
2. 使用mongoDB存储SpringBoot日志
2.1 统一日志框架
在系统开发的过程中,会使用到不同的技术,不同的技术会使用不同的日志框架.为了更好地处理日志信息,首先需要将日志框架进行统一.
为了将其他的日志框架装换为slf4j,只需要在pom.xml进行如下配置:
1 | <!--统一日志框架: Slf4j+logback--> |
2.2 Spring Boot连接mongoDB
2.2.1 mongoDB安装和使用
安装—-使用docker
新建数据库
- use admin
2.2.2 mongoDB的连接
mongoDB的连接和其他数据库的连接存在一定的差异,主要是体现在mongoDB为每一个数据库设置了用户和密码,在建立建立连接通常采用一下方式.
1 | //spring.data.mongodb.uri=mongodb://用户名:密码t@ip:27017/数据库 |
2.3 将日志信息写入mogoDB
2.3.1 重写logback.xml
1 | <?xml version="1.0" encoding="UTF-8"?> |
2.3.2 定义Template
1 | package com.mao.api.core.config; |
2.3.3 定义日志Appender
(重写append,start,stop,setApplicationContext)
1 | package com.mao.api.util;/** |
2.4 效果
2.4.1 查看collections
2.4.2 查看日志细节
3. 使用递归的方法生成菜单
摘抄于:https://www.cnblogs.com/lucky-pin/p/10740037.html
递归生成一个如图的菜单,编写两个类数据模型Menu、和创建树形的MenuTree。通过以下过程实现:
首先从菜单数据中获取所有根节点。
为根节点建立次级子树并拼接上。
递归为子节点建立次级子树并接上,直至为末端节点拼接上空的“树”。
首先,编写数据模型Menu。每条菜单有自己的id、父节点parentId、菜单名称text、菜单还拥有次级菜单children。
1 | import java.util.List; |
创建树形结构的类MenuTree。方法getRootNode获取所有根节点,方法builTree将根节点汇总创建树形结构,buildChilTree为节点建立次级树并拼接上当前树,递归调用buildChilTree不断为当前树开枝散叶直至找不到新的子树。完成递归,获取树形结构。
1 | import java.util.ArrayList; |
最后,插入一些数据试试效果。得到的json就可以生成图一菜单了。
1 | import java.util.ArrayList; |
4. 重试
4.1 Guava Retrying框架重试机制的使用
API远程接口在调用时会偶发网络超时、网络异常,导致调用失败,这时候某些特殊需求可能需要使用重试机制,当发生网络等异常时重新再发起调用请求。Guava Retryer能完美的解决这一需求。
4.1.1 引入依赖
1 | <dependency> |
4.1.2 根据调用返回接口判断是否需要重试
1 | import java.util.concurrent.Callable; |
4.1.3 根据调用发生异常判断是否需要重试
1 | import java.util.concurrent.Callable; |
Guava时间重试机制:固定、自增、斐波拉契数组
https://rholder.github.io/guava-retrying/javadoc/2.0.0/com/github/rholder/retry/WaitStrategies.html
4.1.4 添加异常监听
1 | .withRetryListener(new MyRetryListener<>()) |
- 监听代码
1 | import com.github.rholder.retry.Attempt; |
4.1.5 总结
RetryerBuilder是一个factory创建者,可以定制设置重试源且可以支持多个重试源,可以配置重试次数或重试超时时间,以及可以配置等待时间间隔,创建重试者Retryer实例。
RetryerBuilder的重试源支持Exception异常对象 和自定义断言对象,通过retryIfException 和retryIfResult设置,同时支持多个且能兼容。
retryIfException,抛出runtime异常、checked异常时都会重试,但是抛出error不会重试。
retryIfRuntimeException只会在抛runtime异常的时候才重试,checked异常和error都不重试。
retryIfExceptionOfType允许我们只在发生特定异常的时候才重试,比如NullPointerException和IllegalStateException都属于runtime异常,也包括自定义的error。
4.2 @Retryable(spring的重试机制)
4.2.1 引入依赖
1 | <dependency> |
4.2.2 在启动类或者配置类上添加注解@EnableRetry
- 配置类
1 |
|
- 启动类
1 |
|
4.2.3 在需要重试的方法上添加注解@Retryable
1 |
|
@Retryable被注解的方法发生异常时会重试
@Retryable注解中的参数说明:
- maxAttempts :最大重试次数,默认为3,如果要设置的重试次数为3,可以不写;
- value:抛出指定异常才会重试
- include:和value一样,默认为空,当exclude也为空时,所有异常都重试
- exclude:指定不处理的异常,默认空,当include也为空时,所有异常都重试
- backoff:重试等待策略,默认使用@Backoff@Backoff的value默认为1000L,我们设置为2000L。
@Backoff重试补偿机制,默认没有
@Backoff注解中的参数说明:
- value:隔多少毫秒后重试,默认为1000L,我们设置为3000L;
- delay:和value一样,但是默认为0;
- multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。
5. 线程池
5.1 Spring的线程池ThreadPoolTaskExecutor使用案例
1 |
|