網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
前言
日前,有一個(gè)“折現(xiàn)圖”的需求,如下圖所示:
概述
如何自定義折線圖?首先將折線圖的繪制部分拆分成三部分:
- 原點(diǎn)
- X軸
- Y軸
- 折線
原點(diǎn)
第一步,需要定義出“折線圖”原點(diǎn)的位置,由圖得:
可以發(fā)現(xiàn),原點(diǎn)的位置由X軸、Y軸所占空間決定:
OriginX:Y軸寬度
OriginY:View高度 - X軸高度
計(jì)算Y軸寬度
思路:遍歷Y軸的繪制文字,用畫(huà)筆測(cè)量其最大寬度,在加上其左右Margin間距即Y軸寬度
Y軸寬度 = Y軸MarginLeft + Y軸最大文字寬度 + Y軸MariginRight
計(jì)算X軸高度
思路:獲取X軸畫(huà)筆FontMetrics,根據(jù)其top、bottom計(jì)算出X軸文字高度,在加上其上下Margin間距即X軸高度
val fontMetrics = xAxisTextPaint.fontMetrics
val lineHeight = fontMetrics.bottom - fontMetrics.top
xAxisHeight = lineHeight + xAxisOptions.textMarginTop + xAxisOptions.textMarginBottom
X軸
第二步,根據(jù)原點(diǎn)位置,繪制X軸軸線、網(wǎng)格線、文本
繪制軸線
繪制軸線比較簡(jiǎn)單,沿原點(diǎn)向控件右側(cè)畫(huà)一條直線即可
if (xAxisOptions.isEnableLine) {
xAxisLinePaint.strokeWidth = xAxisOptions.lineWidth
xAxisLinePaint.color = xAxisOptions.lineColor
xAxisLinePaint.pathEffect = xAxisOptions.linePathEffect
canvas.drawLine(originX, originY, width.toFloat(), originY, xAxisLinePaint)
}
X軸刻度間隔
在繪制網(wǎng)格線、文本之前需要先計(jì)算X軸的刻度間隔:
這里處理的方式比較隨意,直接將X軸等分7份即可(因?yàn)樾枰@示近7天的數(shù)據(jù))
xGap = (width - originX) / 7
網(wǎng)格線、文本
網(wǎng)格線:只需要根據(jù)X軸的刻度,沿Y軸方向依次向控件頂部,畫(huà)直線即可
文本:文本需要通過(guò)畫(huà)筆,提前測(cè)量出待繪制文本的區(qū)域,然后計(jì)算出居中位置繪制即可
xAxisTexts.forEachIndexed { index, text ->
val pointX = originX + index * xGap
//刻度線
if (xAxisOptions.isEnableRuler) {
xAxisLinePaint.strokeWidth = xAxisOptions.rulerWidth
xAxisLinePaint.color = xAxisOptions.rulerColor
canvas.drawLine(
pointX, originY,
pointX, originY - xAxisOptions.rulerHeight,
xAxisLinePaint
)
}
//網(wǎng)格線
if (xAxisOptions.isEnableGrid) {
xAxisLinePaint.strokeWidth = xAxisOptions.gridWidth
xAxisLinePaint.color = xAxisOptions.gridColor
xAxisLinePaint.pathEffect = xAxisOptions.gridPathEffect
canvas.drawLine(pointX, originY, pointX, 0f, xAxisLinePaint)
}
//文本
bounds.setEmpty()
xAxisTextPaint.textSize = xAxisOptions.textSize
xAxisTextPaint.color = xAxisOptions.textColor
xAxisTextPaint.getTextBounds(text, 0, text.length, bounds)
val fm = xAxisTextPaint.fontMetrics
val fontHeight = fm.bottom - fm.top
val fontX = originX + index * xGap + (xGap - bounds.width()) / 2f
val fontBaseline = originY + (xAxisHeight - fontHeight) / 2f - fm.top
canvas.drawText(text, fontX, fontBaseline, xAxisTextPaint)
}
Y軸
第三步:根據(jù)原點(diǎn)位置,繪制Y軸軸線、網(wǎng)格線、文本
計(jì)算Y軸分布
個(gè)人認(rèn)為,這里是自定義折線圖的一個(gè)難點(diǎn),這里經(jīng)過(guò)查閱資料,使用該文章中的算法:
基于JavaScript實(shí)現(xiàn)數(shù)值型坐標(biāo)軸刻度計(jì)算算法(echarts的y軸刻度計(jì)算)
/**
* 根據(jù)Y軸最大值、數(shù)量獲取Y軸的標(biāo)準(zhǔn)間隔
*/
private fun getYInterval(maxY: Int): Int {
val yIntervalCount = yAxisCount - 1
val rawInterval = maxY / yIntervalCount.toFloat()
val magicPower = floor(log10(rawInterval.toDouble()))
var magic = 10.0.pow(magicPower).toFloat()
if (magic == rawInterval) {
magic = rawInterval
} else {
magic *= 10
}
val rawStandardInterval = rawInterval / magic
val standardInterval = getStandardInterval(rawStandardInterval) * magic
return standardInterval.roundToInt()
}
/**
* 根據(jù)初始的歸一化后的間隔,轉(zhuǎn)化為目標(biāo)的間隔
*/
private fun getStandardInterval(x: Float): Float {
return when {
x <= 0.1f -> 0.1f
x <= 0.2f -> 0.2f
x <= 0.25f -> 0.25f
x <= 0.5f -> 0.5f
x <= 1f -> 1f
else -> getStandardInterval(x / 10) * 10
}
}
刻度間隔、網(wǎng)格線、文本
Y軸的軸線、網(wǎng)格線、文本剩下的內(nèi)容與X軸的處理方式幾乎一致
//繪制Y軸
//軸線
if (yAxisOptions.isEnableLine) {
yAxisLinePaint.strokeWidth = yAxisOptions.lineWidth
yAxisLinePaint.color = yAxisOptions.lineColor
yAxisLinePaint.pathEffect = yAxisOptions.linePathEffect
canvas.drawLine(originX, 0f, originX, originY, yAxisLinePaint)
}
yAxisTexts.forEachIndexed { index, text ->
//刻度線
val pointY = originY - index * yGap
if (yAxisOptions.isEnableRuler) {
yAxisLinePaint.strokeWidth = yAxisOptions.rulerWidth
yAxisLinePaint.color = yAxisOptions.rulerColor
canvas.drawLine(
originX,
pointY,
originX + yAxisOptions.rulerHeight,
pointY,
yAxisLinePaint
)
}
//網(wǎng)格線
if (yAxisOptions.isEnableGrid) {
yAxisLinePaint.strokeWidth = yAxisOptions.gridWidth
yAxisLinePaint.color = yAxisOptions.gridColor
yAxisLinePaint.pathEffect = yAxisOptions.gridPathEffect
canvas.drawLine(originX, pointY, width.toFloat(), pointY, yAxisLinePaint)
}
//文本
bounds.setEmpty()
yAxisTextPaint.textSize = yAxisOptions.textSize
yAxisTextPaint.color = yAxisOptions.textColor
yAxisTextPaint.getTextBounds(text, 0, text.length, bounds)
val fm = yAxisTextPaint.fontMetrics
val x = (yAxisWidth - bounds.width()) / 2f
val fontHeight = fm.bottom - fm.top
val y = originY - index * yGap - fontHeight / 2f - fm.top
canvas.drawText(text, x, y, yAxisTextPaint)
}
折線
折線的連接,這里使用的是Path,將一個(gè)一個(gè)坐標(biāo)點(diǎn)連接,最后將Path繪制,就形成了圖中的折線圖
//繪制數(shù)據(jù)
path.reset()
points.forEachIndexed { index, point ->
val x = originX + index * xGap + xGap / 2f
val y = originY - (point.yAxis.toFloat() / yAxisMaxValue) * (yGap * (yAxisCount - 1))
if (index == 0) {
path.moveTo(x, y)
} else {
path.lineTo(x, y)
}
//圓點(diǎn)
circlePaint.color = dataOptions.circleColor
canvas.drawCircle(x, y, dataOptions.circleRadius, circlePaint)
}
pathPaint.strokeWidth = dataOptions.pathWidth
pathPaint.color = dataOptions.pathColor
canvas.drawPath(path, pathPaint)
值得注意的是:坐標(biāo)點(diǎn)X根據(jù)間隔是相對(duì)確定的,而坐標(biāo)點(diǎn)Y則需要進(jìn)行百分比換算
代碼
折線圖LineChart
package com.vander.pool.widget.linechart
import android.content.Context
import android.graphics.*
import android.text.TextPaint
import android.util.AttributeSet
import android.view.View
import java.text.DecimalFormat
import kotlin.math.floor
import kotlin.math.log10
import kotlin.math.pow
import kotlin.math.roundToInt
class LineChart : View {
private var options = ChartOptions()
/**
* X軸相關(guān)
*/
private val xAxisTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
private val xAxisLinePaint = Paint(Paint.ANTI_ALIAS_FLAG)
private val xAxisTexts = mutableListOf<String>()
private var xAxisHeight = 0f
/**
* Y軸相關(guān)
*/
private val yAxisTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
private val yAxisLinePaint = Paint(Paint.ANTI_ALIAS_FLAG)
private val yAxisTexts = mutableListOf<String>()
private var yAxisWidth = 0f
private val yAxisCount = 5
private var yAxisMaxValue: Int = 0
/**
* 原點(diǎn)
*/
private var originX = 0f
private var originY = 0f
private var xGap = 0f
private var yGap = 0f
/**
* 數(shù)據(jù)相關(guān)
*/
private val pathPaint = Paint(Paint.ANTI_ALIAS_FLAG).also {
it.style = Paint.Style.STROKE
}
private val circlePaint = Paint(Paint.ANTI_ALIAS_FLAG).also {
it.color = Color.parseColor("#79EBCF")
it.style = Paint.Style.FILL
}
private val points = mutableListOf<ChartBean>()
private val bounds = Rect()
private val path = Path()
constructor(context: Context)
: this(context, null)
constructor(context: Context, attrs: AttributeSet?)
: this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
super(context, attrs, defStyleAttr)
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (points.isEmpty()) return
val xAxisOptions = options.xAxisOptions
val yAxisOptions = options.yAxisOptions
val dataOptions = options.dataOptions
//設(shè)置原點(diǎn)
originX = yAxisWidth
originY = height - xAxisHeight
//設(shè)置X軸Y軸間隔
xGap = (width - originX) / points.size
//Y軸默認(rèn)頂部會(huì)留出一半空間
yGap = originY / (yAxisCount - 1 + 0.5f)
//繪制X軸
//軸線
if (xAxisOptions.isEnableLine) {
xAxisLinePaint.strokeWidth = xAxisOptions.lineWidth
xAxisLinePaint.color = xAxisOptions.lineColor
xAxisLinePaint.pathEffect = xAxisOptions.linePathEffect
canvas.drawLine(originX, originY, width.toFloat(), originY, xAxisLinePaint)
}
xAxisTexts.forEachIndexed { index, text ->
val pointX = originX + index * xGap
//刻度線
if (xAxisOptions.isEnableRuler) {
xAxisLinePaint.strokeWidth = xAxisOptions.rulerWidth
xAxisLinePaint.color = xAxisOptions.rulerColor
canvas.drawLine(
pointX, originY,
pointX, originY - xAxisOptions.rulerHeight,
xAxisLinePaint
)
}
//網(wǎng)格線
if (xAxisOptions.isEnableGrid) {
xAxisLinePaint.strokeWidth = xAxisOptions.gridWidth
xAxisLinePaint.color = xAxisOptions.gridColor
xAxisLinePaint.pathEffect = xAxisOptions.gridPathEffect
canvas.drawLine(pointX, originY, pointX, 0f, xAxisLinePaint)
}
//文本
bounds.setEmpty()
xAxisTextPaint.textSize = xAxisOptions.textSize
xAxisTextPaint.color = xAxisOptions.textColor
xAxisTextPaint.getTextBounds(text, 0, text.length, bounds)
val fm = xAxisTextPaint.fontMetrics
val fontHeight = fm.bottom - fm.top
val fontX = originX + index * xGap + (xGap - bounds.width()) / 2f
val fontBaseline = originY + (xAxisHeight - fontHeight) / 2f - fm.top
canvas.drawText(text, fontX, fontBaseline, xAxisTextPaint)
}
//繪制Y軸
//軸線
if (yAxisOptions.isEnableLine) {
yAxisLinePaint.strokeWidth = yAxisOptions.lineWidth
yAxisLinePaint.color = yAxisOptions.lineColor
yAxisLinePaint.pathEffect = yAxisOptions.linePathEffect
canvas.drawLine(originX, 0f, originX, originY, yAxisLinePaint)
}
yAxisTexts.forEachIndexed { index, text ->
//刻度線
val pointY = originY - index * yGap
if (yAxisOptions.isEnableRuler) {
yAxisLinePaint.strokeWidth = yAxisOptions.rulerWidth
yAxisLinePaint.color = yAxisOptions.rulerColor
canvas.drawLine(
originX,
pointY,
originX + yAxisOptions.rulerHeight,
pointY,
yAxisLinePaint
)
}
//網(wǎng)格線
if (yAxisOptions.isEnableGrid) {
yAxisLinePaint.strokeWidth = yAxisOptions.gridWidth
yAxisLinePaint.color = yAxisOptions.gridColor
yAxisLinePaint.pathEffect = yAxisOptions.gridPathEffect
canvas.drawLine(originX, pointY, width.toFloat(), pointY, yAxisLinePaint)
}
//文本
bounds.setEmpty()
yAxisTextPaint.textSize = yAxisOptions.textSize
yAxisTextPaint.color = yAxisOptions.textColor
yAxisTextPaint.getTextBounds(text, 0, text.length, bounds)
val fm = yAxisTextPaint.fontMetrics
val x = (yAxisWidth - bounds.width()) / 2f
val fontHeight = fm.bottom - fm.top
val y = originY - index * yGap - fontHeight / 2f - fm.top
canvas.drawText(text, x, y, yAxisTextPaint)
}
//繪制數(shù)據(jù)
path.reset()
points.forEachIndexed { index, point ->
val x = originX + index * xGap + xGap / 2f
val y = originY - (point.yAxis.toFloat() / yAxisMaxValue) * (yGap * (yAxisCount - 1))
if (index == 0) {
path.moveTo(x, y)
} else {
path.lineTo(x, y)
}
//圓點(diǎn)
circlePaint.color = dataOptions.circleColor
canvas.drawCircle(x, y, dataOptions.circleRadius, circlePaint)
}
pathPaint.strokeWidth = dataOptions.pathWidth
pathPaint.color = dataOptions.pathColor
canvas.drawPath(path, pathPaint)
}
/**
* 設(shè)置數(shù)據(jù)
*/
fun setData(list: List<ChartBean>) {
points.clear()
points.addAll(list)
//設(shè)置X軸、Y軸數(shù)據(jù)
setXAxisData(list)
setYAxisData(list)
invalidate()
}
/**
* 設(shè)置X軸數(shù)據(jù)
*/
private fun setXAxisData(list: List<ChartBean>) {
val xAxisOptions = options.xAxisOptions
val values = list.map { it.xAxis }
//X軸文本
xAxisTexts.clear()
xAxisTexts.addAll(values)
//X軸高度
val fontMetrics = xAxisTextPaint.fontMetrics
val lineHeight = fontMetrics.bottom - fontMetrics.top
xAxisHeight = lineHeight + xAxisOptions.textMarginTop + xAxisOptions.textMarginBottom
}
/**
* 設(shè)置Y軸數(shù)據(jù)
*/
private fun setYAxisData(list: List<ChartBean>) {
val yAxisOptions = options.yAxisOptions
yAxisTextPaint.textSize = yAxisOptions.textSize
yAxisTextPaint.color = yAxisOptions.textColor
val texts = list.map { it.yAxis.toString() }
yAxisTexts.clear()
yAxisTexts.addAll(texts)
//Y軸高度
val maxTextWidth = yAxisTexts.maxOf { yAxisTextPaint.measureText(it) }
yAxisWidth = maxTextWidth + yAxisOptions.textMarginLeft + yAxisOptions.textMarginRight
//Y軸間隔
val maxY = list.maxOf { it.yAxis }
val interval = when {
maxY <= 10 -> getYInterval(10)
else -> getYInterval(maxY)
}
//Y軸文字
yAxisTexts.clear()
for (index in 0..yAxisCount) {
val value = index * interval
yAxisTexts.add(formatNum(value))
}
yAxisMaxValue = (yAxisCount - 1) * interval
}
/**
* 格式化數(shù)值
*/
private fun formatNum(num: Int): String {
val absNum = Math.abs(num)
return if (absNum >= 0 && absNum < 1000) {
return num.toString()
} else {
val format = DecimalFormat("0.0")
val value = num / 1000f
"${format.format(value)}k"
}
}
/**
* 根據(jù)Y軸最大值、數(shù)量獲取Y軸的標(biāo)準(zhǔn)間隔
*/
private fun getYInterval(maxY: Int): Int {
val yIntervalCount = yAxisCount - 1
val rawInterval = maxY / yIntervalCount.toFloat()
val magicPower = floor(log10(rawInterval.toDouble()))
var magic = 10.0.pow(magicPower).toFloat()
if (magic == rawInterval) {
magic = rawInterval
} else {
magic *= 10
}
val rawStandardInterval = rawInterval / magic
val standardInterval = getStandardInterval(rawStandardInterval) * magic
return standardInterval.roundToInt()
}
/**
* 根據(jù)初始的歸一化后的間隔,轉(zhuǎn)化為目標(biāo)的間隔
*/
private fun getStandardInterval(x: Float): Float {
return when {
x <= 0.1f -> 0.1f
x <= 0.2f -> 0.2f
x <= 0.25f -> 0.25f
x <= 0.5f -> 0.5f
x <= 1f -> 1f
else -> getStandardInterval(x / 10) * 10
}
}
/**
* 重置參數(shù)
*/
fun setOptions(newOptions: ChartOptions) {
this.options = newOptions
setData(points)
}
fun getOptions(): ChartOptions {
return options
}
data class ChartBean(val xAxis: String, val yAxis: Int)
}
ChartOptions配置選項(xiàng):
class ChartOptions {
//X軸配置
var xAxisOptions = AxisOptions()
//Y軸配置
var yAxisOptions = AxisOptions()
//數(shù)據(jù)配置
var dataOptions = DataOptions()
}
/**
* 軸線配置參數(shù)
*/
class AxisOptions {
companion object {
private const val DEFAULT_TEXT_SIZE = 20f
private const val DEFAULT_TEXT_COLOR = Color.BLACK
private const val DEFAULT_TEXT_MARGIN = 20
private const val DEFAULT_LINE_WIDTH = 2f
private const val DEFAULT_RULER_WIDTH = 10f
}
/**
* 文字大小
*/
@FloatRange(from = 1.0)
var textSize: Float = DEFAULT_TEXT_SIZE
@ColorInt
var textColor: Int = DEFAULT_TEXT_COLOR
/**
* X軸文字內(nèi)容上下兩側(cè)margin
*/
var textMarginTop: Int = DEFAULT_TEXT_MARGIN
var textMarginBottom: Int = DEFAULT_TEXT_MARGIN
/**
* Y軸文字內(nèi)容左右兩側(cè)margin
*/
var textMarginLeft: Int = DEFAULT_TEXT_MARGIN
var textMarginRight: Int = DEFAULT_TEXT_MARGIN
/**
* 軸線
*/
var lineWidth: Float = DEFAULT_LINE_WIDTH
@ColorInt
var lineColor: Int = DEFAULT_TEXT_COLOR
var isEnableLine = true
var linePathEffect: PathEffect? = null
/**
* 刻度
*/
var rulerWidth = DEFAULT_LINE_WIDTH
var rulerHeight = DEFAULT_RULER_WIDTH
@ColorInt
var rulerColor = DEFAULT_TEXT_COLOR
var isEnableRuler = true
/**
* 網(wǎng)格
*/
var gridWidth: Float = DEFAULT_LINE_WIDTH
@ColorInt
var gridColor: Int = DEFAULT_TEXT_COLOR
var gridPathEffect: PathEffect? = null
var isEnableGrid = true
}
/**
* 數(shù)據(jù)配置參數(shù)
*/
class DataOptions {
companion object {
private const val DEFAULT_PATH_WIDTH = 2f
private const val DEFAULT_PATH_COLOR = Color.BLACK
private const val DEFAULT_CIRCLE_RADIUS = 10f
private const val DEFAULT_CIRCLE_COLOR = Color.BLACK
}
var pathWidth = DEFAULT_PATH_WIDTH
var pathColor = DEFAULT_PATH_COLOR
var circleRadius = DEFAULT_CIRCLE_RADIUS
var circleColor = DEFAULT_CIRCLE_COLOR
}
Demo樣式:
private fun initView() {
val options = binding.chart.getOptions()
//X軸
val xAxisOptions = options.xAxisOptions
xAxisOptions.isEnableLine = false
xAxisOptions.textColor = Color.parseColor("#999999")
xAxisOptions.textSize = dpToPx(12)
xAxisOptions.textMarginTop = dpToPx(12).toInt()
xAxisOptions.textMarginBottom = dpToPx(12).toInt()
xAxisOptions.isEnableGrid = false
xAxisOptions.isEnableRuler = false
//Y軸
val yAxisOptions = options.yAxisOptions
yAxisOptions.isEnableLine = false
yAxisOptions.textColor = Color.parseColor("#999999")
yAxisOptions.textSize = dpToPx(12)
yAxisOptions.textMarginLeft = dpToPx(12).toInt()
yAxisOptions.textMarginRight = dpToPx(12).toInt()
yAxisOptions.gridColor = Color.parseColor("#999999")
yAxisOptions.gridWidth = dpToPx(0.5f)
val dashLength = dpToPx(8f)
yAxisOptions.gridPathEffect = DashPathEffect(floatArrayOf(dashLength, dashLength / 2), 0f)
yAxisOptions.isEnableRuler = false
//數(shù)據(jù)
val dataOptions = options.dataOptions
dataOptions.pathColor = Color.parseColor("#79EBCF")
dataOptions.pathWidth = dpToPx(1f)
dataOptions.circleColor = Color.parseColor("#79EBCF")
dataOptions.circleRadius = dpToPx(3f)
binding.chart.setOnClickListener {
initChartData()
}
binding.toolbar.setLeftClick {
finish()
}
}
private fun initChartData() {
val random = 1000
val list = mutableListOf<LineChart.ChartBean>()
list.add(LineChart.ChartBean("05-01", Random.nextInt(random)))
list.add(LineChart.ChartBean("05-02", Random.nextInt(random)))
list.add(LineChart.ChartBean("05-03", Random.nextInt(random)))
list.add(LineChart.ChartBean("05-04", Random.nextInt(random)))
list.add(LineChart.ChartBean("05-05", Random.nextInt(random)))
list.add(LineChart.ChartBean("05-06", Random.nextInt(random)))
list.add(LineChart.ChartBean("05-07", Random.nextInt(random)))
binding.chart.setData(list)
//文本
val text = list.joinToString("\n") {
"x : ${it.xAxis} y:${it.yAxis}"
}
binding.value.text = text
}
原文鏈接:https://juejin.cn/post/7105243451037319204
相關(guān)推薦
- 2023-09-12 spring webflux配置成tomcat的線程池
- 2023-03-28 python程序中調(diào)用其他程序的實(shí)現(xiàn)_python
- 2022-11-14 深度強(qiáng)化學(xué)習(xí)預(yù)訓(xùn)練,在線、離線
- 2022-08-15 VPP靜態(tài)映射實(shí)現(xiàn)DNAT
- 2022-05-16 Qt5中QML自定義環(huán)形菜單/環(huán)形選擇框的實(shí)現(xiàn)_C 語(yǔ)言
- 2022-04-02 python3?QT5?端口轉(zhuǎn)發(fā)工具兩種場(chǎng)景分析_python
- 2022-08-03 C#后端接收f(shuō)orm-data,創(chuàng)建實(shí)體類(lèi)教程_C#教程
- 2022-11-10 golang?常用定時(shí)任務(wù)匯總_Golang
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支