真棒!
真棒!
要是eladmin-mp能添加数据权限功能,一定会是非常好用的一个框架
eladmin-mp缺少仅本人数据权限,还有怎样自动处理数据权限
不能装centos8吗
谢谢分享
感谢分享,试试看
大佬,互换个友链, 已添加您了
{ "title": "SerMs", "screenshot": "https://bu.dusays.com/2023/10/11/65264d86ddebb.png", "url": "https://blog.serms.top/", "avatar": "https://bu.dusays.com/2023/10/11/65269ea6226c8.png", "description": "代码如诗,细节成就极致,逻辑成就完美。", "keywords": "SerMs" }
感谢大佬
博主已经添加了
名称: Fostmar博客地址: https://fostmar.online图标:https://fostmar.online/usr/uploads/2023/12/2354092855.webp简介:kali渗透、建站、数码,以博客为核心,打造生态圈
首页
统计
微语
留言
邻居
壁纸
推荐
我的开源
Search
1
快速解决 npm 安装 node-sass 速度慢/错误的问题
24,446 阅读
2
升级 element-ui 2.15.7 后遇到 el-date-picker 警告问题
10,295 阅读
3
Centos 7 彻底卸载清除 Docker 环境
8,728 阅读
4
vue 更新 sass 版本出现大量警告的坑
8,589 阅读
5
前端 axios 中 qs 介绍与使用
8,441 阅读
推荐分享
文章推荐
资源分享
软件开发
异常记录
Linux学习
日常杂记
开源
登录
Search
标签搜索
Java记录
Linux系统
eladmin开源
Web前端
Spring教程
Docker容器
其他
Git教程
Google插件
jpa
好文分享
Nginx配置
异常记录
持续集成工具
数据库
线程池
Typecho博客
Azure管理
Lambda表达式
PowerDesigner
知了小站
不怕学问浅,就怕志气短。
累计撰写
74
篇文章
累计收到
397
条评论
首页
栏目
推荐分享
文章推荐
资源分享
软件开发
异常记录
Linux学习
日常杂记
开源
页面
统计
微语
留言
邻居
壁纸
推荐
我的开源
用户登录
登录
搜索到
18
篇与
的结果
2024-07-30
总结国内加速拉取 Docker 镜像的几种方法
本文介绍多种方法加速Docker镜像拉取,包括利用Nginx、Cloudflare反向代理等方案,使国内服务器更高效获取海外Docker镜像。目前仍可用的镜像(随时可能失效)sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": [ "https://docker.m.daocloud.io", "https://huecker.io", "https://dockerhub.timeweb.cloud", "https://noohub.ru" ] } EOF sudo systemctl daemon-reload sudo systemctl restart docker使用 Nginx 反向代理需要有一台国外服务器, 按下面添加 Nginx 配置即可,配置完成后,在 /etc/docker/daemon.json 中修改成你的域名,具体参考上方配置,然后重启 dockerserver { listen 443 ssl; server_name 域名; ssl_certificate 证书地址; ssl_certificate_key 密钥地址; proxy_ssl_server_name on; # 启用SNI ssl_session_timeout 24h; ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256'; ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; location / { proxy_pass https://registry-1.docker.io; # Docker Hub 的官方镜像仓库 proxy_set_header Host registry-1.docker.io; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 关闭缓存 proxy_buffering off; # 转发认证相关的头部 proxy_set_header Authorization $http_authorization; proxy_pass_header Authorization; # 对 upstream 状态码检查,实现 error_page 错误重定向 proxy_intercept_errors on; # error_page 指令默认只检查了第一次后端返回的状态码,开启后可以跟随多次重定向。 recursive_error_pages on; # 根据状态码执行对应操作,以下为301、302、307状态码都会触发 #error_page 301 302 307 = @handle_redirect; error_page 429 = @handle_too_many_requests; } #处理重定向 location @handle_redirect { resolver 1.1.1.1; set $saved_redirect_location '$upstream_http_location'; proxy_pass $saved_redirect_location; } # 处理429错误 location @handle_too_many_requests { proxy_set_header Host 替换为在CloudFlare Worker设置的域名; # 替换为另一个服务器的地址 proxy_pass http://替换为在CloudFlare Worker设置的域名; proxy_set_header Host $http_host; } }使用 Cloudflare 反向代理登录到 Cloudflare 控制台, 新建 worker, 在 worker.js 文件中输入以下代码, 注意需要自行修改代码中的域名'use strict' const hub_host = 'registry-1.docker.io' const auth_url = 'https://auth.docker.io' const workers_url = 'https://你的域名' const workers_host = '你的域名' const home_page_url = 'https://qninq.cn/file/html/dockerproxy.html' /** @type {RequestInit} */ const PREFLIGHT_INIT = { status: 204, headers: new Headers({ 'access-control-allow-origin': '*', 'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS', 'access-control-max-age': '1728000', }), } /** * @param {any} body * @param {number} status * @param {Object<string, string>} headers */ function makeRes(body, status = 200, headers = {}) { headers['access-control-allow-origin'] = '*' return new Response(body, {status, headers}) } /** * @param {string} urlStr */ function newUrl(urlStr) { try { return new URL(urlStr) } catch (err) { return null } } addEventListener('fetch', e => { const ret = fetchHandler(e) .catch(err => makeRes('cfworker error:\n' + err.stack, 502)) e.respondWith(ret) }) /** * @param {FetchEvent} e */ async function fetchHandler(e) { const getReqHeader = (key) => e.request.headers.get(key); let url = new URL(e.request.url); if (url.pathname === '/') { // Fetch and return the home page HTML content with replacement let response = await fetch(home_page_url); let text = await response.text(); text = text.replace(/{workers_host}/g, workers_host); return new Response(text, { status: response.status, headers: response.headers }); } if (url.pathname === '/token') { let token_parameter = { headers: { 'Host': 'auth.docker.io', 'User-Agent': getReqHeader("User-Agent"), 'Accept': getReqHeader("Accept"), 'Accept-Language': getReqHeader("Accept-Language"), 'Accept-Encoding': getReqHeader("Accept-Encoding"), 'Connection': 'keep-alive', 'Cache-Control': 'max-age=0' } }; let token_url = auth_url + url.pathname + url.search return fetch(new Request(token_url, e.request), token_parameter) } url.hostname = hub_host; let parameter = { headers: { 'Host': hub_host, 'User-Agent': getReqHeader("User-Agent"), 'Accept': getReqHeader("Accept"), 'Accept-Language': getReqHeader("Accept-Language"), 'Accept-Encoding': getReqHeader("Accept-Encoding"), 'Connection': 'keep-alive', 'Cache-Control': 'max-age=0' }, cacheTtl: 3600 }; if (e.request.headers.has("Authorization")) { parameter.headers.Authorization = getReqHeader("Authorization"); } let original_response = await fetch(new Request(url, e.request), parameter) let original_response_clone = original_response.clone(); let original_text = original_response_clone.body; let response_headers = original_response.headers; let new_response_headers = new Headers(response_headers); let status = original_response.status; if (new_response_headers.get("Www-Authenticate")) { let auth = new_response_headers.get("Www-Authenticate"); let re = new RegExp(auth_url, 'g'); new_response_headers.set("Www-Authenticate", response_headers.get("Www-Authenticate").replace(re, workers_url)); } if (new_response_headers.get("Location")) { return httpHandler(e.request, new_response_headers.get("Location")) } let response = new Response(original_text, { status, headers: new_response_headers }) return response; } /** * @param {Request} req * @param {string} pathname */ function httpHandler(req, pathname) { const reqHdrRaw = req.headers // preflight if (req.method === 'OPTIONS' && reqHdrRaw.has('access-control-request-headers') ) { return new Response(null, PREFLIGHT_INIT) } let rawLen = '' const reqHdrNew = new Headers(reqHdrRaw) const refer = reqHdrNew.get('referer') let urlStr = pathname const urlObj = newUrl(urlStr) /** @type {RequestInit} */ const reqInit = { method: req.method, headers: reqHdrNew, redirect: 'follow', body: req.body } return proxy(urlObj, reqInit, rawLen, 0) } /** * * @param {URL} urlObj * @param {RequestInit} reqInit */ async function proxy(urlObj, reqInit, rawLen) { const res = await fetch(urlObj.href, reqInit) const resHdrOld = res.headers const resHdrNew = new Headers(resHdrOld) // verify if (rawLen) { const newLen = resHdrOld.get('content-length') || '' const badLen = (rawLen !== newLen) if (badLen) { return makeRes(res.body, 400, { '--error': `bad len: ${newLen}, except: ${rawLen}`, 'access-control-expose-headers': '--error', }) } } const status = res.status resHdrNew.set('access-control-expose-headers', '*') resHdrNew.set('access-control-allow-origin', '*') resHdrNew.set('Cache-Control', 'max-age=1500') resHdrNew.delete('content-security-policy') resHdrNew.delete('content-security-policy-report-only') resHdrNew.delete('clear-site-data') return new Response(res.body, { status, headers: resHdrNew }) }部署完成后,点击设置->触发器->添加自定义域,绑定自己的域名即可。配置完成后,在 /etc/docker/daemon.json 中修改成你的域名,具体参考上方配置,然后重启 docker。原文地址:https://blog.csun.site/posts/b3f62daf.html
2024年07月30日
71 阅读
0 评论
0 点赞
2023-03-06
Java 给 OkHttpClient 添加 Socks 代理
OkHttpClient 我使用的并不多,这里记录下怎么给 OkHttpClient 添加代理,首先还是添加 OkHttpClient 依赖<dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.10.0</version> </dependency>API接入package demo; import okhttp3.OkHttpClient; import okhttp3.Request; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Proxy; class ApiProxyJava { public static void main(String[] args) throws IOException { testHttpWithOkHttp(); testSocks5WithOkHttp(); } public static void testHttpWithOkHttp() throws IOException { String url = "https://www.google.com/"; Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(IP, PORT)); OkHttpClient client = new OkHttpClient().newBuilder().proxy(proxy).build(); Request request = new Request.Builder().url(url).build(); okhttp3.Response response = client.newCall(request).execute(); String responseString = response.body().string(); System.out.println(responseString); response.close(); } public static void testSocks5WithOkHttp() throws IOException { String url = "https://www.google.com/"; Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(IP, PORT)); OkHttpClient client = new OkHttpClient().newBuilder().proxy(proxy).build(); Request request = new Request.Builder().url(url).build(); okhttp3.Response response = client.newCall(request).execute(); String responseString = response.body().string(); System.out.println(responseString); response.close(); }账密接入package demo; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import java.io.IOException; import java.net.InetSocketAddress; import java.net.PasswordAuthentication; import java.net.Proxy; /** * compile 'com.squareup.okhttp3:okhttp:3.10.0' */ class AutProxyJava { public static void main(String[] args) throws IOException { testWithOkHttp(); testSocks5WithOkHttp(); } public static void testWithOkHttp() throws IOException { String url = "https://www.google.com/"; String gateIp = "" int gatePort = 1000 Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(gateIp, gatePort)); OkHttpClient client = new OkHttpClient().newBuilder().proxy(proxy).proxyAuthenticator((route, response) -> { String credential = Credentials.basic("账户", "密码"); return response.request().newBuilder() .header("Proxy-Authorization", credential) .build(); }).build(); Request request = new Request.Builder().url(url).build(); okhttp3.Response response = client.newCall(request).execute(); String responseString = response.body().string(); System.out.println(responseString); response.close(); } public static void testSocks5WithOkHttp() throws IOException { String url = "https://www.google.com/"; String gateIp = "" int gatePort = 2000 Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(gateIp, gatePort)); java.net.Authenticator.setDefault(new java.net.Authenticator() { private PasswordAuthentication authentication = new PasswordAuthentication("账户", "密码".toCharArray()); @Override protected PasswordAuthentication getPasswordAuthentication() { return authentication; } }); OkHttpClient client = new OkHttpClient().newBuilder().proxy(proxy).build(); Request request = new Request.Builder().url(url).build(); okhttp3.Response response = client.newCall(request).execute(); String responseString = response.body().string(); System.out.println(responseString); response.close(); } }原文地址:https://admin.rola-ip.co/v_manual.html#/api_java
2023年03月06日
637 阅读
0 评论
1 点赞
2022-12-01
Java 开发之 BigDecimal 用法细节详解
一、BigDecimal 概述 Java 在 java.math 包中提供的 API 类 BigDecimal,用来对超过 16 位有效位的数进行精确的运算。双精度浮点型变量 double 可以处理 16 位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理。一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用 Float 和 Double 处理,但是 Double.valueOf(String) 和 Float.valueOf(String) 会丢失精度。所以开发中,如果我们需要精确计算的结果,则必须使用 BigDecimal 类来操作。BigDecimal所创建的是对象,故我们不能使用传统的 +、-、*、/ 等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是 BigDecimal 的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。二、BigDecimal 常用构造函数2.1、常用构造函数// 创建一个具有参数所指定整数值的对象 BigDecimal(int) // 创建一个具有参数所指定双精度值的对象 BigDecimal(double) // 创建一个具有参数所指定长整数值的对象 BigDecimal(long) // 创建一个具有参数所指定以字符串表示的数值的对象 BigDecimal(String)2.2、使用问题分析使用示例:BigDecimal a =new BigDecimal(0.1); System.out.println("a values is:"+a); System.out.println("====================="); BigDecimal b =new BigDecimal("0.1"); System.out.println("b values is:"+b);结果示例:a values is:0.1000000000000000055511151231257827021181583404541015625 ===================== b values is:0.1原因分析:1)参数类型为 double 的构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 newBigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。2)String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言, 通常建议优先使用String构造方法。3)当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 构造方法,将 double 转换为 String。要获取该结果,请使用 static valueOf(double) 方法。三、BigDecimal 常用方法详解3.1、常用方法// BigDecimal 对象中的值相加,返回 BigDecimal 对象 add(BigDecimal) // BigDecimal 对象中的值相减,返回 BigDecimal 对象 subtract(BigDecimal) // BigDecimal 对象中的值相乘,返回 BigDecimal 对象 multiply(BigDecimal) // BigDecimal 对象中的值相除,返回 BigDecimal 对象 divide(BigDecimal) // 将 BigDecimal 对象中的值转换成字符串 toString() // 将 BigDecimal 对象中的值转换成双精度数 doubleValue() // 将 BigDecimal 对象中的值转换成单精度数 floatValue() // 将 BigDecimal 对象中的值转换成长整数 longValue() // 将 BigDecimal 对象中的值转换成整数 intValue()3.2、BigDecimal大小比较java 中对 BigDecimal 比较大小一般用的是 bigdemical 的 compareTo 方法int a = bigdemical.compareTo(bigdemical2)返回结果分析:a = -1,表示bigdemical小于bigdemical2; a = 0,表示bigdemical等于bigdemical2; a = 1,表示bigdemical大于bigdemical2;举例:a大于等于bnew bigdemica(a).compareTo(new bigdemical(b)) >= 0四、BigDecimal 格式化由于 NumberFormat 类的 format() 方法可以使用 BigDecimal 对象作为其参数,可以利用 BigDecimal 对超出 16 位有效数字的货币值,百分值,以及一般数值进行格式化控制。以利用 BigDecimal 对货币和百分比格式化为例。首先,创建 BigDecimal 对象,进行 BigDecimal 的算术运算后,分别建立对货币和百分比格式化的引用,最后利用 BigDecimal 对象作为 format() 方法的参数,输出其格式化的货币值和百分比。NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用 NumberFormat percent = NumberFormat.getPercentInstance(); //建立百分比格式化引用 percent.setMaximumFractionDigits(3); //百分比小数点最多3位 BigDecimal loanAmount = new BigDecimal("15000.48"); //贷款金额 BigDecimal interestRate = new BigDecimal("0.008"); //利率 BigDecimal interest = loanAmount.multiply(interestRate); //相乘 System.out.println("贷款金额:\t" + currency.format(loanAmount)); System.out.println("利率:\t" + percent.format(interestRate)); System.out.println("利息:\t" + currency.format(interest)); 结果:贷款金额: ¥15,000.48 利率: 0.8% 利息: ¥120.00BigDecimal 格式化保留两位小数,不足则补 0:public class NumberFormat { public static void main(String[] s){ System.out.println(formatToNumber(new BigDecimal("3.435"))); System.out.println(formatToNumber(new BigDecimal(0))); System.out.println(formatToNumber(new BigDecimal("0.00"))); System.out.println(formatToNumber(new BigDecimal("0.001"))); System.out.println(formatToNumber(new BigDecimal("0.006"))); System.out.println(formatToNumber(new BigDecimal("0.206"))); } /** * @desc 1.0~1之间的BigDecimal小数,格式化后失去前面的0,则前面直接加上0。 * 2.传入的参数等于0,则直接返回字符串"0.00" * 3.大于1的小数,直接格式化返回字符串 * @param obj传入的小数 * @return */ public static String formatToNumber(BigDecimal obj) { DecimalFormat df = new DecimalFormat("#.00"); if(obj.compareTo(BigDecimal.ZERO)==0) { return "0.00"; }else if(obj.compareTo(BigDecimal.ZERO)>0&&obj.compareTo(new BigDecimal(1))<0){ return "0"+df.format(obj).toString(); }else { return df.format(obj).toString(); } } }结果为:3.44 0.00 0.00 0.00 0.01 0.21五、BigDecimal常见异常5.1、除法的时候出现异常java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result原因分析:通过BigDecimal的divide方法进行除法时当不整除,出现无限循环小数时,就会抛异常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.解决方法:divide 方法设置精确的小数点,如:divide(xxxxx,2)六、BigDecimal总结6.1、总结在需要精确的小数计算时再使用 BigDecimal,BigDecimal 的性能比 double 和 float 差,在处理庞大,复杂的运算时尤为明显。故一般精度的计算没必要使用 BigDecimal。尽量使用参数类型为 String 的构造函数。BigDecimal 都是不可变的(immutable)的, 在进行每一次四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得要保存操作后的值。6.2、工具类推荐package com.vivo.ars.util; import java.math.BigDecimal; /** * 用于高精确处理常用的数学运算 */ public class ArithmeticUtils { //默认除法运算精度 private static final int DEF_DIV_SCALE = 10; /** * 提供精确的加法运算 * * @param v1 被加数 * @param v2 加数 * @return 两个参数的和 */ public static double add(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.add(b2).doubleValue(); } /** * 提供精确的加法运算 * * @param v1 被加数 * @param v2 加数 * @return 两个参数的和 */ public static BigDecimal add(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.add(b2); } /** * 提供精确的加法运算 * * @param v1 被加数 * @param v2 加数 * @param scale 保留scale 位小数 * @return 两个参数的和 */ public static String add(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 提供精确的减法运算 * * @param v1 被减数 * @param v2 减数 * @return 两个参数的差 */ public static double sub(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.subtract(b2).doubleValue(); } /** * 提供精确的减法运算。 * * @param v1 被减数 * @param v2 减数 * @return 两个参数的差 */ public static BigDecimal sub(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.subtract(b2); } /** * 提供精确的减法运算 * * @param v1 被减数 * @param v2 减数 * @param scale 保留scale 位小数 * @return 两个参数的差 */ public static String sub(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 提供精确的乘法运算 * * @param v1 被乘数 * @param v2 乘数 * @return 两个参数的积 */ public static double mul(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.multiply(b2).doubleValue(); } /** * 提供精确的乘法运算 * * @param v1 被乘数 * @param v2 乘数 * @return 两个参数的积 */ public static BigDecimal mul(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.multiply(b2); } /** * 提供精确的乘法运算 * * @param v1 被乘数 * @param v2 乘数 * @param scale 保留scale 位小数 * @return 两个参数的积 */ public static double mul(double v1, double v2, int scale) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return round(b1.multiply(b2).doubleValue(), scale); } /** * 提供精确的乘法运算 * * @param v1 被乘数 * @param v2 乘数 * @param scale 保留scale 位小数 * @return 两个参数的积 */ public static String mul(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 * 小数点以后10位,以后的数字四舍五入 * * @param v1 被除数 * @param v2 除数 * @return 两个参数的商 */ public static double div(double v1, double v2) { return div(v1, v2, DEF_DIV_SCALE); } /** * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 * 定精度,以后的数字四舍五入 * * @param v1 被除数 * @param v2 除数 * @param scale 表示表示需要精确到小数点以后几位。 * @return 两个参数的商 */ public static double div(double v1, double v2, int scale) { if (scale < 0) { throw new IllegalArgumentException("The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 * 定精度,以后的数字四舍五入 * * @param v1 被除数 * @param v2 除数 * @param scale 表示需要精确到小数点以后几位 * @return 两个参数的商 */ public static String div(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException("The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v1); return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 提供精确的小数位四舍五入处理 * * @param v 需要四舍五入的数字 * @param scale 小数点后保留几位 * @return 四舍五入后的结果 */ public static double round(double v, int scale) { if (scale < 0) { throw new IllegalArgumentException("The scale must be a positive integer or zero"); } BigDecimal b = new BigDecimal(Double.toString(v)); return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 提供精确的小数位四舍五入处理 * * @param v 需要四舍五入的数字 * @param scale 小数点后保留几位 * @return 四舍五入后的结果 */ public static String round(String v, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b = new BigDecimal(v); return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 取余数 * * @param v1 被除数 * @param v2 除数 * @param scale 小数点后保留几位 * @return 余数 */ public static String remainder(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 取余数 BigDecimal * * @param v1 被除数 * @param v2 除数 * @param scale 小数点后保留几位 * @return 余数 */ public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP); } /** * 比较大小 * * @param v1 被比较数 * @param v2 比较数 * @return 如果v1 大于v2 则 返回true 否则false */ public static boolean compare(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); int bj = b1.compareTo(b2); boolean res; if (bj > 0) res = true; else res = false; return res; } }原文地址:https://www.cnblogs.com/zhangyinhua/p/11545305.html
2022年12月01日
417 阅读
1 评论
1 点赞
2022-07-15
Spring boot 使用 logback 自定义日志脱敏教程
前言在我们书写代码的时候,会书写许多日志代码,但是有些敏感数据是需要进行安全脱敏处理的。对于日志脱敏的方式有很多,常见的有① 使用 conversionRule 标签,继承 MessageConverter② 书写一个脱敏工具类,在打印日志的时候对特定特字段进行脱敏返回两种方式各有优缺点:第一种方式需要修改代码,不符合开闭原则。第二种方式,需要在日志方法的参数进行脱敏,对原生日志有入侵行为。自定义脱敏组件(slf4j + logback)一个项目在书写了很多打印日志的代码,但是后面有了脱敏需求,如果我们去手动改动代码,会花费大量时间。如果引入本组件,完成配置即可轻松完成脱敏。(仅需三步可轻松配置)一、自定义脱敏组件 - 脱敏效果演示二、自定义脱敏组件 - 使用方式1、引入Jar包依赖前提是你将Jar包打入本地仓库,Jar包地址见后文。<dependency> <groupId>pers.liuchengyin</groupId> <artifactId>logback-desensitization</artifactId> <version>1.0.0</version> </dependency>2、替换日志文件配置类(logback.xml)日志打印方式都只需要替换成脱敏的类即可,如果你的业务不需要,则无需替换。① ConsoleAppender - 控制台脱敏// 原类 ch.qos.logback.core.ConsoleAppender // 替换类 pers.liuchengyin.logbackadvice.LcyConsoleAppender② RollingFileAppender - 滚动文件// 原类 ch.qos.logback.core.rolling.RollingFileAppender // 替换类 pers.liuchengyin.logbackadvice.LcyRollingFileAppender③ FileAppender - 文件// 原类 ch.qos.logback.core.FileAppender // 替换类 pers.liuchengyin.logbackadvice.LcyFileAppender替换示例:<property name="CONSOLE_LOG_PATTERN" value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/> <!-- ConsoleAppender 控制台输出日志 --> <appender name="CONSOLE" class="pers.liuchengyin.logbackadvice.LcyConsoleAppender"> <encoder> <pattern> ${CONSOLE_LOG_PATTERN} </pattern> </encoder> </appender>3、添加脱敏配置文件(logback-desensitize.yml)该配置文件应该放在 resources 文件下三、自定义脱敏组件 - 脱敏规范1、支持数据类型八大基本类型及其包装类型、Map、List、业务里的Pojo对象、List<业务里的Pojo对象>、JSON字符串。注:在配置文件中配置的时候,只需要配置对象里的属性值就行。2、不支持的数据类型List<八大基本类型及包装类型>,因为不知道脱敏的数据源具体是哪一个。3、匹配规则key + 分割符 + value,目前仅支持冒号 (:) 和等号 (=),示例如下:log.info("your email:{}, your phone:{}", "123456789@qq.com","15310763497"); log.info("your email={}, your cellphone={}", "123456789@qq.com","15310763497");key:定义了对应需要脱敏的关键字,如上诉的 email、phone 等以及业务对象中的字段、Map 中的 Key、JSON 中的 Keyvalue:需要脱敏的值,如上诉的 123456789@qq.com、153107634974、日志规范建议书写日志的时候尽量规范,对于key为中文的是没有办法脱敏的,规范程度可以见脱敏效果演示里的代码。四、logback-desensitize.yml配置说明# 日志脱敏 log-desensitize: # 是否忽略大小写匹配,默认为true ignore: true # 是否开启脱敏,默认为false open: true # pattern下的key/value为固定脱敏规则 pattern: # 邮箱 - @前第4-7位脱敏 email: "@>(4,7)" # qq邮箱 - @后1-3位脱敏 qqemail: "@<(1,3)" # 姓名 - 姓脱敏,如*杰伦 name: 1,1 # 密码 - 所有需要完全脱敏的都可以使用内置的password password: password patterns: # 身份证号,key后面的字段都可以匹配以下规则(用逗号分隔) - key: identity,idcard # 定义规则的标识 custom: # defaultRegex表示使用组件内置的规则:identity表示身份证号 - 内置的18/15位 - defaultRegex: identity position: 9,13 # 内置的other表示如果其他规则都无法匹配到,则按该规则处理 - defaultRegex: other position: 9,10 # 电话号码,key后面的字段都可以匹配以下规则(用逗号分隔) - key: phone,cellphone,mobile custom: # 手机号 - 内置的11位手机匹配规则 - defaultRegex: phone position: 4,7 # 自定义正则匹配表达式:座机号(带区号,号码七位|八位) - customRegex: "^0[0-9]{2,3}-[0-9]{7,8}" # -后面的1-4位脱敏 position: "-<(1,4)" # 自定义正则匹配表达式:座机号(不带区号) - customRegex: "^[0-9]{7,8}" position: 3,5 # 内置的other表示如果其他规则都无法匹配到,则按该规则处理 - defaultRegex: other position: 1,3 # 这种方式不太推荐 - 一旦匹配不上,就不会脱敏 - key: localMobile custom: customRegex: "^0[0-9]{2,3}-[0-9]{7,8}" position: 1,3上面这个配置是相对完整的,一定要严格遵守层级配置格式。自定义脱敏支持的方式1、key:value的方式phone:4,7,表示 phone 属性的 4-7 位进行脱敏原始数据:13610357861脱敏后:136**78612、以符号作为起始、结束节点作为脱敏标志email:"@>(4,7)",@ 为脱敏标志,> 表示其为结束节点,< 表示其为开始节点。即 @> 表示对 @ 之前的进行脱敏,@< 表示对 @ 之后的进行脱敏。这个示例就是 @ 前的数据的第 4-7 位进行脱敏。注意:这种规则里的双引号、括号不能省略,其次 : 和 = 不能作为标志符号,因为和匹配规则有冲突。原始数据:123456789@qq.com"@>(4,7)"脱敏后:123**89@qq.com"@<(1,3)"脱敏后:123456789@*com3、自定义正则脱敏patterns: # 手机号 - key: phone,mobile custom: # 手机号的正则 - customRegex: "^1[0-9]{10}" # 脱敏范围 position: 4,7customRegex:正则表达式,如果符合该表达式,则使用其对应的脱敏规则 (position)4、一个字段,根据多种值含义进行自定义脱敏比如说,username 字段的值可以是手机号、也可以是邮箱,这个值动态改变的,前面几种方式都没办法解决,可以使用该方式。patterns: - key: username custom: # 手机号 - 11位 - defaultRegex: phone position : 4,7 # 邮箱 - @ - defaultRegex: email position : "@>(3,12)" # 身份证 - 15/18位 - defaultRegex: identity position : 1,3 # 自定义正则 - customRegex: "^1[0-9]{10}" position : 1,3 # 都匹配不到时,按照这种规则来 - defaultRegex: other position : 1,3注意:上面示例中匹配规则里的 双引号和括号 都不能省略该组件内置四种匹配规则:手机号、身份证号、邮箱、other(其他匹配不到时用的),内置一种脱敏方式:password,表示完全脱敏,可用于 pattren 下的。注:当pattern和patterns下的key有重复的时候,只会使用pattern下指定的方式进行脱敏。Jar包地址和源码地址Jar包Github地址 - logback-desensitization-1.0.0.jar Github地址: Logback和slf4j的日志脱敏组件Demo Gitee地址: Logback和slf4j的日志脱敏组件DemoJar包打入Maven本地仓库的方式1、下载Jar包,放在一个文件夹里2、在这个文件夹里打开cmd(打开cmd,进入到这个文件夹)3、执行命令(前提保证maven配置正常,使用mvn -v命令查看是否正常,如果显示版本号表示正常)mvn install:install-file -DgroupId=pers.liuchengyin -DartifactId=logback-desensitization -Dversion=1.0.0 -Dpackaging=jar -Dfile=logback-desensitization-1.0.0.jar命令说明: -DgroupId 表示jar对应的groupId <groupId>pers.liuchengyin</groupId> -DartifactId: 表示jar对应的artifactId <artifactId>logback-desensitization</artifactId> -Dversion 表示jar对应的 version <version>1.0.0</version>原文作者: 九月清晨柳成荫 原文地址:https://blog.csdn.net/qq_40885085/article/details/113385261?spm=1001.2014.3001.5501
2022年07月15日
1,005 阅读
0 评论
1 点赞
2021-08-16
Objects.equals(a,b) 、 a.equals(b) 、== 判断对象相等的区别
一、值是null的情况1、a.equals(b), a 是 null, 抛出 NullPointException 异常。2、a.equals(b), a不是 null, b是null, 返回 false3、Objects.equals(a, b) 比较时, 若 a 和 b 都是 null, 则返回 true, 如果 a 和 b 其中一个是 null, 另一个不是 null, 则返回 false。注意:不会抛出空指针异常。null.equals("abc") → 抛出 NullPointerException 异常 "abc".equals(null) → 返回 false null.equals(null) → 抛出 NullPointerException 异常Objects.equals(null, "abc") → 返回 false Objects.equals("abc",null) → 返回 false Objects.equals(null, null) → 返回 true二、值是空字符串的情况1、a 和 b 如果都是空值字符串:"", 则 a.equals(b), 返回的值是 true, 如果 a 和 b 其中有一个不是空值字符串,则返回 false; 2、这种情况下 Objects.equals 与情况 1 行为一致。"abc".equals("") → 返回 false "".equals("abc") → 返回 false "".equals("") → 返回 trueObjects.equals("abc", "") → 返回 false Objects.equals("","abc") → 返回 false Objects.equals("","") → 返回 true三、源码分析1.源码* @since 1.7 */ public final class Objects { private Objects() { throw new AssertionError("No java.util.Objects instances for you!"); } /** * Returns {@code true} if the arguments are equal to each other * and {@code false} otherwise. * Consequently, if both arguments are {@code null}, {@code true} * is returned and if exactly one argument is {@code null}, {@code * false} is returned. Otherwise, equality is determined by using * the {@link Object#equals equals} method of the first * argument. * * @param a an object * @param b an object to be compared with {@code a} for equality * @return {@code true} if the arguments are equal to each other * and {@code false} otherwise * @see Object#equals(Object) */ public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); }2、说明1) 进行了对象地址的判断,如果是真,则不再继续判断。2) 如果不相等,后面的表达式的意思是,先判断 a 不为空,然后根据上面的知识点,就不会再出现空指针。3) 如果都是 null,在第一个判断上就为 true 了。如果不为空,地址不同,就重要的是判断 a.equals(b)。四、"a==b" 和 "a.equals(b)" 有什么区别?如果 a 和 b 都是对象,则 a==b 是比较两个对象的引用,只有当 a 和 b 指向的是堆中的同一个对象才会返回 true。而 a.equals(b) 是进行逻辑比较,当内容相同时,返回 true,所以通常需要重写该方法来提供逻辑一致性的比较。转载至:https://www.cnblogs.com/juncaoit/p/12422752.html
2021年08月16日
390 阅读
0 评论
7 点赞
2021-06-25
在线工具推荐:10个解放双手的实用在线工具
1、Aliyun Java Initializr在日常开发工作和自己学习跑 demo 的时候,往往都需要快速构建一个 springboot 基础工程。除了用 IDEA 开发工具构建,更多就是用 Spring Initializr 来生成,但用的时间长了发现,它也就仅仅只能帮我们引入一些必要的 jar 包,其他插件轮子还是得自己配置。Aliyun Java Initializr 覆盖了 Spring Initializr 的所有功能,又在其基础上增加了很多实用功能,它几乎可以集成当下所有主流技术,只要勾选相应的模块就可以自动集成进来。可不是简单的引入 jar 包,而是帮你把工程目录,相关配置文件和基础 java 代码全部生成了,省了很多环境搭建的时间。地址:https://start.aliyun.com/bootstrap.html2、magicalcodermagicalcoder 是一个自动生成代码的工具,JAVA 代码自动生成,还有 H5 样式布局器。对于有写页面需求的后端开发来说是个神器,可以拖拽控件布局直接生成 css、js,支持 element、layui、bootstrap4 这些常用的前端框架。地址:http://bbs.magicalcoder.com/3、菜鸟工具这是一个非常强大的工具类站点,我个人常用得就是 JSON 格式化工和 python 在线工具,虽然主做 Java 但平时还是会写一些 python 脚本,在线运行随时随地写很方便。地址:http://c.runoob.com/4、BEJSONBEJSON站内提供了很多实用的小工具。像我最近正在开发一个调度任务的配置平台,免不了要写 Cron 表达式,但搞过定时任务的都知道 Cron 表达式可不太好记,而且用的频率相对不高,花太多时间在死记硬背上就有点不值当。这个在线工具提供了图形化界面,只要我们输入想要定时执行的周期,就可以自动生成Cron表达式。地址:https://www.bejson.com/othertools/cron5、在线正则表达式正则表达式的写法既复杂又麻烦,而且通常还需要经过大量的测试和验证才能被采用。这个在线工具提供了几乎所有常见的正则校验规则。地址:http://tool.chinaz.com/tools/regexgenerate6、在线工具这个也是一个在线工具站点,但是他的功能相对就比较杂了地址:https://tool.lu/7、文图工作中时不时就会有运营人员或者老板直接甩个任务,给我统计一下某某销量的指标,用 SQL 查询很简单,但给外行人看这类数据,还要是更直观一点,反正都是干活为啥不完成的出彩一点。文图可以根据 excel 里的数据,自动生成对应的统计图形,样式可以自由切换,生成 PDF、JPG 等格式。地址:https://www.wentu.io/8、在线比对Diffchecker是一个使用很不错代码差异对比工具,使过 svn 或者 git 的人对 diffcheck 肯定不陌生,而且他支持比对的类型比较丰富,excel、pdf 支持。地址:https://www.diffchecker.com/9、nginx在线格式化总修改 nginx 配置,这个可以治愈你的强迫症地址:http://www.html580.com/tool/nginx/index.php10、在线生成代码图片代码生成图片,这个不写博客的小伙伴可能不太常用,像我会时常分享一些技术类的文章,里边难免会贴出代码块,出于观看方便和样式美观就会转成图片。地址:https://www.dute.org/code-snapshot总结上边这些是我常用到的几个在线工具,其实远不止这些,虽然说不上神器,但确实实在在是提升了我的工作效率,希望也能对你有点帮助。转载至:https://segmentfault.com/a/1190000039362219
2021年06月25日
593 阅读
0 评论
0 点赞
2021-03-08
Centos 7 彻底卸载清除 Docker 环境
CentOS 7 彻底卸载 Docker 环境流程如下杀死所有运行容器docker kill $(docker ps -a -q)删除所有Docker容器docker rm $(docker ps -a -q)删除所有Docker镜像docker rmi $(docker images -q)停止 docker 服务systemctl stop docker删除存储目录rm -rf /etc/docker rm -rf /run/docker rm -rf /var/lib/dockershim rm -rf /var/lib/docker如果发现删除不掉,需要先 umount,如umount /var/lib/docker/devicemapper卸载 docker查看已安装的 docker 包yum list installed | grep docker卸载相关包yum remove docker-ce* yum remove containerd.io.x86_64原文地址:https://www.omicsclass.com/article/1184
2021年03月08日
8,728 阅读
0 评论
10 点赞
2021-03-08
彻底卸载阿里云服务器上的阿里云盾,以及其他监控服务
用过阿里云服务器(ECS、轻量应用服务器)的人应该都知道,阿里云提供的系统镜像里预装了它的各种监控服务。如:阿里云盾(安骑士)、云监控(Cloudmonitor) 这些监控服务的存在使得人们可以直接在控制台看到当前系统的状态,某种程度上来说确实方便了服务器的管理。但是对于我们这种“爱折腾党”,基本没有用到这些服务的时候,而且天天被这些监控盯着还总有种被偷窥的感觉。卸载云盾(安骑士)在 官方文档 页面只介绍了怎么在控制台去卸载,手动卸载的操作步骤需要提交工单获取,不是很方便。在这里介绍一种用脚本手动卸载的方法(其实用的也是官方提供的两个脚本),执行以下命令即可。wget http://update.aegis.aliyun.com/download/uninstall.sh && chmod +x uninstall.sh &&./uninstall.sh wget http://update.aegis.aliyun.com/download/quartz_uninstall.sh && chmod +x quartz_uninstall.sh && ./quartz_uninstall.sh测试发现上面的脚本运行完之后可能还会有一些服务/文件/文件夹残留,所以我们手动清理一下,顺便把刚刚下下来的两个脚本文件也删了(如果提示文件不存在就不用管了)。sudo rm -r /usr/local/aegis sudo systemctl disable aliyun.service sudo rm /usr/sbin/aliyun-service sudo rm /usr/sbin/aliyun-service.backup sudo rm /usr/sbin/aliyun_installer sudo rm /etc/systemd/system/aliyun.service sudo rm /lib/systemd/system/aliyun.service rm uninstall.sh quartz_uninstall.sh卸载云监控(Cloudmonitor)云监控有 Java 版本和 Go 语言版本两种(Java 版本已经不再提供升级了,现在新开的服务器应该都是 Go 语言版本的),可以根据其安装目录 /usr/local/cloudmonitor/ 下的文件名判断你的服务器上装的具体是哪一种。下面分别介绍两个版本的卸载方法。卸载云监控 Go 语言版云监控 Go 语言版的可执行文件名为 CmsGoAgent.linux-${ARCH} ,其中的 ARCH 根据 Linux 架构的不同,分为 amd64 和 386 ,可以在其安装目录 /usr/local/cloudmonitor/ 下找到具体的文件名。为了方便表述,下面的命令中也使用 ${ARCH} 替代文件名中的系统架构部分。要能够直接复制下面的命令行去执行的话,可以先设置一下临时环境变量(相当于后面输入命令中的 ${ARCH} 会被自动替换成我们在这里设置的值)# 64 位系统 export ARCH=amd64 # 32 位系统 export ARCH=386或者的话你也可以在找到具体的文件名后自行替换。下面是卸载相关的一些命令:# 从系统服务中移除 /usr/local/cloudmonitor/CmsGoAgent.linux-${ARCH} uninstall # 停止 /usr/local/cloudmonitor/CmsGoAgent.linux-${ARCH} stop # 卸载 /usr/local/cloudmonitor/CmsGoAgent.linux-${ARCH} stop && \ /usr/local/cloudmonitor/CmsGoAgent.linux-${ARCH} uninstall && \ rm -rf /usr/local/cloudmonitor附上官方文档页面链接:https://help.aliyun.com/document_detail/97929.html卸载云监控 Java 版# 停止 /usr/local/cloudmonitor/wrapper/bin/cloudmonitor.sh stop # 卸载 /usr/local/cloudmonitor/wrapper/bin/cloudmonitor.sh remove && \ rm -rf /usr/local/cloudmonitor附上官方文档页面链接:https://help.aliyun.com/knowledge_detail/38859.html原文地址:https://xirikm.net/2019/331-1
2021年03月08日
5,788 阅读
2 评论
2 点赞
2021-03-07
全局ID生成器:SpringBoot2.x 集成百度 uidgenerator
因为升级 使用springboot2.x java 11 的关系,根据官方文档和网上其他作者配置的怎么也配置不成功,最后自己一步一步升级引入依赖,修改增加接口注入来源,最后成功。升级成功后的源码地址:https://github.com/foxiswho/java-spring-boot-uid-generator-baidu部分升级说明这里的升级,是升级 官方 代码依赖官方代码地址:https://github.com/baidu/uid-generator升级spring boot 版本: 2.0.7.RELEASE升级 mybatis,mybatis-spring 版本升级 mysql-connector-java 版本:8.0.12升级 junit 版本创建数据库存导入官网数据库SQL https://github.com/baidu/uid-generator/blob/master/src/main/scripts/WORKER_NODE.sql也就是一张表我这里是在 demo 库中,创建了这张表DROP TABLE IF EXISTS WORKER_NODE; CREATE TABLE WORKER_NODE ( ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id', HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name', PORT VARCHAR(64) NOT NULL COMMENT 'port', TYPE INT NOT NULL COMMENT 'node type: ACTUAL or CONTAINER', LAUNCH_DATE DATE NOT NULL COMMENT 'launch date', MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time', CREATED TIMESTAMP NOT NULL COMMENT 'created time', PRIMARY KEY(ID) ) COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB;如果报错,基本上是时间问题,因为mysql 低版本控制比较严格,解决方法有多种方式方式一:直接把TIMESTAMP改成DATETIME 即可方式二:执行SQL 语句前先执行:set sql_mode="NO_ENGINE_SUBSTITUTION";mysql 配置信息更改因为升级到8.x ,配置文件部分也要跟着修改 uid-generator 下,测试文件夹下的资源包 uid/mysql.properties以下修改为mysql.driver=com.mysql.cj.jdbc.Driver修改完成后,配置好数据库相关参数,这样单元测试即可执行成功案例计划将全局生成唯一ID作为一个服务提供者,供其他微服务使用调用这里创建了一个项目,项目中包含两个子项目一个是 uid-generator 官方本身,当然你也可以不需要放到本项目中,直接使用官方的自行打包即可,一个是 uid-provider 服务提供者以下说明的主要是服务提供者创建 子项目 uid-provider如何创建 略POM配置文件如下<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>java-spring-boot-uid-generator-baidu</artifactId> <groupId>com.foxwho.demo</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>uid-provider</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!--for Mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> <version>8.0.12</version> </dependency> <!-- druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.16</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <optional>true</optional> </dependency> <dependency> <groupId>com.foxwho.demo</groupId> <artifactId>uid-generator</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>复制 mapper先在 uid-provider 项目资源包路径下创建 mapper 文件夹,然后到官方 uid-generator 资源包路径下 META-INF/mybatis/mapper/WORKER_NODE.xml 复制 WORKER_NODE.xml 文件,粘贴到该文件夹 mapper 内cache id 配置文件先在 uid-provider 项目资源包路径下创建 uid 文件夹,然后到官方 uid-generator 测试 [注意:这里是测试资源包] 资源包路径下 uid/cached-uid-spring.xml 复制 cached-uid-spring.xml 文件,粘贴到该文件夹 uid 内最后根据需要 配置参数,可以看官方说明创建 spring boot 启动入口主要就是加上注解 @MapperScan("com.baidu.fsg.uid") 让 mybatis 能扫描到 Mapper 类的包的路径package com.foxwho.demo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication @MapperScan("com.baidu.fsg.uid") public class ConsumerApplication { public static void main(String[] args) { new SpringApplicationBuilder(ConsumerApplication.class).run(args); } }创建配置package com.foxwho.demo.config; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; @Configuration @ImportResource(locations = { "classpath:uid/cached-uid-spring.xml" }) public class UidConfig { }创建服务接口package com.foxwho.demo.service; import com.baidu.fsg.uid.UidGenerator; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class UidGenService { @Resource(name = "cachedUidGenerator") private UidGenerator uidGenerator; public long getUid() { return uidGenerator.getUID(); } }主要说明一下 @Resource(name = "cachedUidGenerator") 以往错误都是少了这里,没有标明注入来源控制器package com.foxwho.demo.controller; import com.foxwho.demo.service.UidGenService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UidController { @Autowired private UidGenService uidGenService; @GetMapping("/uidGenerator") public String UidGenerator() { return String.valueOf(uidGenService.getUid()); } @GetMapping("/") public String index() { return "index"; } }项目配置文件server.port=8080 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver mybatis.mapper-locations=classpath:mapper/*.xml mybatis.configuration.map-underscore-to-camel-case=true启动项目从启动入口,启动,然后访问浏览器http://localhost:8080/uidGenerator页面输出13128615512260612原文地址:https://foxwho.blog.csdn.net/article/details/90200602
2021年03月07日
1,374 阅读
1 评论
3 点赞
2021-03-07
PowerDesigner 连接 MySQL 与生成逆向工程图
最近想梳理公司项目的表间关系,从项目后台管理系统的操作入手,以及代码的hibernate注解入手,都不算特别尽人意,于是最后还是鼓捣了一下PowerDesigner的逆向工程图,这样更直观一些。想着以后不论项目切换或者接手的时候肯定是用得上的,所以在这里也记录一下,毕竟,好记性不如烂笔头,更何况我这还不是好记性。看网上有个哥们说他已经是三次忘了步骤了,所以我吸取教训赶紧第一次就记录下来。1、MySQL数据库连接(JDBC方式)JDBC的配置方式需要一些基础的环境和准备,但是也很简单,无非也就是JDK和mysql的连接jar包,这里不再展开阐述。1.1 新建一个pdm,dbms选择mysql1.2 Database - Connect 选择数据库连接1.3 配置连接信息数据库连接这里是通过一个配置文件来获取连接信息的,首次的话因为没有,所以我们需要选择Configure进行配置。1.4 填写配置信息如图,选择添加数据库资源,出现如上,相关说明如下:Connection profile name:JDBC配置文件名称,可随意填写Directory:配置文件保存路径Description:配置文件描述,可根据实际用途填写Connection type:连接方式,这里我们选择JDBCDBMS type:数据库类型,提供大部分主流数据库选择,我们选择MySQLUser name:登录数据库的用户名JDBC driver class:指定驱动类,使用默认的com.mysql.jdbc.DriverJDBC connection URL:连接URL,格式 jdbc:mysql://ServerIP/Hostname:port/databaseJDBC driver jar files:指定连接的jar包路径1.5连接测试和配置保存如图填写信息完成后,点击左下角的 Test Connection,出现成功提示则说明连接可行:如果测试连接不通过,且出现 Non SQL Error : Could not load class com.mysql.jdbc.Drive 的错误,而指定的jar包没有问题,那么是因为PowerDesigner无法找到驱动所产生的。解决办法是配置系统的classpath路径,指定jar包路径就好了。成功连接后,我们一路确定下去把这个配置文件进行保存,最终你可以在你指定的文件夹(该目录没有限制,自定义一个目录即可,此处我是建立在安装文件下的一个userConf文件夹内)中看到这个保存好的文件:2、从已有数据库中的表进行逆向工程图2.1 菜单选择,从数据库更新模型2.2 选择数据库连接配置文件2.3 选择涉及的数据库和想要导出的表2.4 大功告成原文链接:https://www.cnblogs.com/deng-cc/p/6824946.html
2021年03月07日
963 阅读
0 评论
0 点赞
2021-02-28
Spring 的 Controller 是单例还是多例?怎么保证并发的安全?
答案如下controller 默认是单例的,不要使用非静态的成员变量,否则会发生数据逻辑混乱。正因为单例所以不是线程安全的。我们下面来简单的验证下:package com.riemann.springbootdemo.controller; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * @author riemann * @date 2019/07/29 22:56 */ @Controller public class ScopeTestController { private int num = 0; @RequestMapping("/testScope") public void testScope() { System.out.println(++num); } @RequestMapping("/testScope2") public void testScope2() { System.out.println(++num); } }我们首先访问 http://localhost:8080/testScope,得到的答案是1;然后我们再访问 http://localhost:8080/testScope2,得到的答案是 2。得到的不同的值,这是线程不安全的。接下来我们再来给 controller 增加作用多例 @Scope("prototype")package com.riemann.springbootdemo.controller; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * @author riemann * @date 2019/07/29 22:56 */ @Controller @Scope("prototype") public class ScopeTestController { private int num = 0; @RequestMapping("/testScope") public void testScope() { System.out.println(++num); } @RequestMapping("/testScope2") public void testScope2() { System.out.println(++num); } }我们依旧首先访问 http://localhost:8080/testScope,得到的答案是1;然后我们再访问 http://localhost:8080/testScope2,得到的答案还是 1。相信大家不难发现 :单例是不安全的,会导致属性重复使用。解决方案1、不要在 controller 中定义成员变量。2、万一必须要定义一个非静态成员变量时候,则通过注解@Scope(“prototype”),将其设置为多例模式。3、在 Controller 中使用 ThreadLocal 变量补充说明spring bean作用域有以下5个:1、singleton:单例模式,当 spring 创建 applicationContex t容器的时候,spring 会欲初始化所有的该作用域实例,加上 lazy-init 就可以避免预处理;2、prototype:原型模式,每次通过 getBean 获取该 bean 就会新产生一个实例,创建后 spring 将不再对其管理;下面是在web项目下才用到的3、request:搞 web 的大家都应该明白 request 的域了吧,就是每次请求都新产生一个实例,和 prototype 不同就是创建后,接下来的管理,spring 依然在监听;4、session:每次会话,同上;5、global session:全局的 web 域,类似于 servlet 中的 application。原文地址:https://blog.csdn.net/riemann_/article/details/97698560
2021年02月28日
952 阅读
0 评论
4 点赞
1
2