-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.json
More file actions
1 lines (1 loc) · 70.6 KB
/
Copy pathcontent.json
File metadata and controls
1 lines (1 loc) · 70.6 KB
1
{"meta":{"title":"summerain0的技术笔记","subtitle":"","description":"summerain0的技术笔记","author":"summerain0","url":"https://summerain0.top","root":"/"},"pages":[{"title":"","date":"2026-06-16T11:40:42.470Z","updated":"2026-06-16T11:40:42.470Z","comments":true,"path":"about/index.html","permalink":"https://summerain0.top/about/","excerpt":"","text":"欢迎About Me欢迎光临summerain0的个人小站,如你所见,这是一个用来写个人笔记的网站,主要分享一些教程及个人遇到的问题及解决方法,希望对大家能有所帮助。 男 IT牛马 日常码代码 啥都写一点,Android,JavaWeb,Vue.js …"},{"title":"","date":"2026-06-16T11:40:42.470Z","updated":"2026-06-16T11:40:42.470Z","comments":true,"path":"404.html","permalink":"https://summerain0.top/404","excerpt":"","text":"404 很抱歉,您访问的页面不存在 可能是输入地址有误或该地址已被删除"},{"title":"所有分类","date":"2026-06-16T11:40:42.490Z","updated":"2026-06-16T11:40:42.490Z","comments":true,"path":"categories/index.html","permalink":"https://summerain0.top/categories/","excerpt":"","text":""},{"title":"友情链接","date":"2026-06-16T11:40:42.490Z","updated":"2026-06-16T11:40:42.490Z","comments":true,"path":"friends/index.html","permalink":"https://summerain0.top/friends/","excerpt":"","text":""},{"title":"所有标签","date":"2026-06-16T11:40:42.490Z","updated":"2026-06-16T11:40:42.490Z","comments":true,"path":"tags/index.html","permalink":"https://summerain0.top/tags/","excerpt":"","text":""},{"title":"小工具","date":"2026-06-16T11:40:42.490Z","updated":"2026-06-16T11:40:42.490Z","comments":true,"path":"tools/index.html","permalink":"https://summerain0.top/tools/","excerpt":"","text":"Jetpack Compose Vector转ImageVector"},{"title":"","date":"2026-06-16T11:40:42.490Z","updated":"2026-06-16T11:40:42.490Z","comments":true,"path":"assets/tools/vector2compose/index.js","permalink":"https://summerain0.top/assets/tools/vector2compose/index.js","excerpt":"","text":"const example = ` ` const template = `val %iconName1%: ImageVector get() { if (_%iconName2% != null) { return _%iconName2%!! } _%iconName2% = ImageVector.Builder( name = \"%iconName1%\", defaultWidth = %defaultWidth%.dp, defaultHeight = %defaultHeight%.dp, viewportWidth = %viewportWidth%f, viewportHeight = %viewportHeight%f, autoMirror = false )%result%.build() return _%iconName2%!! } private var _%iconName2%: ImageVector? = null` const textPadding = \" \" const iconNameField = document.getElementById(\"iconNameField\") const rawTextarea = document.getElementById(\"rawText\") const targetTextarea = document.getElementById(\"targetText\") const conversionButton = document.getElementById(\"conversionBtn\") const clearButton = document.getElementById(\"clearBtn\") const copyButton = document.getElementById(\"copyBtn\") const exegesisSwitch = document.getElementById(\"exegesisSwitch\") window.onload = function () { rawTextarea.value = example } conversionButton.onclick = function () { const rawText = rawTextarea.value.trim() if (rawText.length === 0) { mdui.snackbar({ message: '请输入文本' }); return } const parser = new DOMParser() let doc = parser.parseFromString(rawText, \"application/xml\"); // 检查合法性 if (doc.querySelector('html') !== null) { mdui.snackbar({ message: '待处理文本不合法!' }); return } // 检查画布大小 const vector = doc.querySelector('vector') const viewportWidth = vector.attributes['android:viewportWidth'].value const viewportHeight = vector.attributes['android:viewportHeight'].value const defaultWidth = vector.attributes['android:width'].value.replaceAll(\"dp\", \"\") const defaultHeight = vector.attributes['android:height'].value.replaceAll(\"dp\", \"\") const paths = vector.querySelectorAll('path') let result = \"\" paths.forEach(function (path) { const pathData = path.attributes['android:pathData'].value if (pathData === null) { mdui.snackbar({ message: '没获取到pathData!' }); return } let patchText = `.path( fill = SolidColor(Color.Black), fillAlpha = 1f, stroke = null, strokeAlpha = 1f, strokeLineWidth = 1f, strokeLineCap = StrokeCap.Butt, strokeLineJoin = StrokeJoin.Bevel, strokeLineMiter = 1f, pathFillType = PathFillType.NonZero ) {\\n` const commands = parseSVGPath(pathData) patchText += convertToPathBuilder(commands) patchText += \" }\" result += patchText }) // icon名字 const iconName1 = iconNameField.value if (iconName1.trim().length === 0) { mdui.snackbar({ message: '请输入图标名称!' }); return } const iconName2 = iconName1.substring(0, 1).toLowerCase() + iconName1.substring(1, iconName1.length) // path内容 targetTextarea.value = template .replace(\"%result%\", result) .replaceAll(\"%iconName1%\", iconName1) .replaceAll(\"%iconName2%\", iconName2) .replaceAll(\"%defaultWidth%\", defaultWidth) .replaceAll(\"%defaultHeight%\", defaultHeight) .replaceAll(\"%viewportWidth%\", viewportWidth) .replaceAll(\"%viewportHeight%\", viewportHeight) } clearButton.onclick = function () { rawTextarea.value = \"\" targetTextarea.value = \"\" } copyButton.onclick = function () { navigator.clipboard.writeText(targetTextarea.value.trim()).then( () => { mdui.snackbar({ message: '复制成功' }); }, () => { mdui.snackbar({ message: '复制失败' }); } ); } function parseSVGPath(pathString) { let commands = []; let currentCommand = ''; for (let i = 0; i < pathString.length; i++) { let char = pathString[i]; if (char.match(/[a-zA-Z]/)) { // 如果遇到字母,则当前操作结束,将当前操作及其参数保存到数组中 if (currentCommand !== '') { commands.push(currentCommand.split(' ')); } currentCommand = char; } else if (char.match(/[0-9.-]/)) { // 如果是数字或减号,则将其添加到当前操作的参数中 let startIndex = i; while (i + 1 < pathString.length && pathString[i + 1].match(/[0-9.-]/)) { i++; } let parameter = pathString.substring(startIndex, i + 1); currentCommand += ' ' + parameter; } } // 将最后一个操作及其参数添加到数组中 if (currentCommand !== '') { commands.push(currentCommand.split(' ')); } return commands; } // 根据操作符生成文本 function convertToPathBuilder(commands) { let pathBuilder = \"\"; for (let i = 0; i < commands.length; i++) { let command = commands[i][0]; let params = commands[i].slice(1).map(Number); switch (command) { case \"M\": pathBuilder += `${textPadding}moveTo(${params[0]}f, ${params[1]}f)\\n`; break; case \"m\": pathBuilder += `${textPadding}moveToRelative(${params[0]}f, ${params[1]}f)\\n`; break; case \"L\": // 参数可能包含多个,所以需要循环绘制多条线段 for (let index = 0; index < params.length; index += 2) { pathBuilder += `${textPadding}lineTo(${params[index]}f, ${params[index + 1]}f)\\n`; } break; case \"l\": // 参数可能包含多个,所以需要循环绘制多条线段 for (let index = 0; index < params.length; index += 2) { pathBuilder += `${textPadding}lineToRelative(${params[index]}f, ${params[index + 1]}f)\\n`; } break; case \"H\": pathBuilder += `${textPadding}horizontalLineTo(${params[0]}f)\\n`; break; case \"h\": pathBuilder += `${textPadding}horizontalLineToRelative(${params[0]}f)\\n`; break; case \"V\": pathBuilder += `${textPadding}verticalLineTo(${params[0]}f)\\n`; break; case \"v\": pathBuilder += `${textPadding}verticalLineToRelative(${params[0]}f)\\n`; break; case \"C\": // 参数可能包含多个,所以需要循环绘制多条线段 for (let index = 0; index < params.length; index += 6) { pathBuilder += `${textPadding}curveTo(${params[index]}f, ${params[index + 1]}f, ${params[index + 2]}f, ${params[index + 3]}f, ${params[index + 4]}f, ${params[index + 5]}f)\\n`; } break; case \"c\": for (let index = 0; index < params.length; index += 6) { pathBuilder += `${textPadding}curveToRelative(${params[index]}f, ${params[index + 1]}f, ${params[index + 2]}f, ${params[index + 3]}f, ${params[index + 4]}f, ${params[index + 5]}f)\\n`; } break; case \"A\": pathBuilder += `${textPadding}arcTo(${params[0]}f, ${params[1]}f, ${params[2]}f, ${params[3] == '1'}, ${params[4] == '1'}, ${params[5]}f, ${params[6]}f)\\n`; break; case \"a\": pathBuilder += `${textPadding}arcToRelative(${params[0]}f, ${params[1]}f, ${params[2]}f, ${params[3] == '1'}, ${params[4] == '1'}, ${params[5]}f, ${params[6]}f)\\n`; break; case \"S\": pathBuilder += `${textPadding}reflectiveCurveTo(${params[0]}f, ${params[1]}f, ${params[2]}f, ${params[3]}f)\\n`; break; case \"s\": pathBuilder += `${textPadding}reflectiveCurveToRelative(${params[0]}f, ${params[1]}f, ${params[2]}f, ${params[3]}f)\\n`; break; case \"Z\": case \"z\": pathBuilder += textPadding + \"close()\\n\"; break; default: pathBuilder += `${textPadding}[未知的操作符${command}]${params}\\n` break; } } return pathBuilder; }"},{"title":"留言板","date":"2026-06-16T11:40:42.490Z","updated":"2026-06-16T11:40:42.490Z","comments":true,"path":"comment/index.html","permalink":"https://summerain0.top/comment/","excerpt":"","text":"Hello,来聊点什么呢?"},{"title":"","date":"2026-06-16T11:40:42.488Z","updated":"2026-06-16T11:40:42.488Z","comments":true,"path":"assets/lib/moment.min.js","permalink":"https://summerain0.top/assets/lib/moment.min.js","excerpt":"","text":"!function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=t():\"function\"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){\"use strict\";var H;function f(){return H.apply(null,arguments)}function a(e){return e instanceof Array||\"[object Array]\"===Object.prototype.toString.call(e)}function F(e){return null!=e&&\"[object Object]\"===Object.prototype.toString.call(e)}function c(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function L(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;for(var t in e)if(c(e,t))return;return 1}function o(e){return void 0===e}function u(e){return\"number\"==typeof e||\"[object Number]\"===Object.prototype.toString.call(e)}function V(e){return e instanceof Date||\"[object Date]\"===Object.prototype.toString.call(e)}function G(e,t){for(var n=[],s=e.length,i=0;i>0,s=0;s"}],"posts":[{"title":"从零在Docker中搭建微服务环境(docker for windows)","slug":"从零在Docker中搭建微服务环境-docker-for-windows","date":"2024-01-18T08:17:01.000Z","updated":"2026-06-16T11:40:42.470Z","comments":true,"path":"/2024/01/18/从零在Docker中搭建微服务环境-docker-for-windows/","permalink":"https://summerain0.top/2024/01/18/%E4%BB%8E%E9%9B%B6%E5%9C%A8Docker%E4%B8%AD%E6%90%AD%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%8E%AF%E5%A2%83-docker-for-windows/","excerpt":"","text":"1. 前言本文将在Docker环境中搭建微服务环境,包含以下几个环境,均为当前最新版(2024-1-17): MySQL 8 Redis Nacos Sentinel Seata 2.3.0 2. 环境搭建2.1 建立子网未指定容器ip的情况下,docker会按启动顺序分配ip,所以可能每次启动微服务环境的时候,各容器的ip并不一致,所以需要自己建立一个子网,并分配给各个容器 初始状态下,docker有三个网络: 12345PS C:\\Users\\23516> docker network lsNETWORK ID NAME DRIVER SCOPEd71b4e8082bd bridge bridge localcfe60e5ede6f host host localeca3acb024ef none null local 此处我们建立一个自己的网络,名字为myNetwork,网段是172.20.0.0/16,可以分配172.20.0.0 ~ 172.20.255.255 1docker network create --subnet=172.20.0.0/16 myNetwork 再次检查Docker网络列表,发现已经成功创建了自己的网络 123456PS C:\\Users\\23516> docker network lsNETWORK ID NAME DRIVER SCOPEd71b4e8082bd bridge bridge localcfe60e5ede6f host host locale49ab5c335a4 myNetwork bridge localeca3acb024ef none null local 既然我们已经成功创建自己的网络,所以我们需要提前分配一下每个容器的ip,可以列一个表格: 容器 IP MySQL 172.20.0.2 Redis 172.20.0.3 Nacos 172.20.0.4 Sentinel 172.20.0.5 Seata 172.20.0.6 2.2 MySQLMYSQL_ROOT_PASSWORD: MySQL的密码,用户名默认为root 1docker run -itd --name MySQL --network myNetwork --ip 172.20.0.2 -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -p 33060:33060 mysql 执行后我们成功创建了一个容器名为MySQL,分配了ip并且将3306和33060端口暴露出来 当然这还没完,使用select NOW()查询后会发现时间差了8个小时,也就是存在时区问题,所以我们需要修改MySQL的时区 进入MySQL容器,理论上容器是不存在my.cnf这个文件的,所以直接执行以下命令即可。如果存在,则修改相应字段即可。 1echo -e "[mysqld]\\ndefault-time-zone = '+8:00'" > /etc/mysql/my.cnf 执行完毕后重启MySQL容器,再次执行select NOW()即可发现时间显示正常。 2.3 RedisRedis部分就很简单,只分配一个ip即可,没什么特殊的操作 1docker run -itd --name Redis --network myNetwork --ip 172.20.0.3 -p 6379:6379 redis 2.4 NacosNacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 MODE: 运行模式,我们选择单机模式(standalone) Nacos 2.0之后需要额外暴露9848和9849端口 1docker run -itd --name Nacos --network myNetwork --ip 172.20.0.4 -e MODE=standalone -p 8848:8848 -p 9848:9848 -p 9849:9849 nacos/nacos-server 现在就可以访问localhost:8848/nacos进入Nacos的后台管理页面了,用户名和密码默认都是nacos Nacos后台管理页面.png 2.5 Sentinel什么是Sentinel?https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D Sentinel的搭建也是非常的简单,执行这条命令即可创建 1docker run -itd --name Sentinel --network myNetwork --ip 172.20.0.5 -p 8858:8858 bladex/sentinel-dashboard 现在就可以访问localhost:8858进入Sentinel的后台管理页面了,用户名和密码默认都是sentinel Sentinel后台管理页面.png 2.6 Seata恭喜你,到了最后的一步,也是最难搭建的分布式事务容器,这里有一些坑是需要去踩的 2.6.1 创建容器首先,你需要先查一下自己所处的局域网ip,例如我在当前的局域网中的IP是192.168.10.254,这个ip建议在路由器后台中定死。 1234567891011121314151617181920PS C:\\Users\\23516> ipconfigWindows IP 配置以太网适配器 以太网: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接 连接特定的 DNS 后缀 . . . . . . . :无线局域网适配器 WLAN: 连接特定的 DNS 后缀 . . . . . . . : IPv6 地址 . . . . . . . . . . . . : 2409:8a34:61e:15f4:731b:41fe:57a8:a797 临时 IPv6 地址. . . . . . . . . . : 2409:8a34:61e:15f4:b44e:b797:dd16:3dcb 本地链接 IPv6 地址. . . . . . . . : fe80::ca09:8ecc:8470:a83a%13 IPv4 地址 . . . . . . . . . . . . : 192.168.10.254 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : fe80::10%13 192.168.10.1 接下来创建Seata容器,成功创建后,立马停止Seata容器,我们需要进行一些配置 这里的SEATA_IP一定一定一定要和宿主机的局域网IP一致,也就是上面提到的(192.168.10.254),不然后面项目链接Seata服务器的时候会一直报错! 1docker run -itd --name Seata --network myNetwork --ip 172.20.0.6 -e SEATA_IP=192.168.10.254 -p 8091:8091 -p 7091:7091 seataio/seata-server 2.6.2 创建seata数据库然后我们现在需要创建供Seata回滚事务使用的数据库,数据库名为seata(当然也可以自定义),建表语句可以参见官方地址 点击展开查看建表语句 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273-- -------------------------------- The script used when storeMode is 'db' ---------------------------------- the table to store GlobalSession dataCREATE TABLE IF NOT EXISTS `global_table`( `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `status` TINYINT NOT NULL, `application_id` VARCHAR(32), `transaction_service_group` VARCHAR(32), `transaction_name` VARCHAR(128), `timeout` INT, `begin_time` BIGINT, `application_data` VARCHAR(2000), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`xid`), KEY `idx_status_gmt_modified` (`status` , `gmt_modified`), KEY `idx_transaction_id` (`transaction_id`)) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;-- the table to store BranchSession dataCREATE TABLE IF NOT EXISTS `branch_table`( `branch_id` BIGINT NOT NULL, `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `resource_group_id` VARCHAR(32), `resource_id` VARCHAR(256), `branch_type` VARCHAR(8), `status` TINYINT, `client_id` VARCHAR(64), `application_data` VARCHAR(2000), `gmt_create` DATETIME(6), `gmt_modified` DATETIME(6), PRIMARY KEY (`branch_id`), KEY `idx_xid` (`xid`)) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;-- the table to store lock dataCREATE TABLE IF NOT EXISTS `lock_table`( `row_key` VARCHAR(128) NOT NULL, `xid` VARCHAR(128), `transaction_id` BIGINT, `branch_id` BIGINT NOT NULL, `resource_id` VARCHAR(256), `table_name` VARCHAR(32), `pk` VARCHAR(36), `status` TINYINT NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking', `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`row_key`), KEY `idx_status` (`status`), KEY `idx_branch_id` (`branch_id`), KEY `idx_xid` (`xid`)) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;CREATE TABLE IF NOT EXISTS `distributed_lock`( `lock_key` CHAR(20) NOT NULL, `lock_value` VARCHAR(20) NOT NULL, `expire` BIGINT, primary key (`lock_key`)) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0); 2.6.3 在Nacos中导入Seata的配置我们的Seata的配置是从Nacos获取的,所以我们需要事先在Nacos上配置好Seata的相关参数 点击Nacos的配置列表,选择创建配置 选择创建配置 这里填写Data-ID和Group,填入相关的配置信息(如果提示有语法错误,请无视) 填入配置 同样,配置内容可以参见官方提供的config.txt,也可以复制下面的 点击展开查看 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152#For details about configuration items, see https://seata.apache.org/zh-cn/docs/user/configurations#Transport configuration, for client and servertransport.type=TCPtransport.server=NIOtransport.heartbeat=truetransport.enableTmClientBatchSendRequest=falsetransport.enableRmClientBatchSendRequest=truetransport.enableTcServerBatchSendResponse=falsetransport.rpcRmRequestTimeout=30000transport.rpcTmRequestTimeout=30000transport.rpcTcRequestTimeout=30000transport.threadFactory.bossThreadPrefix=NettyBosstransport.threadFactory.workerThreadPrefix=NettyServerNIOWorkertransport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandlertransport.threadFactory.shareBossWorker=falsetransport.threadFactory.clientSelectorThreadPrefix=NettyClientSelectortransport.threadFactory.clientSelectorThreadSize=1transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThreadtransport.threadFactory.bossThreadSize=1transport.threadFactory.workerThreadSize=defaulttransport.shutdown.wait=3transport.serialization=seatatransport.compressor=none#Transaction routing rules configuration, only for the clientservice.vgroupMapping.default_tx_group=default#If you use a registry, you can ignore itservice.default.grouplist=127.0.0.1:8091service.enableDegrade=falseservice.disableGlobalTransaction=falseclient.metadataMaxAgeMs=30000#Transaction rule configuration, only for the clientclient.rm.asyncCommitBufferLimit=10000client.rm.lock.retryInterval=10client.rm.lock.retryTimes=30client.rm.lock.retryPolicyBranchRollbackOnConflict=trueclient.rm.reportRetryCount=5client.rm.tableMetaCheckEnable=trueclient.rm.tableMetaCheckerInterval=60000client.rm.sqlParserType=druidclient.rm.reportSuccessEnable=falseclient.rm.sagaBranchRegisterEnable=falseclient.rm.sagaJsonParser=fastjsonclient.rm.tccActionInterceptorOrder=-2147482648client.tm.commitRetryCount=5client.tm.rollbackRetryCount=5client.tm.defaultGlobalTransactionTimeout=60000client.tm.degradeCheck=falseclient.tm.degradeCheckAllowTimes=10client.tm.degradeCheckPeriod=2000client.tm.interceptorOrder=-2147482648client.undo.dataValidation=trueclient.undo.logSerialization=jacksonclient.undo.onlyCareUpdateColumns=trueserver.undo.logSaveDays=7server.undo.logDeletePeriod=86400000client.undo.logTable=undo_logclient.undo.compress.enable=trueclient.undo.compress.type=zipclient.undo.compress.threshold=64k#For TCC transaction modetcc.fence.logTableName=tcc_fence_logtcc.fence.cleanPeriod=1h# You can choose from the following options: fastjson, jackson, gsontcc.contextJsonParserType=fastjson#Log rule configuration, for client and serverlog.exceptionRate=100#Transaction storage configuration, only for the server. The file, db, and redis configuration values are optional.store.mode=filestore.lock.mode=filestore.session.mode=file#Used for password encryptionstore.publicKey=#If `store.mode,store.lock.mode,store.session.mode` are not equal to `file`, you can remove the configuration block.store.file.dir=file_store/datastore.file.maxBranchSessionSize=16384store.file.maxGlobalSessionSize=512store.file.fileWriteBufferCacheSize=16384store.file.flushDiskMode=asyncstore.file.sessionReloadReadSize=100#These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.store.db.datasource=druidstore.db.dbType=mysqlstore.db.driverClassName=com.mysql.jdbc.Driverstore.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=truestore.db.user=usernamestore.db.password=passwordstore.db.minConn=5store.db.maxConn=30store.db.globalTable=global_tablestore.db.branchTable=branch_tablestore.db.distributedLockTable=distributed_lockstore.db.queryLimit=100store.db.lockTable=lock_tablestore.db.maxWait=5000#These configurations are required if the `store mode` is `redis`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `redis`, you can remove the configuration block.store.redis.mode=singlestore.redis.type=pipelinestore.redis.single.host=127.0.0.1store.redis.single.port=6379store.redis.sentinel.masterName=store.redis.sentinel.sentinelHosts=store.redis.sentinel.sentinelPassword=store.redis.maxConn=10store.redis.minConn=1store.redis.maxTotal=100store.redis.database=0store.redis.password=store.redis.queryLimit=100#Transaction rule configuration, only for the serverserver.recovery.committingRetryPeriod=1000server.recovery.asynCommittingRetryPeriod=1000server.recovery.rollbackingRetryPeriod=1000server.recovery.timeoutRetryPeriod=1000server.maxCommitRetryTimeout=-1server.maxRollbackRetryTimeout=-1server.rollbackRetryTimeoutUnlockEnable=falseserver.distributedLockExpireTime=10000server.session.branchAsyncQueueSize=5000server.session.enableBranchAsyncRemove=falseserver.enableParallelRequestHandle=trueserver.enableParallelHandleBranch=falseserver.applicationDataLimit=64000server.applicationDataLimitCheck=falseserver.raft.server-addr=127.0.0.1:7091,127.0.0.1:7092,127.0.0.1:7093server.raft.snapshotInterval=600server.raft.applyBatch=32server.raft.maxAppendBufferSize=262144server.raft.maxReplicatorInflightMsgs=256server.raft.disruptorBufferSize=16384server.raft.electionTimeoutMs=2000server.raft.reporterEnabled=falseserver.raft.reporterInitialDelay=60server.raft.serialization=jacksonserver.raft.compressor=noneserver.raft.sync=true#Metrics configuration, only for the servermetrics.enabled=falsemetrics.registryType=compactmetrics.exporterList=prometheusmetrics.exporterPrometheusPort=9898 这里我们主要修改以下几个地方 123456789store.mode=dbstore.lock.mode=dbstore.session.mode=dbstore.db.driverClassName=com.mysql.cj.jdbc.Driver# 这里的IP是我们为MySQL容器分配的IDstore.db.url=jdbc:mysql://172.20.0.2:3306/seata?useUnicode=true&rewriteBatchedStatements=truestore.db.user=rootstore.db.password=123456 修改完毕后点击下面的发布即可。 在Nacos中保存Seata的配置 至此,我们在Nacos的配置已经完成,接下来的步骤就是将Seata注册到Nacos上 2.6.4 注册Seata到Nacos中进入到Seata容器中,我们可以找到/seata-server/resources/application.yml这个文件,Seata本质上也是一个SpringBoot项目,所以可以修改application.yml改变Seata的一些参数。 /seata-server/resources/application.yml 123456789101112131415161718192021222324252627282930313233343536373839server: port: 7091spring: application: name: seata-serverlogging: config: classpath:logback-spring.xml file: path: ${log.home:${user.home}/logs/seata} extend: logstash-appender: destination: 127.0.0.1:4560 kafka-appender: bootstrap-servers: 127.0.0.1:9092 topic: logback_to_logstashconsole: user: username: seata password: seataseata: config: # support: nacos, consul, apollo, zk, etcd3 type: file registry: # support: nacos, eureka, redis, zk, consul, etcd3, sofa type: file store: # support: file 、 db 、 redis 、 raft mode: file # server: # service-port: 8091 #If not configured, the default is '${server.port} + 1000' security: secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017 tokenValidityInMilliseconds: 1800000 ignore: urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login 从配置文件可以知道,Seata运行在7091端口上,控制台的用户名和密码都是seata,相关的数据都是文件形式保存在容器中的,所以现在需要修改seata.config和seata.registry,使得Seata能够正确的注册到Nacos上。 1234567seata: config: # support: nacos, consul, apollo, zk, etcd3 type: file registry: # support: nacos, eureka, redis, zk, consul, etcd3, sofa type: file 根据application.example.yml的内容来看,可以知道nacos相关的配置如下,并修改为Nacos的相关信息 12345678910111213141516171819202122232425262728seata: config: # support: nacos, consul, apollo, zk, etcd3 type: nacos nacos: server-addr: 172.20.0.4:8848 namespace: # seata.properties所属的Group的名称 group: DEFAULT_GROUP context-path: # 我们使用第一种鉴权方式:用户名和密码 username: nacos password: nacos data-id: seata.properties registry: # support: nacos, eureka, redis, zk, consul, etcd3, sofa type: nacos preferred-networks: 30.240.* nacos: # 在Nacos中显示Seata的名称 application: seata-server server-addr: 172.20.0.4:8848 group: DEFAULT_GROUP namespace: cluster: default context-path: /nacos username: nacos password: nacos 启动Seata容器,就可以看到Nacos里已经出现Seata服务了。这时候访问http://宿主IP:7091/也是可以访问到Seata的后台页面的 Seata成功在Nacos中注册Seata后台管理页面 现在,一个最基本的微服务环境已经完成。 3. 坑点总结启动Seata容器的时候,SEATA_IP一定是宿主机的IP,否则项目链接的时候会报连接不到Seata服务的异常 1docker run -itd --name Seata --network myNetwork --ip 172.20.0.6 -e SEATA_IP=192.168.10.254 -p 8091:8091 -p 7091:7091 seataio/seata-server","categories":[{"name":"教程","slug":"教程","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/"},{"name":"微服务","slug":"教程/微服务","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"}],"tags":[{"name":"微服务","slug":"微服务","permalink":"https://summerain0.top/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"},{"name":"Docker","slug":"Docker","permalink":"https://summerain0.top/tags/Docker/"}]},{"title":"Hexo7迁移指南","slug":"Hexo7迁移指南","date":"2023-12-12T01:44:38.000Z","updated":"2026-06-16T11:40:42.470Z","comments":true,"path":"/2023/12/12/Hexo7迁移指南/","permalink":"https://summerain0.top/2023/12/12/Hexo7%E8%BF%81%E7%A7%BB%E6%8C%87%E5%8D%97/","excerpt":"","text":"前言Hexo 7已正式发布,本文针对于v6向v7迁移的用户。 破坏性更改Node.js要求Hexo7的Node.js要求版本不低于14 标签移除external_link在7.0中,移除了布尔类型的external_link选项,现在推荐使用以下方式进行配置 1234external_link: enable: true # 在新标签中打开链接 field: site # 对整个网站(site)生效或仅对文章(post)生效 exclude: '' # 需要排除的域名。主域名和子域名如 www 需分别配置 use_date_for_updateduse_date_for_updated选项已经在v7.0.0+中被移除。请改为使用updated_option: 'date'。 日期 / 时间格式https://hexo.io/zh-cn/docs/configuration#%E6%97%A5%E6%9C%9F-x2F-%E6%97%B6%E9%97%B4%E6%A0%BC%E5%BC%8F front-matter的link选项front-matter中,link选项被移除。 部分标签移出Hexo的内置标签hexo7中不再内置以下4个标签 gist youtube jsfiddle vimeo 如果仍然需要这些标签,可以引入hexo-tag-embed继续在Hexo中使用它们。 1npm i hexo-tag-embed 代码高亮代码高亮相关代码被重构,且其配置也发生了改变,目前代码高亮由syntax_highlighter选项控制,且其值可以为:highlight.js、prismjs或空字符串 在7.0之前,我们代码高亮的配置是这样的 12345678910111213141516highlight: enable: true auto_detect: false line_number: true line_threshold: 0 tab_replace: '' exclude_languages: - example wrap: true hljs: falseprismjs: enable: false preprocess: true line_number: true line_threshold: 0 tab_replace: '' 在7.0之后,enable的功能就由syntax_highlighter负责 1234567891011121314151617# syntax_highlighter: # 表示不使用任何高亮syntax_highlighter: highlight.js # 表示使用highlight.js# syntax_highlighter: prismjs # 表示使用prismjshighlight: auto_detect: false line_number: true line_threshold: 0 tab_replace: '' exclude_languages: - example wrap: true hljs: falseprismjs: preprocess: true line_number: true line_threshold: 0 tab_replace: ''","categories":[{"name":"教程","slug":"教程","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/"},{"name":"Hexo","slug":"教程/Hexo","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/Hexo/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://summerain0.top/tags/Hexo/"}]},{"title":"MySQL时区问题","slug":"MySQL时区问题","date":"2023-12-10T14:10:30.000Z","updated":"2026-06-16T11:40:42.470Z","comments":true,"path":"/2023/12/10/MySQL时区问题/","permalink":"https://summerain0.top/2023/12/10/MySQL%E6%97%B6%E5%8C%BA%E9%97%AE%E9%A2%98/","excerpt":"","text":"MySQL的控制台执行以下命令: 123set global time_zone = '+8:00'; # 修改MySQL全局时区为北京时间,即我们所在的东8区set time_zone = '+8:00'; # 修改当前会话时区flush privileges; # 立即生效 然后项目重启即可","categories":[{"name":"教程","slug":"教程","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/"},{"name":"MySQL","slug":"教程/MySQL","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/MySQL/"}],"tags":[{"name":"MySQL","slug":"MySQL","permalink":"https://summerain0.top/tags/MySQL/"}]},{"title":"Java新特性-Stream流","slug":"java中stream的使用","date":"2022-09-19T15:31:50.000Z","updated":"2026-06-16T11:40:42.470Z","comments":true,"path":"/2022/09/19/java中stream的使用/","permalink":"https://summerain0.top/2022/09/19/java%E4%B8%ADstream%E7%9A%84%E4%BD%BF%E7%94%A8/","excerpt":"","text":"1. 什么是Stream流?Stream是一种类似于工厂的流水线的流式思想。在工厂的流水线上,我们可以在每个关口设置不同的条件进行筛选、检查并在尾部输出最终的成品,这里的Stream也可以这样理解。 2. 案例2.1 找出姓名集合中以张开头的姓名集合原来我们的解决方案是 12345678910111213141516public class Main { public static void main(String[] args) { ArrayList<String> names = new ArrayList<>(); names.add("老大"); names.add("小二"); names.add("张三"); names.add("张四"); names.add("王五"); names.add("张玖"); ArrayList<String> list = new ArrayList<>(); names.forEach(s -> { if (s.startsWith("张")) list.add(s); }); System.out.println(list); }} 而使用Stream后不仅方便,代码量也大大减少,而且代码的可读性也更强 12345678910111213141516public class Main { public static void main(String[] args) { ArrayList<String> names = new ArrayList<>(); names.add("老大"); names.add("小二"); names.add("张三"); names.add("张四"); names.add("王五"); names.add("张玖"); // 过滤出以张开头的姓名,并将结果转换成List方便我们处理 List<String> list = names.stream() .filter(s -> s.startsWith("张")) .collect(Collectors.toList()); System.out.println(list); }} 2.2 复杂的集合操作假定我们有一组随机生成的整数集合,我们要过滤出50及以内的数,且剔除重复元素后取前10个进行从小到大排序输出。 有点麻烦是吧?但不难,在一般情况下我们是这么操作的 12345678910111213141516public class Main { public static void main(String[] args) { ArrayList<Integer> numList = new ArrayList<>(200); Random random = new Random(); for (int i = 0; i < 200; i++) { numList.add(random.nextInt(101)); } ArrayList<Integer> list = new ArrayList<>(); numList.forEach(num -> { if (num <= 50 && !list.contains(num)) list.add(num); }); List<Integer> tenNumbers = list.subList(0, 10); tenNumbers.sort(Comparator.comparingInt(o -> o)); System.out.println(tenNumbers); }} 在使用Stream进行处理后,代码就变得十分简洁 12345678910111213141516public class Main { public static void main(String[] args) { ArrayList<Integer> numList = new ArrayList<>(200); Random random = new Random(); for (int i = 0; i < 200; i++) { numList.add(random.nextInt(101)); } List<Integer> list = numList.stream() .filter(num -> num <= 50) .distinct() .limit(10) .sorted() .collect(Collectors.toList()); System.out.println(list); }} 是不是很Easy? 3. 方法使用那么我们怎么使用Stream方便我们的开发呢?Stream的方法不多,我们一一讲解。我们先总览一下有哪些方法,一看就知道的方法就不说了 测试环境:JDK11 点开查看 方法描述builder返回一个构造器of构造Stream流filter过滤出符合要求的元素map将流中的元素映射到另一个流中mapToInt将流中元素转成int类型,其他相似方法同理flatMap合并数据flatMapToInt合并数据并转换成int类型,其他相似方法同理distinct剔除重复元素sorted从小到大排序,sorted(Comparator<? super T> comparator)为按一定规则进行排序peek在流(一个步骤)工作之前插入一个操作,但在这里改变元素并不会生效limit获取前n个元素,如果总元素小于n,则不进行操作skip跳过前n个元素takeWhile逐个获取符合规则的元素,遇到不符合的立马结束操作,丢弃后面的所有元素dropWhile逐个删除符合规则的元素,遇到不符合的立马结束操作,返回剩下的所有元素forEach遍历元素,在并行流中输出元素不保证与原来的一致forEachOrdered在并行流中保证输出顺序一致reduce对Stream元素进行聚合求值collect将流转成你想要的类型anyMatch只要有一个元素符合规则就返回TrueallMatch只有每个元素均符合规则时才会返回TruenoneMatch只有每个元素都不符合规则时返回TruefindAny随便返回一个元素,没错,你没看错 3.1 获取Stream流的三种方法3.1.1 builder12345678public class Main { public static void main(String[] args) { Stream.Builder<Integer> builder = Stream.builder(); builder.add(1); builder.add(2); Stream<Integer> stream = builder.build(); }} 3.1.2 of12345678910public class Main { public static void main(String[] args) { Stream<String> stream1 = Stream.of("张三", "李四", "王五"); Integer[] numbers = new Integer[]{1, 2, 3, 4, 5, 6}; Stream<Integer> stream2 = Stream.of(numbers); Stream<String> stream3 = Stream.ofNullable(null); }} 3.1.3 Collection.stream()任何实现Collection接口的类均可调用stream()获取到Stream流 123456789101112131415161718public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); Stream<Integer> stream1 = nums.stream(); Set<String> set = new HashSet<>(); Stream<String> stream2 = set.stream(); Vector<String> vector = new Vector<>(); Stream<String> stream3 = vector.stream(); // Map不是Collection的子接口,所以需要分别处理 Map<String, String> map = new HashMap<>(); Stream<String> keyStream = map.keySet().stream(); Stream<String> valueStream = map.values().stream(); Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream(); }} 3.2 map将流中的元素映射到另一个流中,在这中间我们可以对元素进行处理,如以下代码是将元素转成int类型,且将456改成999 12345678910111213141516public class Main { public static void main(String[] args) { ArrayList<String> strList = new ArrayList<>(); strList.add("123"); strList.add("456"); strList.add("789"); List<Integer> nums = strList.stream().map( s -> { if (s.equals("456")) return 999; else return Integer.parseInt(s); } ).collect(Collectors.toList()); // 输出[123, 999, 789] System.out.println(nums); }} 3.3 mapToInt将流中元素转成int类型,其他相似方法同理 123456789101112131415public class Main { public static void main(String[] args) { ArrayList<String> strList = new ArrayList<>(); strList.add("123"); strList.add("456"); strList.add("789"); List<Integer> nums = strList.stream() .mapToInt(Integer::parseInt) // 注意要写上这句 .boxed() .collect(Collectors.toList()); // 输出[123, 456, 789] System.out.println(nums); }} 3.4 flatMap把两个列表的数据合并成一个列表数据 123456789101112131415161718public class Main { public static void main(String[] args) { List<List<Integer>> lists = new ArrayList<>(); List<Integer> list = new ArrayList<>(); list.add(4444); list.add(33333); list.add(444444); // 添加两次 lists.add(list); lists.add(list); System.out.println(lists); List<Integer> result = lists.stream().flatMap(Collection::stream).collect(Collectors.toList()); System.out.println(result); // 输出 // [[4444, 33333, 444444], [4444, 33333, 444444]] // [4444, 33333, 444444, 4444, 33333, 444444] }} 3.5 flatMapToInt把两个列表的数据合并成一个列表数据并将数据转成int类型 123456789101112131415161718192021public class Main { public static void main(String[] args) { List<List<Integer>> lists = new ArrayList<>(); List<Integer> list = new ArrayList<>(); list.add(4444); list.add(33333); list.add(444444); // 添加两次 lists.add(list); lists.add(list); System.out.println(lists); List<Integer> result = lists.stream() .flatMapToInt(integers -> integers.stream().mapToInt(value -> value)) .boxed() .collect(Collectors.toList()); System.out.println(result); // 输出 // [[4444, 33333, 444444], [4444, 33333, 444444]] // [4444, 33333, 444444, 4444, 33333, 444444] }} 3.6 filter1234567891011121314151617public class Main { public static void main(String[] args) { ArrayList<String> names = new ArrayList<>(); names.add("老大"); names.add("小二"); names.add("张三"); names.add("张四"); names.add("王五"); names.add("张玖"); ArrayList<String> list = new ArrayList<>(); names.forEach(s -> { if (s.startsWith("张")) list.add(s); }); // 输出[张三, 张四, 张玖] System.out.println(list); }} 3.7 sorted对元素进行排序 12345678910111213141516171819202122232425public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(8); nums.add(18); nums.add(1); nums.add(15); nums.add(20); nums.add(3); System.out.println(nums); List<Integer> list1 = nums.stream() .sorted() .collect(Collectors.toList()); List<Integer> list2 = nums.stream() .sorted((o1, o2) -> o2 - o1) .collect(Collectors.toList()); System.out.println(list1); System.out.println(list2); // 输出 // [5, 8, 18, 1, 15, 20, 3] // [1, 3, 5, 8, 15, 18, 20] // [20, 18, 15, 8, 5, 3, 1] }} 3.8 peek在流(一个步骤)工作之前插入一个操作,但在这里改变元素并不会生效 1234567891011121314151617public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(8); nums.add(18); nums.add(1); nums.add(15); nums.add(20); nums.add(3); nums.stream() .peek(integer -> System.out.println("当前在处理:" + integer)) .sorted() .peek(integer -> System.out.println("当前在准备输出:" + integer)) .forEach(System.out::println); }} 控制台 123456789101112131415161718192021当前在处理:5当前在处理:8当前在处理:18当前在处理:1当前在处理:15当前在处理:20当前在处理:3当前在准备输出:11当前在准备输出:33当前在准备输出:55当前在准备输出:88当前在准备输出:1515当前在准备输出:1818当前在准备输出:2020 3.9 limit获取前n个元素,如果总元素小于n,则不进行操作 12345678910111213141516171819202122232425public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(8); nums.add(18); nums.add(1); nums.add(15); nums.add(20); nums.add(3); System.out.println(nums); List<Integer> list1 = nums.stream() .limit(3) .collect(Collectors.toList()); List<Integer> list2 = nums.stream() .limit(100) .collect(Collectors.toList()); System.out.println(list1); System.out.println(list2); // 输出 // [5, 8, 18, 1, 15, 20, 3] // [5, 8, 18] // [5, 8, 18, 1, 15, 20, 3] }} 3.10 skip跳过前n个元素,元素小于等于n个则返回一个空流 1234567891011121314151617181920public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(8); nums.add(18); nums.add(1); nums.add(15); nums.add(20); nums.add(3); System.out.println(nums); List<Integer> list = nums.stream() .skip(4) .collect(Collectors.toList()); System.out.println(list); // 输出 // [5, 8, 18, 1, 15, 20, 3] // [15, 20, 3] }} 3.11 takeWhile逐个获取符合规则的元素,遇到不符合的立马结束操作,丢弃后面的所有元素 1234567891011121314151617181920public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(8); nums.add(18); nums.add(1); nums.add(15); nums.add(20); nums.add(3); System.out.println(nums); List<Integer> list = nums.stream() .takeWhile(integer -> integer < 10) .collect(Collectors.toList()); System.out.println(list); // 输出 // [5, 8, 18, 1, 15, 20, 3] // [5, 8] }} 3.12 dropWhile逐个删除符合规则的元素,遇到不符合的立马结束操作,返回剩下的所有元素 1234567891011121314151617181920public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(8); nums.add(18); nums.add(1); nums.add(15); nums.add(20); nums.add(3); System.out.println(nums); List<Integer> list = nums.stream() .dropWhile(integer -> integer != 1) .collect(Collectors.toList()); System.out.println(list); // 输出 // [5, 8, 18, 1, 15, 20, 3] // [1, 15, 20, 3] }} 3.13 forEach遍历元素,在并行流中输出元素不保证与原来的一致 123456789101112131415public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(8); nums.add(18); nums.add(1); nums.stream().forEach(System.out::println); // 输出 // 5 // 8 // 18 // 1 }} 3.14 forEachOrder在并行流中保证输出顺序一致 12345678910111213141516171819202122232425262728public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(8); nums.add(18); nums.add(1); System.out.println(nums); // forEach 发现顺序和原来的不一样 System.out.println("forEach"); nums.stream().parallel().forEach(System.out::println); // forEachOrdered 顺序和原来的一样 System.out.println("forEachOrdered"); nums.stream().parallel().forEachOrdered(System.out::println); // 输出 // [5, 8, 18, 1] // forEach // 18 // 1 // 5 // 8 // forEachOrdered // 5 // 8 // 18 // 1 }} 3.15 reduce对Stream元素进行归约 12345678910111213public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(8); nums.add(18); nums.add(1); long sum = nums.stream().reduce(Integer::sum).get(); // 5+8+18+1=32 // 输出 32 System.out.println(sum); }} 3.16 collect将流转成你想要的类型 123456789101112131415public class Main { public static void main(String[] args) { ArrayList<String> strList = new ArrayList<>(); strList.add("123"); strList.add("456"); strList.add("789"); List<Integer> nums = strList.stream() .mapToInt(Integer::parseInt) .boxed() // 转换成List<Integer>集合 .collect(Collectors.toList()); // 输出[123, 456, 789] System.out.println(nums); }} 3.17 anyMatch只要有一个元素符合规则就返回True 123456789101112131415public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(8); nums.add(18); nums.add(1); boolean result1 = nums.stream().anyMatch(integer -> integer == 8); // True System.out.println(result1); boolean result2 = nums.stream().anyMatch(integer -> integer == 5000); // False System.out.println(result2); }} 3.18 allMatch只有每个元素均符合规则时才会返回True 123456789101112131415public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(8); nums.add(18); nums.add(1); boolean result1 = nums.stream().allMatch(integer -> integer < 8); // False System.out.println(result1); boolean result2 = nums.stream().allMatch(integer -> integer > 0); // True System.out.println(result2); }} 3.19 noneMatch只有每个元素都不符合规则时返回True 123456789101112131415public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(8); nums.add(18); nums.add(1); boolean result1 = nums.stream().noneMatch(integer -> integer < 8); // False System.out.println(result1); boolean result2 = nums.stream().noneMatch(integer -> integer == 0); // True System.out.println(result2); }} 3.20 findAny随便返回一个元素,没错,你没看错 123456789101112public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(8); nums.add(18); nums.add(1); int ele = nums.stream().findAny().get(); // 输出8 System.out.println(ele); }} 3.21 distinct对元素进行去重操作 123456789101112131415161718192021public class Main { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(5); nums.add(8); nums.add(8); nums.add(18); nums.add(18); nums.add(1); nums.add(1); System.out.println(nums); List<Integer> list = nums.stream() .distinct() .collect(Collectors.toList()); // 输出 // [5, 5, 8, 8, 18, 18, 1, 1] // [5, 8, 18, 1] System.out.println(list); }} 4. 并行流、串行流4.1 串行流这是案例里的代码,此时我们是串行流,即在一条线程中处理 12345678910111213141516public class Main { public static void main(String[] args) { ArrayList<String> names = new ArrayList<>(); names.add("老大"); names.add("小二"); names.add("张三"); names.add("张四"); names.add("王五"); names.add("张玖"); // 过滤出以张开头的姓名,并将结果转换成List方便我们处理 List<String> list = names.stream() .filter(s -> s.startsWith("张")) .collect(Collectors.toList()); System.out.println(list); }} 1234567Thread[main,5,main]Thread[main,5,main]Thread[main,5,main]Thread[main,5,main]Thread[main,5,main]Thread[main,5,main][张三, 张四, 张玖] 4.2 并行流parallelStream其实就是一个并行执行的流。它通过默认的ForkJoinPool,可能提高多线程任务的速度。可以通过以下方式获取 12345ArrayList<Integer> list = new ArrayList<>();// 直接获取并行的流 Stream<Integer> stream = list.parallelStream();// 将串行流转成并行流Stream<Integer> stream = list.stream().parallel(); 并行执行效果 1234567891011121314151617181920public class Main { public static void main(String[] args) { ArrayList<String> names = new ArrayList<>(); names.add("老大"); names.add("小二"); names.add("张三"); names.add("张四"); names.add("王五"); names.add("张玖"); // 过滤出以张开头的姓名,并将结果转换成List方便我们处理 List<String> list = names.stream() .parallel() .filter(s -> { System.out.println(Thread.currentThread()); return s.startsWith("张"); }) .collect(Collectors.toList()); System.out.println(list); }} 1234567Thread[main,5,main]Thread[ForkJoinPool.commonPool-worker-19,5,main]Thread[ForkJoinPool.commonPool-worker-23,5,main]Thread[ForkJoinPool.commonPool-worker-5,5,main]Thread[ForkJoinPool.commonPool-worker-9,5,main]Thread[ForkJoinPool.commonPool-worker-19,5,main][张三, 张四, 张玖] 4.3 执行效率对比在百万级数据处理时,串行流和并行流的耗时分别为131ms和55ms 12345678910111213141516171819202122public class Main { public static void main(String[] args) { ArrayList<Integer> names = new ArrayList<>(10000000); Random random = new Random(); for (int i = 0; i < 10000000; i++) { names.add(random.nextInt(1000)); } long startTime = System.currentTimeMillis(); long sum = names.stream() .reduce(Integer::sum) .get(); // 131ms System.out.println("串行流耗时:" + (System.currentTimeMillis() - startTime) + "ms"); startTime = System.currentTimeMillis(); long sum1 = names.parallelStream() .reduce(Integer::sum) .get(); // 55ms System.out.println("并行流耗时:" + (System.currentTimeMillis() - startTime) + "ms"); }}","categories":[{"name":"教程","slug":"教程","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/"},{"name":"Java","slug":"教程/Java","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/Java/"}],"tags":[{"name":"Java","slug":"Java","permalink":"https://summerain0.top/tags/Java/"}]},{"title":"Android自定义全局异常捕获——Activity形式","slug":"Android中全局异常捕获-可视化","date":"2022-09-03T15:57:30.000Z","updated":"2026-06-16T11:40:42.470Z","comments":true,"path":"/2022/09/03/Android中全局异常捕获-可视化/","permalink":"https://summerain0.top/2022/09/03/Android%E4%B8%AD%E5%85%A8%E5%B1%80%E5%BC%82%E5%B8%B8%E6%8D%95%E8%8E%B7-%E5%8F%AF%E8%A7%86%E5%8C%96/","excerpt":"","text":"前言开发安卓的小伙伴都遇到过APP突然崩溃,无响应的情况.如果发生在自己手中,那么还可以通过IDE查看错误日志,但是实际都是发生在用户手中,那么这个时候产生崩溃,无响应ANR异常就很麻烦.无从下手.因此,需要全局异常捕获.也就是对未知异常,程序员没有处理的异常进行处理,记录等便于分析查找原因,而一个美观的崩溃提示则可以大大加分 。 效果展示效果展示 源码MyUncaughtExceptionHandler.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152import android.content.Context;import android.content.Intent;import android.content.pm.PackageInfo;import android.content.pm.PackageManager;import android.os.Build;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.PrintWriter;import java.io.StringWriter;import java.io.Writer;import java.lang.reflect.Field;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Arrays;import java.util.Date;import java.util.Locale;/** * @ClassName MyUncaughtExceptionHandler * @Description 全局捕捉异常 * @Author summerain0 * @Date 2020/9/11 15:31 */public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { // 单例 private static MyUncaughtExceptionHandler myUncaughtExceptionHandler; // 上下文 private Context context; // 会输出到文件中 private StringBuilder stringBuilder; // 系统异常处理器 private Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler; public MyUncaughtExceptionHandler(Context context) { this.context = context; } // 获取单例 public static synchronized MyUncaughtExceptionHandler getInstance(Context ctx) { if (myUncaughtExceptionHandler == null) { myUncaughtExceptionHandler = new MyUncaughtExceptionHandler(ctx); } return myUncaughtExceptionHandler; } // 初始化 public void init() { defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); } @Override public void uncaughtException(Thread thread, Throwable throwable) { if (throwable == null) { defaultUncaughtExceptionHandler.uncaughtException(thread, throwable); } // 创建集合对象 stringBuilder = new StringBuilder(); // 记录时间 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss.SSS", Locale.getDefault()); String date = simpleDateFormat.format(new Date()); addMessage("崩溃时间", date); // 记录应用版本信息 try { PackageManager pm = context.getPackageManager(); PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES); addMessage("版本名", pi.versionName); addMessage("版本号", pi.versionCode); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); addMessage("error", "记录版本信息失败!" + e.getMessage()); } // 记录设备信息 Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields) { try { field.setAccessible(true); Object obj = field.get(null); if (obj != null) { addMessage(field.getName(), obj); } } catch (IllegalAccessException e) { e.printStackTrace(); addMessage("error", "记录设备信息失败!" + e.getMessage()); } } // 添加分隔符 addMessage(null, "=============================================================="); addMessage(null, "======================== 崩溃日志 ========================="); addMessage(null, "=============================================================="); // 记录崩溃信息 Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); throwable.printStackTrace(printWriter); Throwable cause = throwable.getCause(); while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.close(); addMessage(null, writer.toString()); // 生成路径,保存至/Android/data/包名,无需读写权限 try { File root = context.getExternalFilesDir("log"); String filename = date + ".log"; File file = new File(root, filename); FileOutputStream fos = new FileOutputStream(file); fos.write(stringBuilder.toString().getBytes()); fos.close(); } catch (IOException e) { e.printStackTrace(); defaultUncaughtExceptionHandler.uncaughtException(thread, throwable); } // 启动崩溃异常页面 Intent intent = new Intent(context, UncaughtExceptionActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// 请勿修改,否则无法打开页面 intent.putExtra("error", stringBuilder.toString()); context.startActivity(intent); System.exit(1);// 请勿修改,否则无法打开页面 } // 添加数据 private void addMessage(String key, Object obj) { // 对数组做一下处理 if (obj instanceof String[]) { String[] list = (String[]) obj; ArrayList<String> array = new ArrayList<>(Arrays.asList(list)); stringBuilder.append(key).append("=").append(array.toString()).append("\\n"); } // 其他的都直接添加 if (key == null) { stringBuilder.append(obj) .append("\\n"); } else { stringBuilder.append(key) .append("=") .append(obj) .append("\\n"); } }} UncaughtExceptionActivity.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657import android.content.Intent;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;import android.widget.ScrollView;import android.widget.TextView;import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;/** * @ClassName UncaughtExceptionActivity * @Description 异常页面 * @Author summerain0 * @Date 2020/9/12 11:02 */public class UncaughtExceptionActivity extends AppCompatActivity { public static final String TAG = "UncaughtExceptionActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 读取日志,显示在屏幕上 String msg = getIntent().getStringExtra("error"); ScrollView scrollView = new ScrollView(this);// 防止日志太长看不完 scrollView.setLayoutParams(new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT, ScrollView.LayoutParams.MATCH_PARENT)); TextView textView = new TextView(this); textView.setText(msg); scrollView.addView(textView); setContentView(scrollView); } @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, 0, 0, "重启").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); menu.add(0, 1, 0, "上传").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case 0: Intent intent = new Intent(this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// 请勿修改,否则无法打开页面 startActivity(intent); System.exit(1);// 请勿修改,否则无法打开页面 case 1: // 这里自己写上传逻辑 Toast.makeText(this, "已上传!", Toast.LENGTH_SHORT).show(); } return true; }} MyApplication.java123456789101112131415161718import android.app.Application;/** * @ClassName MyApplication * @Description TODO * @Author summerain0 * @Date 2020/9/11 14:00 */public class MyApplication extends Application { public static final String TAG = "MyApplication"; @Override public void onCreate() { super.onCreate(); // 初始化异常处理器 MyUncaughtExceptionHandler.getInstance(MyApplication.this).init(); }} AndroidManifest.xml123456789<application android:name=".MyApplication" ......> <activity android:label="崩溃异常" android:name=".UncaughtExceptionActivity" /></application>","categories":[{"name":"教程","slug":"教程","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/"},{"name":"Android","slug":"教程/Android","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/Android/"}],"tags":[{"name":"Android","slug":"Android","permalink":"https://summerain0.top/tags/Android/"},{"name":"Java","slug":"Java","permalink":"https://summerain0.top/tags/Java/"}]},{"title":"自定义全局异常捕捉——保存至本地","slug":"Android中全局异常捕获-文件","date":"2022-09-03T15:57:30.000Z","updated":"2026-06-16T11:40:42.470Z","comments":true,"path":"/2022/09/03/Android中全局异常捕获-文件/","permalink":"https://summerain0.top/2022/09/03/Android%E4%B8%AD%E5%85%A8%E5%B1%80%E5%BC%82%E5%B8%B8%E6%8D%95%E8%8E%B7-%E6%96%87%E4%BB%B6/","excerpt":"","text":"前言开发安卓的小伙伴都遇到过APP突然崩溃,无响应的情况.如果发生在自己手中,那么还可以通过IDE查看错误日志,但是实际都是发生在用户手中,那么这个时候产生崩溃,无响应ANR异常就很麻烦.无从下手.因此,需要全局异常捕获.也就是对未知异常,程序员没有处理的异常进行处理,记录等便于分析查找原因. 源码MyUncaughtExceptionHandler.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144import android.content.Context;import android.content.pm.PackageInfo;import android.content.pm.PackageManager;import android.os.Build;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.PrintWriter;import java.io.StringWriter;import java.io.Writer;import java.lang.reflect.Field;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Arrays;import java.util.Date;import java.util.Locale;/** * @ClassName MyUncaughtExceptionHandler * @Description 全局捕捉异常 * @Author summerain0 * @Date 2020/9/11 15:31 */public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { // 单例 private static MyUncaughtExceptionHandler myUncaughtExceptionHandler; // 上下文 private Context context; // 会输出到文件中 private StringBuilder stringBuilder; // 系统异常处理器 private Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler; public MyUncaughtExceptionHandler(Context context) { this.context = context; } // 获取单例 public static synchronized MyUncaughtExceptionHandler getInstance(Context ctx) { if (myUncaughtExceptionHandler == null) { myUncaughtExceptionHandler = new MyUncaughtExceptionHandler(ctx); } return myUncaughtExceptionHandler; } // 初始化 public void init() { defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); } @Override public void uncaughtException(Thread thread, Throwable throwable) { if (throwable == null) { defaultUncaughtExceptionHandler.uncaughtException(thread, throwable); } // 创建集合对象 stringBuilder = new StringBuilder(); // 记录时间 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss.SSS", Locale.getDefault()); String date = simpleDateFormat.format(new Date()); addMessage("崩溃时间", date); // 记录应用版本信息 try { PackageManager pm = context.getPackageManager(); PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES); addMessage("版本名", pi.versionName); addMessage("版本号", pi.versionCode); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); addMessage("error", "记录版本信息失败!" + e.getMessage()); } // 记录设备信息 Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields) { try { field.setAccessible(true); Object obj = field.get(null); if (obj != null) { addMessage(field.getName(), obj); } } catch (IllegalAccessException e) { e.printStackTrace(); addMessage("error", "记录设备信息失败!" + e.getMessage()); } } // 添加分隔符 addMessage(null, "=============================================================="); addMessage(null, "======================== 崩溃日志 ========================="); addMessage(null, "=============================================================="); // 记录崩溃信息 Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); throwable.printStackTrace(printWriter); Throwable cause = throwable.getCause(); while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.close(); addMessage(null, writer.toString()); // 生成路径,保存至/Android/data/包名,无需读写权限 try { File root = context.getExternalFilesDir("log"); String filename = date + ".log"; File file = new File(root, filename); FileOutputStream fos = new FileOutputStream(file); fos.write(stringBuilder.toString().getBytes()); fos.close(); } catch (IOException e) { e.printStackTrace(); defaultUncaughtExceptionHandler.uncaughtException(thread, throwable); } } // 添加数据 private void addMessage(String key, Object obj) { // 对数组做一下处理 if (obj instanceof String[]) { String[] list = (String[]) obj; ArrayList&lt;String&gt; array = new ArrayList&lt;&gt;(Arrays.asList(list)); stringBuilder.append(key).append("=").append(array.toString()).append("\\n"); } // 其他的都直接添加 if (key == null) { stringBuilder.append(obj) .append("\\n"); } else { stringBuilder.append(key) .append("=") .append(obj) .append("\\n"); } }} MyApplication.java123456789101112131415161718import android.app.Application;/** * @ClassName MyApplication * @Description TODO * @Author summerain0 * @Date 2020/9/11 14:00 */public class MyApplication extends Application { public static final String TAG = "MyApplication"; @Override public void onCreate() { super.onCreate(); // 初始化异常处理器 MyUncaughtExceptionHandler.getInstance(MyApplication.this).init(); }} AndroidManifest.xml1234<application android:name=".MyApplication" ....></application>","categories":[{"name":"教程","slug":"教程","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/"},{"name":"Android","slug":"教程/Android","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/Android/"}],"tags":[{"name":"Android","slug":"Android","permalink":"https://summerain0.top/tags/Android/"},{"name":"Java","slug":"Java","permalink":"https://summerain0.top/tags/Java/"}]},{"title":"什么?你是学生?还不快来白嫖JetBrains全家桶?","slug":"jetbrains教育许可证","date":"2022-09-02T12:28:29.000Z","updated":"2026-06-16T11:40:42.470Z","comments":true,"path":"/2022/09/02/jetbrains教育许可证/","permalink":"https://summerain0.top/2022/09/02/jetbrains%E6%95%99%E8%82%B2%E8%AE%B8%E5%8F%AF%E8%AF%81/","excerpt":"","text":"前言白嫖原理是JetBrains为学生提供的免费许可证,本篇文章使用学信网进行获取免费的许可证 准备工作学信网获取学籍验证报告 登录学信网,没有账号的请自行注册 学信网登录页面 申请验证报告,完成后结果应和下图差不多一致 申请验证报告 下载验证报告备用 申请许可证 进入免费许可证申请页面 申请教育许可证https://www.jetbrains.com/shop/eform/students 填写相关信息(图片约334k,请耐心等待加载) 教程图片 大约一到三周即可收到邮件(最快次日即可收到) 申请成功通知邮件 注册JetBrains账号(已注册可省略此步骤)注册JetBrains账号https://account.jetbrains.com/login PS:注意使用申请时填写的邮箱,注册后会自动绑定许可证 使用许可证Help -> Register -> 输入账号密码确认即可 IDEA许可证页面IDEA已许可页面 注意事项许可证适用于JetBrains全家桶申请后许可证有效期为一年,可重复申请白嫖可白嫖的软件,建议下载JetBrains ToolBox进行管理AppCodeCLionDataGripdotCoverdotMemorydotTraceGoLandIntelliJ IDEA UltimatePhpStormPyCharmReSharperReSharper C++RiderRubyMineWebStorm","categories":[{"name":"教程","slug":"教程","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/"}],"tags":[{"name":"JetBrains","slug":"JetBrains","permalink":"https://summerain0.top/tags/JetBrains/"},{"name":"教育许可证","slug":"教育许可证","permalink":"https://summerain0.top/tags/%E6%95%99%E8%82%B2%E8%AE%B8%E5%8F%AF%E8%AF%81/"}]}],"categories":[{"name":"教程","slug":"教程","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/"},{"name":"微服务","slug":"教程/微服务","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"},{"name":"Hexo","slug":"教程/Hexo","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/Hexo/"},{"name":"MySQL","slug":"教程/MySQL","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/MySQL/"},{"name":"Java","slug":"教程/Java","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/Java/"},{"name":"Android","slug":"教程/Android","permalink":"https://summerain0.top/categories/%E6%95%99%E7%A8%8B/Android/"}],"tags":[{"name":"微服务","slug":"微服务","permalink":"https://summerain0.top/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"},{"name":"Docker","slug":"Docker","permalink":"https://summerain0.top/tags/Docker/"},{"name":"Hexo","slug":"Hexo","permalink":"https://summerain0.top/tags/Hexo/"},{"name":"MySQL","slug":"MySQL","permalink":"https://summerain0.top/tags/MySQL/"},{"name":"Java","slug":"Java","permalink":"https://summerain0.top/tags/Java/"},{"name":"Android","slug":"Android","permalink":"https://summerain0.top/tags/Android/"},{"name":"JetBrains","slug":"JetBrains","permalink":"https://summerain0.top/tags/JetBrains/"},{"name":"教育许可证","slug":"教育许可证","permalink":"https://summerain0.top/tags/%E6%95%99%E8%82%B2%E8%AE%B8%E5%8F%AF%E8%AF%81/"}]}