阿里小纸条是一个每天更新阿里云盘资源的汇总页
https://ppxzy.me
https://pan666.net/d/240406-top/
https://a.sousou.pro/forum-1-1.htm
不错的阿里云盘资源分享网站
https://nav.xybin.top/#category-27
阿里云盘的一些在线文档收集
]]>Telegram电报+86手机无法接受验证码目前可用Telegram X获取,测试可用。获取验证码的前提是需要确保网络通畅
不要同一时段获取超过太多验证码,获取过多验证码将会很长一段时间收不到验证码,6小时最多获取2次验证码。
方法1:使用官方网页获取验证码(https://web.telegram.org)而非客户端获取验证码, 在上午7点到12点之间获取验证码最佳。
如方法1无效,可使用方法2
方法2:安装安卓Telegram X,可以收到验证码。如果输入手机号后,一直转圈无法跳到输入验证码的界面,该情况属于网络不通畅,在浏览器中查看是否能打开电报网页版。
如网络通畅,尝试 移动数据网络下,连续点击飞行模式切换IP 访问: ip和dns查询 查看IP是否变动,变动后输入手机号正常获取验证码。
iOS手机可以借用朋友手机或自己的安卓设备安装Telegram X,接收到的验证码5分钟内有效,在自己的iOS客户端登录。也可以电脑中使用模拟器安装获取验证码。然后正常登录进去。
获取到的验证码可以在网页版和电报官方客户端中使用。
如果按照上述流程操作仍然无法接收验证码,那么代表 你的手机号被tg限制了接收sms短信 ,发邮件给telegram官方解除sms限制,此方法 同样适用于账号被封禁所导致的无法登录的情况 ,前提是你的账号没有做大量违规操作,邮件寄出后大概1-7天Telegram就可以恢复正常。
用手机或者电脑浏览器打开链接:https://telegram.org/support
这个页面是telegram电报支持页面。
在此页面填写以下内容,并将手机号码修改为自己的。
Please describe your problem:
账号封禁填写
1 | Dear Sir/Ma'am.MY number +86 xxx xxx xxxx been banned and i am not able to figure out the reason for supension,please help me to recover my account. Thank you. |
手机号码限制短信功能填写
1 | Dear administrator, my mobile phone number cannot receive the SMS verification code normally. Can I lift the SMS restriction? my phone number is: +86 xxx xxx xxxx |
Your email:
填写你的邮箱,推荐使用hotmail或gmail,其他邮箱也可以。
Your phone number:
填写你的telegram +86手机号码。
]]>在下面的示例代码中,我们创建了一个流速为 2 个请求 / 秒的限流器,这里的流速该怎么理解呢?直观地看,2 个请求 / 秒指的是每秒最多允许 2 个请求通过限流器,其实在 Guava 中,流速还有更深一层的意思:是一种匀速的概念,2 个请求 / 秒等价于 1 个请求 /500 毫秒。
在向线程池提交任务之前,调用 acquire()
方法就能起到限流的作用。通过示例代码的执行结果,任务提交到线程池的时间间隔基本上稳定在 500 毫秒。
1 | //限流器流速:2个请求/秒 |
Guava 的限流器使用上还是很简单的,那它是如何实现的呢?Guava 采用的是令牌桶算法,其核心是要想通过限流器,必须拿到令牌。也就是说,只要我们能够限制发放令牌的速率,那么就能控制流速了。令牌桶算法的详细描述如下:
这个算法中,限流的速率 r 还是比较容易理解的,但令牌桶的容量 b 该怎么理解呢?b 其实是 burst 的简写,意义是限流器允许的最大突发流量 。比如 b=10,而且令牌桶中的令牌已满,此时限流器允许 10 个请求同时通过限流器,当然只是突发流量而已,这 10 个请求会带走 10 个令牌,所以后续的流量只能按照速率 r 通过限流器。
令牌桶这个算法,如何用 Java 实现呢?很可能你的直觉会告诉你生产者 - 消费者模式:一个生产者线程定时向阻塞队列中添加令牌,而试图通过限流器的线程则作为消费者线程,只有从阻塞队列中获取到令牌,才允许通过限流器。
这个算法看上去非常完美,而且实现起来非常简单,如果并发量不大,这个实现并没有什么问题。可实际情况却是使用限流的场景大部分都是高并发场景,而且系统压力已经临近极限了,此时这个实现就有问题了。问题就出在定时器上,在高并发场景下,当系统压力已经临近极限的时候,定时器的精度误差会非常大,同时定时器本身会创建调度线程,也会对系统的性能产生影响。
那还有什么好的实现方式呢?当然有,Guava 的实现就没有使用定时器,下面我们就来看看它是如何实现的。
Guava 实现令牌桶算法,用了一个很简单的办法,其关键是记录并动态计算下一令牌发放的时间 。
下面我们以一个最简单的场景来介绍该算法的执行过程。假设令牌桶的容量为 b=1,限流速率 r = 1 个请求 / 秒,如下图所示,如果当前令牌桶中没有令牌,下一个令牌的发放时间是在第 3 秒,而在第 2 秒的时候有一个线程 T1 请求令牌,此时该如何处理呢?
线程 T1 请求令牌示意图对于这个请求令牌的线程而言,很显然需要等待 1 秒,因为 1 秒以后(第 3 秒)它就能拿到令牌了。此时需要注意的是,下一个令牌发放的时间也要增加 1 秒,为什么呢?因为第 3 秒发放的令牌已经被线程 T1 预占了。处理之后如下图所示。
线程 T1 请求结束示意图假设 T1 在预占了第 3 秒的令牌之后,马上又有一个线程 T2 请求令牌,如下图所示。
线程 T2 请求结束示意图上面线程 T1、T2 都是在下一令牌产生时间之前请求令牌,如果线程在下一令牌产生时间之后请求令牌会如何呢?假设在线程 T1 请求令牌之后的 5 秒,也就是第 7 秒,线程 T3 请求令牌,如下图所示。
线程 T3 请求令牌示意图由于在第 5 秒已经产生了一个令牌,所以此时线程 T3 可以直接拿到令牌,而无需等待。在第 7 秒,实际上限流器能够产生 3 个令牌,第 5、6、7 秒各产生一个令牌。由于我们假设令牌桶的容量是 1,所以第 6、7 秒产生的令牌就丢弃了,其实等价地你也可以认为是保留的第 7 秒的令牌,丢弃的第 5、6 秒的令牌,也就是说第 7 秒的令牌被线程 T3 占有了,于是下一令牌的的产生时间应该是第 8 秒,如下图所示。
线程 T3 请求结束示意图通过上面简要地分析,你会发现,我们只需要记录一个下一令牌产生的时间,并动态更新它,就能够轻松完成限流功能 。我们可以将上面的这个算法代码化,示例代码如下所示,依然假设令牌桶的容量是 1。关键是 reserve()<span> </span>
方法,这个方法会为请求令牌的线程预分配令牌,同时返回该线程能够获取令牌的时间。其实现逻辑就是上面提到的:如果线程请求令牌的时间在下一令牌产生时间之后,那么该线程立刻就能够获取令牌;反之,如果请求时间在下一令牌产生时间之前,那么该线程是在下一令牌产生的时间获取令牌。由于此时下一令牌已经被该线程预占,所以下一令牌产生的时间需要加上 1 秒。
1 | class SimpleLimiter { |
如果令牌桶的容量大于 1,又该如何处理呢?按照令牌桶算法,令牌要首先从令牌桶中出,所以我们需要按需计算令牌桶中的数量,当有线程请求令牌时,先从令牌桶中出。具体的代码实现如下所示。我们增加了一个 resync()<span> </span>
方法,在这个方法中,如果线程请求令牌的时间在下一令牌产生时间之后,会重新计算令牌桶中的令牌数,新产生的令牌的计算公式是 :(now-next)/interval
,你可对照上面的示意图来理解。reserve() 方法中,则增加了先从令牌桶中出令牌的逻辑,不过需要注意的是,如果令牌是从令牌桶中出的,那么 next 就无需增加一个 interval 了。
1 | class SimpleLimiter { |
经典的限流算法有两个,一个是令牌桶算法 (Token Bucket),另一个是漏桶算法 (Leaky Bucket)。令牌桶算法是定时向令牌桶发送令牌,请求能够从令牌桶中拿到令牌,然后才能通过限流器;
而漏桶算法里,请求就像水一样注入漏桶,漏桶会按照一定的速率自动将水漏掉,只有漏桶里还能注入水的时候,请求才能通过限流器。令牌桶算法和漏桶算法很像一个硬币的正反面,所以你可以参考令牌桶算法的实现来实现漏桶算法。
上面我们介绍了 Guava 是如何实现令牌桶算法的,我们的示例代码是对 Guava RateLimiter 的简化,Guava RateLimiter 扩展了标准的令牌桶算法,比如还能支持预热功能。对于按需加载的缓存来说,预热后缓存能支持 5 万 TPS 的并发,但是在预热前 5 万 TPS 的并发直接就把缓存击垮了,所以如果需要给该缓存限流,限流器也需要支持预热功能,在初始阶段,限制的流速 r 很小,但是动态增长的。预热功能的实现非常复杂,Guava 构建了一个积分函数来解决这个问题,如果你感兴趣,可以继续深入研究。
参考:
]]>
通常接口返回值中的一些敏感数据也是要脱敏的,比如身份证号 、手机号码 、地址 …..通常的手段就是用 *
隐藏一部分数据,当然也可以根据自己需求定制。
言归正传,如何优雅的实现呢?有两种实现方案,如下:
Sharding Sphere
实现数据脱敏,查看之前的文章:基于Sharding Sphere实现数据“一键脱敏”第一种方案网上很多实现方式,下面演示第二种,整合Jackson。
需要自定义一个脱敏注解,一旦有属性被标注,则进行对应得脱敏,如下:
1 | /** |
针对项目需求,定制不同字段的脱敏规则,比如手机号中间几位用 *
替代,如下:
1 | /** |
以上只是提供了部分,具体根据自己项目要求进行配置。
下面将是重要实现,对标注注解 @Sensitive
的字段进行脱敏,实现如下:
1 | /** |
使用注解 @Sensitive
注解进行数据脱敏,代码如下:
1 | @Data |
以上4个步骤完成了数据脱敏的Jackson注解,下面写个controller进行测试,代码如下:
1 | @RestController |
调用接口查看数据有没有正常脱敏,结果如下:
1 | { |
数据脱敏有很多种实现方式,关键是哪种更加适合,哪种更加优雅…..
参考:
]]>
分享一道群友去阿里云面试遇到的 Java 基础面试真题:“String
、StringBuffer
、StringBuilder
的区别?String
为什么是不可变的?”。
网站很多文章都把 String
不可变的原因讲错了,建议你重点关注一下。另外,本文还提到了 :“Java 9 为何要将 String 的底层实现由 char[]
改成了 byte[]
?”
下面是正文。
可变性
简单的来说:String
类中使用 final
关键字修饰字符数组来保存字符串,所以String
对象是不可变的。
1 | public final class String implements java.io.Serializable, Comparable<String>, CharSequence { |
🐛 修正 :我们知道被
final
关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象。因此,final
关键字修饰的数组保存字符串并不是String
不可变的根本原因,因为这个数组保存的字符串是可变的(final
修饰引用类型变量的情况)。
String
真正不可变有下面几点原因:
- 保存字符串的数组被
final
修饰且为私有的,并且String
类没有提供/暴露修改这个字符串的方法。String
类被final
修饰导致其不能被继承,进而避免了子类破坏String
不可变。相关阅读:如何理解 String 类型值的不可变?- 知乎提问^[1]^
补充(来自issue 675^[2]^ ):在 Java 9 之后,
String
、StringBuilder
与StringBuffer
的实现改用byte
数组存储字符串。Java 9 为何要将
String
的底层实现由char[]
改成了byte[]
?新版的 String 其实支持两个编码方案:Latin-1 和 UTF-16。如果字符串中包含的汉字没有超过 Latin-1 可表示范围内的字符,那就会使用 Latin-1 作为编码方案。Latin-1 编码方案下,
byte
占一个字节(8 位),char
占用 2 个字节(16),byte
相较char
节省一半的内存空间。如果字符串中包含的汉字超过 Latin-1 可表示范围内的字符,
byte
和char
所占用的空间是一样的。这是官方的介绍:https://openjdk.java.net/jeps/254 。
StringBuilder
与 StringBuffer
都继承自 AbstractStringBuilder
类,在 AbstractStringBuilder
中也是使用字符数组保存字符串,不过没有使用 final
和 private
关键字修饰,最关键的是这个 AbstractStringBuilder
类还提供了很多修改字符串的方法比如 append
方法。
1 | abstract class AbstractStringBuilder implements Appendable, CharSequence { |
线程安全性
String
中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder
是 StringBuilder
与 StringBuffer
的公共父类,定义了一些字符串的基本操作,如 expandCapacity
、append
、insert
、indexOf
等公共方法。StringBuffer
对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder
并没有对方法进行加同步锁,所以是非线程安全的。
性能
每次对 String
类型进行改变的时候,都会生成一个新的 String
对象,然后将指针指向新的 String
对象。StringBuffer
每次都会对 StringBuffer
对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder
相比使用 StringBuffer
仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
对于三者使用的总结:
String
StringBuilder
StringBuffer
[1]如何理解 String 类型值的不可变?- 知乎提问: https://www.zhihu.com/question/20618891/answer/114125846
[2]issue 675: https://github.com/Snailclimb/JavaGuide/issues/675
]]>
Spring Boot Admin(SBA)是一个开源的社区项目,用于管理和监控 Spring Boot 应用程序。应用程序可以通过 http 的方式,或 Spring Cloud 服务发现机制注册到 SBA 中,然后就可以实现对 Spring Boot 项目的可视化管理和查看了。
Spring Boot Admin 可以监控 Spring Boot 单机或集群项目,它提供详细的健康 (Health)信息、内存信息、JVM 系统和环境属性、垃圾回收信息、日志设置和查看、定时任务查看、Spring Boot 缓存查看和管理等功能。接下来我们一起来搭建和使用吧。
它最终的展示效果如下:
我们需要创建一个 Spring Boot Admin 项目,用来监控和管理我们的 Spring Boot 项目,搭建的方式和创建普通的 Spring Boot 项目类似,具体步骤如下。
使用 Idea 创建一个 Spring Boot 项目:
这里需要注意,需要添加 Spring Boot Admin(Server)服务端框架的支持,如下图所示:
也就是创建的 Spring Boot 项目需要添加以下两个重要的框架支持:
1 | <dependency> |
创建完项目之后,需要在启动类上开启 SBA 服务:
1 | import de.codecentric.boot.admin.server.config.EnableAdminServer; |
在 application.properties 中配置一个项目的端口号就可以直接启动了,我配置的端口号是 9001:
1 | server.port=9001 |
PS:配置端口号的主要目的是为了不和其他 Spring Boot 项目冲突,如果 SBA 是单独部署此步骤可以忽略。
启动项目之后,就可以看到 SBA 的主页了,如下图所示:
此时 SBA 中还没有添加任何需要监控的项目,接下来我们再创建一个 Spring Boot 项目,加入到 SBA 中来进行监控和管理吧。
首先,我们需要创建一个普通的 Spring Boot 项目,具体的创建步骤这里就不演示了。当创建好 Spring Boot 项目之后,需要在这个 Spring Boot 项目中需要添加 SBA 客户端框架的支持,也就是在 pom.xml 中配置如下内容:
1 | <dependency> |
然后在 application.properties 文件中配置 SBA 服务器端地址,也就是咱们第一步创建 SBA 项目的地址,配置内容如下:
1 | # 当前项目端口号 |
其中“spring.boot.admin.client.url”为 SBA 监控地址。
配置完以上信息之后,此时查看 Spring Boot Admin 页面中就有被监控的 Spring Boot 项目了,如下图所示:
也可以点击“应用墙”查看 Spring Boot Admin 中所有被监控的 Spring Boot 项目,如下图所示:
点击应用进入详情页面,如下图所示:
事件日志中包含 Spring Boot 各种状态的展示(UP 为正常、OFFLINE 为异常)和发生的时间,如下图所示:
当我们手动把被监控的 Spring Boot 项目停止之后,在 Spring Boot Admin 中就可以查看到一个应用已经被停掉了,如下图所示:
我们也可以通过事件日志查看 Spring Boot 宕机的具体时间,如下图所示:
通过上面的内容我们可以看出,监控的 Spring Boot 选项还是比较少的,怎么才能查看更多的监控项呢? 要解决这个问题,我们需要在被监控的 Spring Boot 项目中添加 spring-boot-starter-actuator 框架的支持,并开启查看所有监控项的配置才行,最终展示效果如下:
接下来我们来配置一下这些监控项。
在被监控的 Spring Boot 项目中添加 actuator 框架支持,也就是在 pom.xml 中添加以下配置:
1 | <dependency> |
手动点击 Maven 导入依赖包(如果开启了自动导入,此步骤可忽略)。
在被监控的 Spring Boot 项目中添加以下配置:
1 | # 开启监控所有项 |
以上的配置是开放监控所有选项,配置完之后,重启此 Spring Boot 项目,然后再刷新 Spring Boot Admin 更多的监控项就展示出来了,如下图所示:
将 Spring Boot 的所有监控项都开启之后,通过 SBA 就可以查看以下内容了:
以下是几个重要页面的截图,我们一起来看。
我们可以通过 Spring Boot Admin 来动态的配置项目中的日志级别。
当然我们还可以对这些缓存进行删除操作。
想要查看监控项目中的日志信息,有一个前提条件,前提条件是你被监控的 Spring Boot 项目,必须配置了日志的保存路径或者日志保存文件名,只有配置这两项中的任意一项,你的 Spring Boot 项目才会将日志保存到磁盘上,这样才能通过 SBA 查看到,我配置的是日志路径,在 Spring Boot 的 application.properties 配置文件中添加以下配置:
1 | # 设置日志保存路径 |
设置完成之后,重启你的 Spring Boot 项目,然后刷新 SBA 页面,最终展示效果如下:
此时我们就可以查看实时的日志信息了,当然你也可以随时下载日志,如果需要的话。
Spring Boot Admin(SBA)是一个社区开源项目,用于管理和监视 Spring Boot 应用程序,它提供详细的健康 (Health)信息、内存信息、JVM 系统和环境属性、垃圾回收信息、日志设置和查看、定时任务查看、Spring Boot 缓存查看和管理等功能。
我们需要创建一个 SBA 服务器端用来监控一个或多个 Spring Boot 项目,被监控的 Spring Boot 项目要添加 SBA Client 框架的支持,且添加 actuator 框架和相应的配置,就可以实现对 Spring Boot 项目的完美监控了。
]]>安装好Intellij idea之后,进行如下的初始化操作,工作效率提升十倍。
1. Codota 代码智能提示插件
只要打出首字母就能联想出一整条语句,这也太智能了,还显示了每条语句使用频率。
原因是它学习了我的项目代码,总结出了我的代码偏好。
如果让它再加上机器学习,人工智能写代码的时代还会远吗?
2. Key Promoter X 快捷键提示插件
每次都会在右下角弹窗提示,帮助我们快速熟悉快捷键。
3. CodeGlance 显示代码缩略图插件
当代码很多的时候,方便查看,很有用。
4. Lombok 简化臃肿代码插件
实体类中的get/set/构造/toString/hashCode等方法,都不需要我们再手动写了
5. Alibaba Java Coding Guidelines 阿里巴巴代码规范检查插件
会按照阿里Java开发手册上规范帮我们检查代码,然后对代码做不同颜色展示,鼠标放上去,会看到提示内容,帮助我们写出更规范的代码。
6. CamelCase 驼峰命名和下划线命名转换
这几种风格的命名方式,用快捷键 ⇧ + ⌥ + U / Shift + Alt + U可以进行快速转换,当我们需要修改大量变量名称的时候很方便。
7. MybatisX 高效操作Mybatis插件
8. SonarLint 代码质量检查插件
提示我不要用System.out输出,要用logger输出,诸如此类,帮助我们提升代码质量。
9. Save Actions 格式化代码插件
可以帮忙我们优化包导入,自动给没有修改的变量添加final修饰符,调用方法的时候自动添加this关键字等,使我们的代码更规范统一。
10. CheckStyle 代码风格检查插件
功能跟Alibaba Java Coding Guidelines类似
11. Grep Console 自定义控制台输出格式插件
12. MetricsReloaded 代码复杂度检查插件
13. Statistic 代码统计插件
14. Translation 翻译插件
15. Rainbow Brackets 彩虹括号插件
成对儿的括号显示相同的颜色,有了这个插件,我的近视都好了。
只要输入apr ,就能自动提示,并且生成Autowired 语句了。可以根据自己的代码习惯,自定义一些代码模板,帮助我们快速写代码。
1. 优化导包配置
2. 取消tab页单行显示
多行显示更多的文件,方便查看。
3. 双斜杠注释改成紧跟代码头
4. 选中复制整行
原本只会复制你选中的代码,改完配置后,就能复制整行,无论你是否完全选中。
5. 取消匹配大小写
取消勾选后,输入小写 s ,也能提示出 String
6. 优化版本控制的目录颜色展示
7. 创建文件时,自动生成作者和时间信息
8 . 显示行号和方法分割线
你还知道哪些关于Intelij idea高效操作或插件呢?
]]>1.创建有json字段的表
1 | -- 创建表 |
2.插入记录
1 | -- 插入含有json数组的记录 |
3.查询记录
1 | -- 查询记录 |
4.修改记录
1 | -- 增加键 |
1.JSON_ARRAY 生成json数组
1 | -- JSON_ARRAY(val1,val2,val3...) |
2.JSON_OBJECT 生成json对象
1 | -- JSON_OBJECT(key1,val1,key2,val2...) |
3.JSON_QUOTE 加”号
1 | -- JSON_QUOTE(json_val) |
1.JSON_CONTAINS 指定数据是否存在
1 | set @j = '{"a": 1, "b": 2, "c": {"d": 4}}'; |
2.JSON_CONTAINS_PATH 指定路径是否存在
1 | -- JSON_CONTAINS_PATH(json_doc, one_or_all, path[, path] ...) |
3.JSON_EXTRACT 查找所有指定数据
1 | -- JSON_EXTRACT(json_doc, path[, path] ...) |
4.JSON_KEYS 查找所有指定键值
1 | -- JSON_KEYS(json_doc[, path]) |
5.JSON_SEARCH 查找所有指定值的位置
1 | -- JSON_SEARCH(json_doc, one_or_all, search_str[, escape_char[, path] ...]) |
1.JSON_ARRAY_APPEND 指定位置追加数组元素
1 | -- JSON_ARRAY_APPEND(json_doc, path, val[, path, val] ...) |
2.JSON_ARRAY_INSERT 指定位置插入数组元素
1 | -- JSON_ARRAY_INSERT(json_doc, path, val[, path, val] ...) |
3.JSON_INSERT 指定位置插入
1 | -- JSON_INSERT(json_doc, path, val[, path, val] ...) |
4.JSON_REPLACE 指定位置替换
1 | -- JSON_REPLACE(json_doc, path, val[, path, val] ...) |
5.JSON_SET 指定位置设置
1 | -- JSON_SET(json_doc, path, val[, path, val] ...) |
6.JSON_MERGE 合并
1 | -- JSON_MERGE(json_doc, json_doc[, json_doc] ...) |
7.JSON_REMOVE 指定位置移除
1 | -- JSON_REMOVE(json_doc, path[, path] ...) |
8.JSON_UNQUOTE 去”号
1 | -- JSON_UNQUOTE(val) |
1.JSON_DEPTH 深度
1 | -- JSON_DEPTH(json_doc) |
2.JSON_LENGTH 长度
1 | -- JSON_LENGTH(json_doc[, path]) |
3.JSON_TYPE 类型
1 | -- JSON_TYPE(json_val) |
4.JSON_VALID 是否有效json格式
1 | -- JSON_VALID(val) |
附录:
1 | JSON_ARRAY 生成json数组 |
函数名 | 描述 |
---|---|
JSON_APPEND() (废弃的5.7.9) | JSON文件追加数据 |
JSON_ARRAY() | 创建JSON数组 |
JSON_ARRAY_APPEND() | JSON文件追加数据 |
JSON_ARRAY_INSERT() | 插入JSON数组 |
-> | 在评估路径返回JSON列值;相当于json_extract()。 |
JSON_CONTAINS() | 是否包含特定对象的JSON文档路径 |
JSON_CONTAINS_PATH() | 无论是JSON文件包含任何数据路径 |
JSON_DEPTH() | JSON文档的最大深度 |
JSON_EXTRACT() | 从JSON文档返回数据 |
->> | 在评估路径和结束引语结果返回JSON列值;相当于json_unquote(json_extract())。 |
JSON_INSERT() | 将数据插入到JSON文档 |
JSON_KEYS() | 从JSON文件密钥数组 |
JSON_LENGTH() | 在JSON文档中的元素数 |
JSON_MERGE() (废弃的5.7.22) | 合并的JSON文件,保存重复键。不json_merge_preserve()的同义词 |
JSON_MERGE_PATCH() | 合并的JSON文件,免去重复键的值 |
JSON_MERGE_PRESERVE() | 合并的JSON文件,保存重复键 |
JSON_OBJECT() | 创建JSON对象 |
JSON_PRETTY() | 版画在人类可读的格式JSON文档,每个数组元素或对象成员打印在新的行中,缩进两个空格就其母。 |
JSON_QUOTE() | 引用JSON文档 |
JSON_REMOVE() | 从JSON文件中删除数据 |
JSON_REPLACE() | 在JSON文件的值替换 |
JSON_SEARCH() | 在JSON文件价值路径 |
JSON_SET() | 将数据插入到JSON文档 |
JSON_STORAGE_SIZE() | 用于一个JSON文件的二进制表示形式存储空间;一个JSON柱,空间时使用的文档插入到任何部分更新之前, |
JSON_TYPE() | JSON值类型 |
JSON_UNQUOTE() | JSON值而言 |
JSON_VALID() | JSON值是否是有效的 |
mysql官方文档: https://dev.mysql.com/doc/refman/5.7/en/json-utility-functions.html
]]>参考:
https://www.cnblogs.com/waterystone/p/5626098.html
https://www.cnblogs.com/ooo0/p/9309277.html
这表是干啥的呢?就是记录一下谁扫码了。谁登录了。
User_Token表
字段如下:
咱们还需要分析一下子。扫码登录这个业务逻辑都有哪些角色
有了角色。你用大腿也能想出来接口了对不对!!
所以咱们的接口有2个!
那句话怎么说的来着。要把大象装冰箱一共分几步?
好了!分析完了这些。你们一定在想。。还有完没完啊。。不要在BB了。。赶紧贴代码吧。。
作者:观众老爷们。我这是在教给你们如何思考的方法呀?
那么开始贴代码吧!希望大家在看到的同时也可以自己进行思考。
首先需要获取二维码的代码对不对!贴!
1 | //获取登录二维码、放入Token |
有了获取二维码的接口。相对的前端需要调用。
==========================================
知识点:动态加载图片流并取出header中的参数
这里使用了xmlhttp进行处理。
为什么?
因为后端返回的是一个流。
那么流中。就是放置了二维码中的uuid。这个uuid作为一次会话的标识符使用。
那么前端也需要拿到。跟后端进行webSocket链接。
这样有人扫码后。服务端才可以使用webSocket的方式通知前端。有人扫码成功了。你做你的业务吧。酱紫。
所以为了拿到请求中 header中放置的uuid 所以这样通过xmlhttp进行处理
==========================================
1 | <div class="qrCodeImg-box" id="qrImgDiv"></div> |
1 | initQrImg(); |
好了。上面已经提到了前端如何配置webSocket。
1、增加pom.xml
1 | <dependency> |
2、增加一个Bean
1 | /** |
3、定义WebSocketServer
1 | package com.stylefeng.guns.rest.modular.inve.websocket; |
这样就增加了webSocket的支持啦。
1、首先PC端调用接口展示出来了二维码。
2、请求二维码中的http请求。就有uuid在 header中。直接取到uuid 作为webSocket的标识sid进行连接。
3、然后手机端使用相机拿到二维码中的uuid。使用uuid + userid 请求 扫码成功接口。
贴扫码成功接口
Controller代码:
1 | /** |
Service代码
1 | @Override |
逻辑大概就是判断一下 token对不对。
如果对的话。时间是否过期。如果没有过期进行业务逻辑操作
1 | //这句话比较关键 |
就是通知前端已经登录成功了。并且给他业务所需要的内容。
然后前端代码接收到了。就进行业务逻辑操作就可以啦。
————————————————
版权声明:本文为CSDN博主「93年颈椎病人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/q826qq1878/article/details/91041679
1 | userList.stream().collect(Collectors.toMap(User::getId, User::getName)); |
当然,如果希望得到 Map 的 value 为对象本身时,可以这样写:
1 | userList.stream().collect(Collectors.toMap(User::getId, t -> t)); |
Collectors.toMap 有三个重载方法:
1 | toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper); |
参数含义分别是:
1 | List<User> userList = Lists.newArrayList( |
异常:
1 | java.lang.IllegalStateException: Duplicate key A |
这时就需要调用第二个重载方法,传入合并函数,如:
1 | userList.stream().collect(Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1 + n2)); |
输出结果:
1 | A-> 张三李四 |
第四个参数(mapSupplier)用于自定义返回 Map 类型,比如我们希望返回的 Map 是根据 Key 排序的,可以使用如下写法:
1 | List<User> userList = Lists.newArrayList( |
输出结果:
1 | A-> 李四 |
方法 | 返回类型 | 作用 |
---|---|---|
toList | List< T > | 把流中所有项目收集到一个 List |
toSet | Set< T > | 把流中所有项目收集到一个 Set,删除重复项 |
toCollection | Collection< T > | 把流中所有项目收集到给定的供应源创建的集合menuStream.collect(toCollection(), ArrayList::new) |
counting | Long | 计算流中元素的个数 |
sumInt | Integer | 对流中项目的一个整数属性求和 |
averagingInt | Double | 计算流中项目 Integer 属性的平均值 |
summarizingInt | IntSummaryStatistics | 收集关于流中项目 Integer 属性的统计值,例如最大、最小、 总和与平均值 |
joining | String | 连接对流中每个项目调用 toString 方法所生成的字符串collect(joining(", ")) |
maxBy | Optional< T > | 一个包裹了流中按照给定比较器选出的最大元素的 Optional, 或如果流为空则为 Optional.empty() |
minBy | Optional< T > | 一个包裹了流中按照给定比较器选出的最小元素的 Optional, 或如果流为空则为 Optional.empty() |
reducing | 归约操作产生的类型 | 从一个作为累加器的初始值开始,利用 BinaryOperator 与流 中的元素逐个结合,从而将流归约为单个值累加int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum)); |
collectingAndThen | 转换函数返回的类型 | 包裹另一个收集器,对其结果应用转换函数int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size)) |
groupingBy | Map< K,List< T > > | 根据项目的一个属性的值对流中的项目作问组,并将属性值作 为结果 Map 的键 |
partitioningBy | Map< Boolean , List< T >> | 根据对流中每个项目应用谓词的结果来对项目进行分区 |
1 | public ThreadPoolExecutor(int corePoolSize, |
1 | public ThreadPoolExecutor(int corePoolSize, |
1 | public ThreadPoolExecutor(int corePoolSize, |
1 | public ThreadPoolExecutor(int corePoolSize, |
参数说明:
序号 | 名称 | 类型 | 含义 |
---|---|---|---|
1 | corePoolSize | int | 核心线程池大小 |
2 | maximumPoolSize | int | 最大线程池大小 |
3 | keepAliveTime | long | 线程最大空闲时间 |
4 | unit | TimeUnit | 时间单位 |
5 | workQueue | BlockingQueue | 线程等待队列 |
6 | threadFactory | ThreadFactory | 线程创建工厂 |
7 | handler | RejectedExecutionHandler | 拒绝策略 |
1 | public static ExecutorService newFixedThreadPool(int nThreads) { |
- corePoolSize与maximumPoolSize相等,即其线程全为核心线程,是一个固定大小的线程池,是其优势;
- keepAliveTime = 0 该参数默认对核心线程无效,而FixedThreadPool全部为核心线程;
- workQueue 为LinkedBlockingQueue(无界阻塞队列),队列最大值为Integer.MAX_VALUE。如果任务提交速度持续大余任务处理速度,会造成队列大量阻塞。因为队列很大,很有可能在拒绝策略前,内存溢出。是其劣势;
- FixedThreadPool的任务执行是无序的;
适用场景:可用于Web服务瞬时削峰,但需注意长时间持续高峰情况造成的队列阻塞。
1 | public static ExecutorService newCachedThreadPool() { |
- corePoolSize = 0,maximumPoolSize = Integer.MAX_VALUE,即线程数量几乎无限制;
- keepAliveTime = 60s,线程空闲60s后自动结束。
- workQueue 为 SynchronousQueue 同步队列,这个队列类似于一个接力棒,入队出队必须同时传递,因为CachedThreadPool线程创建无限制,不会有队列等待,所以使用SynchronousQueue;
适用场景:快速处理大量耗时较短的任务,如Netty的NIO接受请求时,可使用CachedThreadPool。
1 | public static ExecutorService newSingleThreadExecutor() { |
咋一瞅,不就是newFixedThreadPool(1)吗?定眼一看,这里多了一层FinalizableDelegatedExecutorService包装,这一层有什么用呢,写个dome来解释一下:
**
1 | public static void main(String[] args) { |
对比可以看出,FixedThreadPool可以向下转型为ThreadPoolExecutor,并对其线程池进行配置,而SingleThreadExecutor被包装后,无法成功向下转型。因此,SingleThreadExecutor被定以后,无法修改,做到了真正的Single。
1 | public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { |
newScheduledThreadPool调用的是ScheduledThreadPoolExecutor的构造方法,而ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,构造是还是调用了其父类的构造方法。
1 | public ScheduledThreadPoolExecutor(int corePoolSize) { |
对于ScheduledThreadPool本文不做描述,其特性请关注后续篇章。
以下是自定义线程池,使用了有界队列,自定义ThreadFactory和拒绝策略的demo:
1 | public class ThreadTest { |
结果:
在日常开发中经常遇到先根据条件判断某条数据是否存在,如果不存在的话就插入,如果存在的话就更新或提示异常。一般代码的模式都写成下面这个样子,是一种很常见的写法,但是在并发情况下很容易会重复插入两条数据,大概的情况就是第一个请求进来,没有查询到该用户通过了if
判断,但是if
中有比较耗时的逻辑,在第一个请求还没执行insert
的时候第二个请求也进来了,因为这个时候第一个请求还没执行insert
操作,所以第二个请求也没有查询到该用户也通过了if
判断,这个样子就造成了两条重复的数据。
1 | // 查询名字叫user1的用户是否存在 |
synchronized
同步代码块直接将查询校验逻辑和插入逻辑都进行同步,也就是说第一个请求的逻辑没结束,第二个请求就会一直等待着,只有当第一个请求执行完同步代码块中的逻辑释放锁后第二个请求才能获取到锁执行这段逻辑。
1 | private Object obj = new Object(); |
Lock
锁其实和synchronized
代码块是相同的作用,但是要注意必须在finally
中释放锁,避免出现异常死锁了。
1 | private Lock lock = new ReentrantLock(); |
既然是要根据用户名字判断是否有重复数据,所以可以直接在数据库上给userName
字段添加UNIQUE
索引,这样在第二次重复插入的时候就会提示异常。如果不想重复插入的时候有报错提示可以使用INSERT IGNORE INTO
语句。而代码则不必做任何逻辑操作。
1 | // 查询名字叫user1的用户是否存在 |
redis
中setnx
来作为锁redis
中setnx
命令是只有当你存入的key
不存在时才会成功存入,并返回1,而如果key
已经存在的时候则存入失败并返回0,我们可以拿这个特性来当做锁。首先这个方法进来第一步就是执行setnx
操作,把查询的用户名存入redis
,然后查询该用户是否存在,第一个请求进到if
判断中但是没执行插入逻辑,第二个请求虽然也没有查询到该用户,但是它的setnx
会失败,因为第一个请求存的key
还没删除,所以这样就避免了并发重新插入的问题,而且最大的优点就是它不像synchronized
和Lock
无论所有请求进来都只能一个一个通过,使用这种方法是只有当操作同一个用户有并发请求的时候才会阻塞,而如果是请求两个不同的用户时是不会阻塞的,都可以顺利通过,因为存入的key
是不同的。
1 | // 自动注入spring的redis操作类 |
上述四种方法,给数据库加索引、Lock
和redis
都有使用过,synchronized
和Lock
也差不多,个人感觉给数据库加索引来控制这种并发太死板了,万一系统中有其他地方的逻辑是需要重复添加这个字段的数据,这个时候就没办法使用索引了,synchronized
和Lock
效率太低了,如果是并发量太大的这种方式肯定是不可缺的,而redis的这种方法则效率高很多,比较适合并发量高的操作。
转载:架构之师
例如:小米手机每周二的秒杀,可能手机只有1万部,但瞬时进入的流量可能是几百几千万。
又例如:12306抢票,票是有限的,库存一份,瞬时流量非常多,都读相同的库存。读写冲突,锁非常严重,这是秒杀业务难的地方。那我们怎么优化秒杀业务的架构呢?
优化方向有两个(今天就讲这两个点):
(1)将请求尽量拦截在系统上游(不要让锁冲突落到数据库上去)。传统秒杀系统之所以挂,请求都压倒了后端数据层,数据读写锁冲突严重,并发高响应慢,几乎所有请求都超时,流量虽大,下单成功的有效流量甚小。以12306为例,一趟火车其实只有2000张票,200w个人来买,基本没有人能买成功,请求有效率为0。
(2)充分利用缓存,秒杀买票,这是一个典型的读多写少的应用场景,大部分请求是车次查询,票查询,下单和支付才是写请求。一趟火车其实只有2000张票,200w个人来买,最多2000个人下单成功,其他人都是查询库存,写比例只有0.1%,读比例占99.9%,非常适合使用缓存来优化。好,后续讲讲怎么个“将请求尽量拦截在系统上游”法,以及怎么个“缓存”法,讲讲细节。
常见的站点架构基本是这样的(绝对不画忽悠类的架构图)
秒杀架构
这个图虽然简单,但能形象的说明大流量高并发的秒杀业务架构,大家要记得这一张图。后面细细解析各个层级怎么优化。
问大家一个问题,大家都玩过微信的摇一摇抢红包对吧,每次摇一摇,就会往后端发送请求么?回顾我们下单抢票的场景,点击了“查询”按钮之后,系统那个卡呀,进度条涨的慢呀,作为用户,我会不自觉的再去点击“查询”,对么?继续点,继续点,点点点。。。有用么?平白无故的增加了系统负载,一个用户点5次,80%的请求是这么多出来的,怎么整?
APP层面,可以做类似的事情,虽然你疯狂的在摇微信,其实x秒才向后端发起一次请求。这就是所谓的“将请求尽量拦截在系统上游”,越上游越好,浏览器层,APP层就给拦住,这样就能挡住80%+的请求,这种办法只能拦住普通用户(但99%的用户是普通用户)对于群内的高端程序员是拦不住的。firebug一抓包,http长啥样都知道,js是万万拦不住程序员写for循环,调用http接口的,这部分请求怎么处理?
怎么拦截?怎么防止程序员写for循环调用,有去重依据么?ip?cookie-id?…想复杂了,这类业务都需要登录,用uid即可。在站点层面,对uid进行请求计数和去重,甚至不需要统一存储计数,直接站点层内存存储(这样计数会不准,但最简单)。一个uid,5秒只准透过1个请求,这样又能拦住99%的for循环请求。
5s只透过一个请求,其余的请求怎么办?缓存,页面缓存,同一个uid,限制访问频度,做页面缓存,x秒内到达站点层的请求,均返回同一页面。同一个item的查询,例如车次,做页面缓存,x秒内到达站点层的请求,均返回同一页面。如此限流,既能保证用户有良好的用户体验(没有返回404)又能保证系统的健壮性(利用页面缓存,把请求拦截在站点层了)。
页面缓存不一定要保证所有站点返回一致的页面,直接放在每个站点的内存也是可以的。优点是简单,坏处是http请求落到不同的站点,返回的车票数据可能不一样,这是站点层的请求拦截与缓存优化。
好,这个方式拦住了写for循环发http请求的程序员,有些高端程序员(黑客)控制了10w个肉鸡,手里有10w个uid,同时发请求(先不考虑实名制的问题,小米抢手机不需要实名制),这下怎么办,站点层按照uid限流拦不住了。
服务层怎么拦截?大哥,我是服务层,我清楚的知道小米只有1万部手机,我清楚的知道一列火车只有2000张车票,我透10w个请求去数据库有什么意义呢?没错,请求队列!
对于写请求,做请求队列,每次只透有限的写请求去数据层(下订单,支付这样的写业务)
1w部手机,只透1w个下单请求去db
3k张火车票,只透3k个下单请求去db
如果均成功再放下一批,如果库存不够则队列里的写请求全部返回“已售完”。
对于读请求,怎么优化?cache抗,不管是memcached还是redis,单机抗个每秒10w应该都是没什么问题的。如此限流,只有非常少的写请求,和非常少的读缓存mis的请求会透到数据层去,又有99.9%的请求被拦住了。
当然,还有业务规则上的一些优化。回想12306所做的,分时分段售票,原来统一10点卖票,现在8点,8点半,9点,…每隔半个小时放出一批:将流量摊匀。
其次,数据粒度的优化:你去购票,对于余票查询这个业务,票剩了58张,还是26张,你真的关注么,其实我们只关心有票和无票?流量大的时候,做一个粗粒度的“有票”“无票”缓存即可。
第三,一些业务逻辑的异步:例如下单业务与 支付业务的分离。这些优化都是结合 业务 来的,我之前分享过一个观点“一切脱离业务的架构设计都是耍流氓”架构的优化也要针对业务。
浏览器拦截了80%,站点层拦截了99.9%并做了页面缓存,服务层又做了写请求队列与数据缓存,每次透到数据库层的请求都是可控的。db基本就没什么压力了,闲庭信步,单机也能扛得住,还是那句话,库存是有限的,小米的产能有限,透这么多请求来数据库没有意义。
全部透到数据库,100w个下单,0个成功,请求有效率0%。透3k个到数据,全部成功,请求有效率100%。
上文应该描述的非常清楚了,没什么总结了,对于秒杀系统,再次重复下我个人经验的两个架构优化思路:
浏览器和APP:做限速
站点层:按照uid做限速,做页面缓存
服务层:按照业务做写请求队列控制流量,做数据缓存
数据层:闲庭信步
并且:结合业务做优化
问题1、按你的架构,其实压力最大的反而是站点层,假设真实有效的请求数有1000万,不太可能限制请求连接数吧,那么这部分的压力怎么处理?
答:每秒钟的并发可能没有1kw,假设有1kw,解决方案2个:
问题2、“控制了10w个肉鸡,手里有10w个uid,同时发请求” 这个问题怎么解决哈?
答:上面说了,服务层写请求队列控制
问题3:限制访问频次的缓存,是否也可以用于搜索?例如A用户搜索了“手机”,B用户搜索“手机”,优先使用A搜索后生成的缓存页面?
答:这个是可以的,这个方法也经常用在“动态”运营活动页,例如短时间推送4kw用户app-push运营活动,做页面缓存。
问题4:如果队列处理失败,如何处理?肉鸡把队列被撑爆了怎么办?
答:处理失败返回下单失败,让用户再试。队列成本很低,爆了很难吧。最坏的情况下,缓存了若干请求之后,后续请求都直接返回“无票”(队列里已经有100w请求了,都等着,再接受请求也没有意义了)
问题5:站点层过滤的话,是把uid请求数单独保存到各个站点的内存中么?如果是这样的话,怎么处理多台服务器集群经过负载均衡器将相同用户的响应分布到不同服务器的情况呢?还是说将站点层的过滤放到负载均衡前?
答:可以放在内存,这样的话看似一台服务器限制了5s一个请求,全局来说(假设有10台机器),其实是限制了5s 10个请求,解决办法:
问题6:服务层过滤的话,队列是服务层统一的一个队列?还是每个提供服务的服务器各一个队列?如果是统一的一个队列的话,需不需要在各个服务器提交的请求入队列前进行锁控制?
答:可以不用统一一个队列,这样的话每个服务透过更少量的请求(总票数/服务个数),这样简单。统一一个队列又复杂了。
问题7:秒杀之后的支付完成,以及未支付取消占位,如何对剩余库存做及时的控制更新?
答:数据库里一个状态,未支付。如果超过时间,例如45分钟,库存会重新会恢复(大家熟知的“回仓”),给我们抢票的启示是,开动秒杀后,45分钟之后再试试看,说不定又有票哟~
问题8:不同的用户浏览同一个商品 落在不同的缓存实例显示的库存完全不一样 请问老师怎么做缓存数据一致或者是允许脏读?
答:目前的架构设计,请求落到不同的站点上,数据可能不一致(页面缓存不一样),这个业务场景能接受。但数据库层面真实数据是没问题的。
问题9:就算处于业务把优化考虑“3k张火车票,只透3k个下单请求去db”那这3K个订单就不会发生拥堵了吗?
答:
问题10、如果在站点层或者服务层处理后台失败的话,需不需要考虑对这批处理失败的请求做重放?还是就直接丢弃?
答:别重放了,返回用户查询失败或者下单失败吧,架构设计原则之一是“fail fast”。
问题11、对于大型系统的秒杀,比如12306,同时进行的秒杀活动很多,如何分流?
答:垂直拆分
问题12、额外又想到一个问题。这套流程做成同步还是异步的?如果是同步的话,应该还存在会有响应反馈慢的情况。但如果是异步的话,如何控制能够将响应结果返回正确的请求方?
答:用户层面肯定是同步的(用户的http请求是夯住的),服务层面可以同步可以异步。
问题13、秒杀群提问:减库存是在那个阶段减呢?如果是下单锁库存的话,大量恶意用户下单锁库存而不支付如何处理呢?
答:数据库层面写请求量很低,还好,下单不支付,等时间过完再“回仓”,之前提过了。
]]>经常记不住,只能写下来了!!!
1 | import static java.util.stream.Collectors.toList; |
1 | Integer[] integers = Arrays.stream(arr).boxed().toArray(Integer[]::new); |
int[ ] 转 List< Integer >
1 | public static void main(String[] args) { |
Integer[ ]转 int[ ]
1 | int[] arr= Arrays.stream(integers).mapToInt(Integer::valueOf).toArray(); |
Integer[ ]转 List< Integer >
1 | Integer[] integers = {1,2,3,4,5}; |
List< Integer > 转 Integer[ ]
1 | Integer[] integers = list.toArray(new Integer[list.size()]); |
List< Integer > 转 int[ ]
1 | int[] arr2 = list.stream().mapToInt(Integer::valueOf).toArray(); |
Spring Cloud使用的版本号是英文方式,而不是传统的数字版本,为什么呢。因为Spring Cloud是微服务的解决方案,他会有很多子项目,每个子项目都维护这自己的版本号,为了避免冲突,就使用了伦敦地铁站的名字作为版本号。以首字母作为顺序,a,b,c,d….排列。
现有版本号:Angel、Brixton、Camden、Daston、Edgware、Finchley、GreenWich、Hoxton。
例如 2.1.13.RELEASE
其中Release的意思是最终版本,除此之外:
Base:设计阶段。只有相应的设计没有具体的功能实现。
Alpha:软件的初级版本。基本功能已经实现,但存在较多的bug。
Bate:相对于Alpha已经有了很大的进步,消除了严重的BUG,但还存在一些潜在的BUG,还需要不断测试。
RELEASE:最终版本,没有太大的问题。
另外,springcloud和springboot的版本有相应的对应关系,如果不对应会出现问题。
具体关系可以查看:https://start.spring.io/info
]]>今天推送几个沙雕项目,太秀了🤣 ,找几个过于沙雕的分享给大家。
这个项目是一个简易的 logo 生成器,能生成下图类似风格的 logo。下图中关键部分做了马赛克处理 :trollface:
经常刷小破站的读者应该经常看到类似风格的视频封面,比如下图圈出来的封面。估计这种风格的封面是半佛老师的最爱呀~
(不仅仅是B站,经常逛 P 站的朋友肯定见过这个风格的logo )
如何生成自己的 logo
你可能想不到这个 17.7k star 的项目是一个面向可爱的蓝孩子的 git 学习实践项目 。
该项目对贡献者的要求不高,并不要求你贡献代码,没有编程技能都可以参加。你可以从这里学习从克隆项目,创建分支,提交和同步修改,到合并分支请求的整套流程,一次即可熟悉 Git/GitHub 的使用。
但是:你还要事先准备至少一张你的女装照。
没错,这就是女装大佬集结地,截止目前为止,已经有 两百多名大佬上传过女装照,并且有 1103 次提交。光看贡献者的头像,二次元风格满满。本来想找几张给大家养养眼,打开后发现顶不住啊,溜了溜了。。。
想在简历里写上:参与过Github 10000+ starts的项目,那你就PR这个吧
一个开源的个灭霸命令,可随机删除电脑上一半文件
PS:请不要在家里或其他地方使用。这是真家伙,要小心…
这是一个文言文編程語言,没错,文言文。。。你以为只是玩玩?你想多了,还有配套的 ide:https://ide.wy-lang.org/ 下图是一个快速排序的
Helloworld
Wenyan:
1 | 吾有一數。曰三。名之曰「甲」。 |
Equivalent JavaScript:
1 | var n = 3; |
Output:
1 | 問天地好在。 |
刚刚是文言文编程语言,这个是东北方言编程。体验一下东北方言编程的 hello word。
吃了没,老铁
创建一个名字叫 hello-world.dongbei 的文本文件,内容如下:
唠唠:“唉呀,这嘎哒真他妈那啥!”。
用 utf-8 编码保存。要是编辑器因为编码有毛病埋汰你,那就把文件内容改成
1 | # -*- coding: utf-8 -*- |
再试,应该就成了。
然后在命令行窗口运行:
dongbei hello-world.dongbei
你应该看到执行结果:
唉呀,这嘎哒真他妈那啥!
项目理念:没有代码是编写安全可靠的应用程序的最佳方法。什么也不要写,也不用部署。
Hello Word:
入门
首先不编写任何代码。
1 |
这只是一个示例应用程序,但是请想象它可以做任何您想做的事情。添加新功能也很容易:
1 |
一切皆有可能!
构建应用程序
现在您还没有做任何事情,现在是时候构建您的应用程序了:
1 |
您应该看到以下输出:
1 |
部署
尽管你没有做任何事情,但是现在该部署应用程序了。通过运行以下命令部署你的应用程序。
1 |
就这么简单。当需要扩展应用程序时,你要做的就是:
1 |
酷不酷?
这个项目啥都没有,就有 42.5k Star,而且 Issues 竟然也有 3.2k,集体装逼盛宴!翻了几页,大家体会一下:
这是一个可以伪造任何网站界面截图的工具。
但本工具的目的其实不是破坏,而是为了警告(不懂编程的)普通人:不要轻易相信网上看到的“截图”!****2020-4-26:突破性更新,可以修改任何网站的任何文字/图片。
很有意思的一个截图工具!!!
最全中华古诗词数据库, 唐宋两朝近一万四千古诗人, 接近5.5万首唐诗加26万宋诗. 两宋时期1564位词人,21050首词。
Tool for visualizing GitHub profiles(图像化展示Github账号的信息)
PS:这个很Nice,可以再GitHub首页使用试试
]]>代码转换成图片
MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL
docker
方式部署MinIO
推荐使用 docker
一键安装:
1 | docker run -it -p 9000:9000 --name minio \ |
注意:
1 | server{ |
通过浏览器访问配置的地址,使用指定的 MINIO_ACCESS_KEY
及 MINIO_SECRET_KEY
登录即可
界面简单,功能齐全,支持创建
Bucket
,文件上传、删除、分享、下载,同时可以对Bucket
设置读写权限
1 | @Excel(name = "地址", isHyperlink = true) |
1 | public class ExcelDataHandler extends ExcelDataHandlerDefaultImpl<Object> { |
Object
为操作类
1 | ExportParams exportParams = new ExportParams(title, sheetName); |
Object
操作模板
今天更新了IDEA,更新到2020.1.1,发现了两个关于数据库问题。
明明是永远save,就是不起作用,每次重启IDEA都要输入。
在IDEA File—>Setting—>搜索In KeePass,然后勾选即可。
主要特征:
无Office365桌面版.无Azure Directory API操作权限.有管理员存在,才能在第三方应用挂载.有OneDrive for Busniess空间(1T~5T).正常情况下,许可证数量无限制(unlimited).
一些说明:
早些年的时候, 这种订阅被微软封掉的时候, 是只封管理员. 不封普通用户.最近出的订阅, 都是大批量产生的。被微软封的时候, 所有用户都被封不分管理和用户.订阅许可证 教师版 和 学生版 没有什么明显区别.一般被微软查到没有教育资格或者被举报, 都会有可能被封.有些许可证数量减少了, 从无限制(unlimited)减少到一个数量.这些订阅不正常的情况则是微软对该订阅有所限制了.
注意: 一般所出售的或者公开注册的,都不是老订阅.不要心存侥幸!
主要特征:
有Office365桌面版.无Azure Directory API操作权限.有管理员存在,才能在第三方应用挂载.有OneDrive for Busniess空间(1T~5T).正常情况下,许可证数量无限制(unlimited).
一些说明:
同 Microsoft Office365 A1 说明.
主要特征:
有Office365桌面版.有Azure Directory API操作权限.有OneDrive for Busniess空间(1T~5T).正常情况下,许可证数量25个.
一些说明:
通过特殊链接注册,是微软提供给订阅MSDN的开发者权益之一.
目前总体上来说,是使用来说最安心的订阅.
现在微软改变条款了,MSDN订阅权益里面不再提供这种E3注册链接.
目前市场上能看到卖的都是之前留下来的存货用一个少一个.
每年(或每月)可自动免费续期,无需额外操作!
官方的描述:”This subscription will automatically renewed all the time.”
主要特征:
无Office365桌面版.无Azure Directory API操作权限.有OneDrive for Busniess空间(1T~5T).正常情况下,许可证数量5个.
一些说明:
一年试用期,到期后如果想继续使用.那就要花钱去微软官方购买了!
主要特征:
许可证数量最高可达到350万个.
一些说明:
订阅不稳,随时做好翻车准备.一般用于某宝奸商忽悠小白.其他订阅不细说了,自己琢磨一下!
无论是哪个版本?都没有100%的安全!存储任何重要的数据都要做好备份!
Office365 E3 Trial 安全性最高,毕竟是准付费用户,正经方式上车。可惜不支持OFFICE桌面版,一年到期就得花钱了。
Office365 E3 MSDN 安全性不错,订阅特征支持的内容基本能满足我们的使用。并且有全局管理。特别推荐!不过目前E3的注册链接的价格水涨船高。17年10元一个链接已经不存在了!
Office365 A1/A1P 安全性比较低,之前博主更新过许多A1的注册地址。大部分都翻车了!
Q:全局管理员能否查看用户OneDrive 存储的内容?
A:是可以的!下图是管理员访问用户的OneDrive 设置,可见入口。
Q:Office365 E3 MSDN 三年到期后是否可以使用?
A:是可以使用的!
在博主17年的文章中曾经说过理论上是到期后可以继续使用,当时只是看到微软官方的一个说明。现在第一波E3全局管理已经到期了,还是可以继续使用的!
]]>JSON
]]>开发过程中,我们自己会在.gitignore 文件中添加一些忽略项,然而,每次提交代码还是回有提示,原因是什么呐?
新建的文件在git中会有缓存,如果某些文件已经被纳入了版本管理中,就算是在.gitignore文件中已经声明了忽略路径也是不起作用的
应该先把本地缓存删除,然后再进行git的push,这样就不会出现忽略的文件了。
命令:
1 | git rm -r --cached . |
今天导出Excel使用EasyPoi工具,发现图片导出一直报错出现越界问题java.lang.ArrayIndexOutOfBoundsException: 0
1 | cn.afterturn.easypoi.exception.excel.ExcelExportException: Excel导出错误 |
最后网上发现一篇文章 https://blog.csdn.net/qq_34988540/article/details/83050187 解释的很详细,包括源码!
主要原因:
1 | 图片的URL为空导致 |
可以看到是在ImageIO.write()我们图片url的时候得到了一个空的字节数组。
那么问题出来了,这里为什么返回了一个空的字节数组。从write方法的三个参数中的第二个参数我们可以看到这里将图片路径进行了字符串截取,截取的是第一个点以后的字符串,显然这是在截取图片的后缀名。
因为我们使用的图片路径为http://192.168.25.133/group1/M00/00/01/wKgZhVvC78SAfpWaAAAsAp7EzlE763.jpg。 随意第一个点之后的字符串显然有问题,所以这里应该截取最后一个点后面的字符串。
Easypoi的ImageCache为我们提供了setLoadingCache()方法,使得我们可以设置自己想要的loadingCache,所以,我们只需要自己重写一个loadingCache并赋值给ImageCache。
我们可以在Spring容器启动后把这个自定义的loadingCache赋值给ImageCache。
1 | package com.haier.hzy.admin.util.excel.listener; |
这个问题好像很久了,目前easypoi版本都是4.0了,好像还是没有修复!归根结底就是URL截取问题!
最后好像webp图片还不能解析,网上资料需要单独解析,没做深究,自行研究!
]]>众所周知,Github是目前最大的项目的托管平台,而且免费套餐还支持私有仓库了!但是呐,国内访问比较慢!!
今天给介绍一种加速访问方式 jsDelivr CDN
jsDelivr提供npm,GitHub,WordPress等项目的镜像,全球加速访问!
针对Github提供免费的CDN加速,在国内使用的是网宿的CDN加速!访问速度一点儿都不慢!
但是利用它读取静态文件处处有余,如JS、CSS、图片、文件等!
这里以图床方式
例如:https://cdn.jsdelivr.net/gh/JssDream/image-hosting/img/002_2.jpg
简单吧!!!:huaji:
]]>转自:如有乐享
或者打开招商APP找到学理财赢红包
需要支付1分钱买货币基金,随时可提取
红包抽奖最低6.6元,最高88元
打开gitlab发现报错500,一开始以为是网络问题,但是试了好几次发现不是,进入后台开始排查问题
gitlab-ctl restart 重启没有问题
1 | [root@localhost ~]# gitlab-ctl status |
gitlab-ctl status 当前状态没有问题
netstat -tnlp | grep 8898 没有问题
1 | [root@localhost ~]# netstat -tnlp | grep 8898 |
gitlab-ctl tail 发现问题
1 | ==> /var/log/gitlab/prometheus/current <== |
问题出现
no space left on device
,没有空间了!!!!
查看当前磁盘空间
通过命令df -h,发现系统各个文件夹的空寂,我们清理磁盘即可
1 | Filesystem Size Used Avail Use% Mounted on |
使用 du -sh查看linux硬盘各个文件占用空间统计
du -sh /var/lib/*|grep "G"
disk usage,是通过搜索文件来计算每个文件的大小然后累加,du能看到的文件只是一些当前存在的,没有被删除的。他计算的大小就是当前他认为存在的所有文件大小的累加和。
]]>disk free,通过文件系统来快速获取空间大小的信息,当我们删除一个文件的时候,这个文件不是马上就在文件系统当中消失了,而是暂时消失了,当所有程序都不用时,才会根据OS的规则释放掉已经删除的文件,df记录的是通过文件系统获取到的文件的大小,他比du强的地方就是能够看到已经删除的文件,而且计算大小的时候,把这一部分的空间也加上了,更精确了。
最近发现一位大佬做了一个工具箱,功能基本包含了榜单、图床、短链、趣图、在线音乐,感觉很有趣,分享下!
进入网站首先是几个网站实时榜单(后面应该会持续更新)
图床功能包括了图片上传、预览,还是不错的(PS:图床设置了鉴黄功能,使用人工智能,通过训练机器人达到鉴黄,可以多多益善哦 :huaji: )
这个短链功能还是很实用的,作用大家都知道就不多说了,有还原和生成短链功能
:doge:
在线音乐支持搜索!(默认网易音乐)
闲聊么功能,需要微信扫码,进去在线聊天,当然目前没什么活跃量🤣
网站右下角点击进入
:huaji: 再也不用担心没地方摸鱼了!
]]>1 | /** |
获取用户真实IP地址,为什么不直接使用request.getRemoteAddr()的原因是有可能用户使用了代理方式避免真实IP地址,如果使用了代理就可能出现多级反向代理,那X-Forwarded-For的值并不止一个,而是一串IP值,至于哪个才是真正的用户端的真实IP? 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串,而且可能获取的是代理服务器的IP
最近网上好像又开始疯传小丑了,这部电影一直想看,但一直没有看!这次周末必须看下,看看这部真实反映社会现状的神作是什么样的!!
强烈推荐!!
自行百度 :huaji:
]]>使用自定义参数解析器进行全局参数(用户信息UserInfo)注入,可以使用注解,也可以直接对象!
HandlerMethodArgumentResolver
:处理函数参数的分解器,自定义需要实现这个接口(在SpringMVC中也可以使用)
接口方法
supportsParameter
:用于判定是否需要处理该参数分解,必须返回true才会去调用下面的方法resolveArgument。(博主之前就是这个问题) resolveArgument
:真正用于处理参数分解的方法,返回的Object就是controller方法上的形参对象博主使用的是UserInfo
对象注入(注解方式自行百度)
1 | package com.bigbigsun.assets.admin.config; |
1 | package com.jssdream.admin.config; |
1 | @RequestMapping("user") |
WebMvcConfigurerAdapter这个抽象类中的方法可以看一看,addInterceptors可以添加拦截器
1 | @Override |
在Spring Boot中启动类是默认加载数据库配置的
如果在启动类的配置上加了注解@SpringBootApplication(exclude= DataSourceAutoConfiguration.class)
,项目也没有自己配置自定义DataSource那么在项目启动的时候会报数据源找不到
还有一种可能是在application.properties压根没有配置数据库配置,也会报数据源找不到
1 | *************************** |
]]>在Spring Boot 中配置Dubbo的时候,在消费者模块不需要配置数据库信息,这个时候在启动类上面添加注解
@SpringBootApplication(exclude= DataSourceAutoConfiguration.class)
没有问题!
领取腾讯云代金券
为了更好的购买体验,建议前往PC端使用代金券
适用产品:点播-存储包、点播-转码包、点播-流量包、实时音视频-预付费套餐包、实时视频通话-时长套餐包、云服务器、云数据库(MySQL)、云数据库HBase、云数据库Redis、云数据库(SQL Server)、云数据库(MariaDB)、云硬盘、MongoDB、负载均衡、云数据库(PostgreSQL)、CDN流量包、cos资源包(标准存储100G)、cos资源包(标准存储200G)、cos资源包(标准存储500G)、cos资源包(标准存储2048G)、cos资源包(标准存储10240G)、cos资源包(标准存储51200G)、cos资源包(归档存储100G)、cos资源包(归档存储200G)、cos资源包(归档存储500G)、cos资源包(归档存储2048G)、cos资源包(归档存储10240G)、cos资源包(归档存储51200G)、cos资源包(IDC流量100G)、cos资源包(IDC流量200G)、cos资源包(IDC流量500G)
地址 云服务器
]]>地址 云硬盘
今年京东双十一的全民养红包活动,网上惊现刷金币脚本!现分享出来:
1 | let productList = [], shopList = [], url = "https://api.m.jd.com/client.action"; |
每天都可以运行一次(前提没有被封!)
]]>来源福利吧,非本人原创
周末了,终于可以休息了,躺在床上看一部电影(动漫、电视剧、综艺),这是多么享受,Enjoy Time!
话说2019年国内的动画真是逆天了有没有,要崛起的节奏
先有《哪吒之魔童降世》“我命由我不由天”
后有《罗小黑战记》“人和妖一样,很难定义好坏,好坏在不同人眼里也不是绝对的。”
链接: https://pan.baidu.com/s/1nqZ1C1gQZzTOcS7Il6tXeg&shfl=shareset 提取码: 8n4x
本剧的主角为艾略特·奥尔德森 (Elliot Alderson),他居住在纽约市,是一名网络安全工程师。晚上,他化身为一名黑客。艾略特患有社交恐惧症。艾略特遇见了一个神秘的无政府主义者,他名为机器人先生 (Mr. Robot) 。机器人先生招募艾略特加入他的黑客组织“反社会 (fsociety)”。艾略特感到好奇,但他并没有确定是否要加入这个黑客组织。机器人先生让艾略特暗中摧毁雇佣艾略特的公司。艾略特对此跨国集团反感,但秉持良心公义,他不想伤害无辜,所以仍然一次又一次抗拒去彻底揭发对世界有巨大影响力的这个集团。
链接: https://pan.baidu.com/s/1fCKEe5zbjmvgJuU4vC2zXg 提取码: 5ktn
有喜欢的可以下载
链接: https://pan.baidu.com/s/1FH9qG1YAf8u6ar0zUQqD6Q 提取码: u4b9
]]>最新号段:
手机号码: 13[0-9], 14[5,6,7,8,9], 15[0-3, 5-9], 16[2,5,6,7], 17[0-8], 18[0-9], 19[1,3,5,8,9]
1 | regex:/^1([38]\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|9[13589])\d{8}$/ |
1 | /^[1]([3-9])[0-9]{9}$/ |
手机号前三位
130 131 132 133 134 135 136 137 138 139
145 146 147 148 149
150 151 152 153 | 155 156 157 158 159
162 165 166 167
170 171 172 173 174 175 176 177 178
180 181 182 183 184 185 186 187 188 189
191 193 195 198 199
1 | public static boolean isMobileNO(String mobiles) { |
sublime text 3
,至于软件,官网下载sublime text
的安装目录,然后备份一下 sublime_text.exe
Open file
并选择自己安装的sublime_text.exe
可执行文件Search for
框, 输入97 94
并点击Search now
进行搜索97 94 0D
,如果看到97 94 0D
, 将它改为00 00 00
Export
“并保存到本地,替换源文件2021年7月14日 最新更新!
软件版本 4113 依此替换下方 2 组字节!C3 C6 01 00 C3
替换为 C3 C6 01 01 C3
51 31 C0 88 05
替换为 51 b0 01 88 05
2021年12月25日 最新版本 Sublime Text 4 Build 4121/4126,按步骤操作!
第一步:
X64版本
4157415656575553B828210000
替换为 33C0FEC0C3575553B828210000
X86版本55535756B8AC200000
替换为 33C0FEC0C3AC200000
第二步:6C6963656E73652E7375626C696D6568712E636F6D
替换为 7375626C696D6568712E6C6F63616C686F73740000
第三步:替换exe后用以下注册码激活
1 | ----- BEGIN LICENSE ----- |
C:\Windows\System32\Drivers\etc
添加下面两行:
1 | ----- BEGIN LICENSE ----- |
今天玩了下python,发现有些库下载不下来,然后找了些国内的仓库地址:
requests
库没成功,用的豆瓣的链接: https://pan.baidu.com/s/10AJmO-ScBeTbtT8qGVcKtQ 提取码: uphi
链接: https://pan.baidu.com/s/1avzPjiEF72G06jAJ0_fK4A 提取码: iina
链接: https://pan.baidu.com/s/1LrTd8d-JVbOaEOikgzWrzw 提取码: hc2g
链接: https://pan.baidu.com/s/12mKLNnijyaJb_iW55LN0VQ 提取码: irz2
]]>最近发现Docker的相同容器多了几个,这样就会多出好多无用的镜像,所以需要删除,查了下none镜像产生的原因:
有时候重新构建镜像的时候,该镜像正在被某容器使用中,那么在重新构建同名同版本镜像后,docker保留原来的镜像,即容器还是用原来的,除非重启。
那么原来的镜像名称变成NONE,TAG也成了NONE
看到这里就想到应该是之前的定时脚本引起的,,,
命令:
列出所有的镜像
1 >docker images -a
列出所有的容器
1 >docker ps -a
列出所有的容器 ID
1 >docker ps -a -q
1 >docker stop $(docker ps -a -q)
1 >docker rmi $(docker images | grep "<none>" | awk "{print $3}")PS:说明
docker images | grep "<none>"
查询所有为<none>
的镜像awk "{print $3}"
输出第三列
1 docker rm $(docker ps -a -q)
IMAGE ID
:
1 docker images -q
1 docker rmi $(docker images -q)
关于Navicat的破解激活相信使用数据库的都会使用到,前段时间换了公司,之后又是一波安装软件,然后网上找破解教程,找半天,然后下次用的时候又没有,这就很尴尬,于是想着把教程记录下来,激活软件也记录下!!!而且这次发现了 GitHub
上面有两款开源的激活软件,这就很nice!:huaji:
直接在Navicat的官网进行下载
选择自己需要的产品点击【免费试用】即可下载软件!
两种激活软件,原理应该是一样,只是操作流程不一样
4)
可以定制属于自己名称的秘钥Generate
,将新生成的 code
copy到刚刚的软件激活页面,OK了!PS:第一种没有界面,直接在cmd中进行操作;第二中有对应的图像,一目了然
注意杀毒软件的屏蔽!!!!
附加:百度网盘链接 提取码: hwep
最新破解文件 提取码: v867
]]>本文的Nginx主要是使用Docker安装,所以着重讲解容器版Nginx安装SSL过程,当然普通的Nginx安装也适用,都是通用的!
腾讯云:
直接点击下载:
目录如下:
阿里云:(PS:没有使用阿里云 😰 )
阿里云Nginx安装文档
首先创建文件夹备用
1 | mkdir /data/nginx/logs /data/nginx/www /data/nginx/ssl /data/nginx/conf |
将Nginx文件夹中的两个文件上传到服务器/data/nginx/ssl
将Nginx容器中的配置文件cp出来
1 | docker cp nginx:/etc/nginx/nginx.conf /data/nginx/conf/nginx.conf |
填写配置文件
1 | -- 配置SSL |
PS:可以参考腾讯云NginxSSL配置
启动Nginx
1 | docker run -d -p 80:80 -p 443:443 --name nginx \ |
PS:
/data/nginx/www
中可自定义页面
1 | /** |
PS:
@Around
的作用
虽然Around功能强大,但通常需要在线程安全的环境下使用。因此,如果使用普通的Before、AfterReturing增强方法就可以解决的事情,就没有必要使用Around增强处理了。
]]>最近遇到了好几个网友出现
MySQL
数据库勒索事件,本人前两天也遇到,事情大概这样子的:
大早上起来,打开网站,发现500错误,这就很尴尬了,感觉查看日志发现怪事,数据库找不到了(PS:心里很慌,原因之前就想着备份,一直给忘了)打开数据库真的不见了,多了一个不知道的库,里面有一张WARNING表,内容全是英文(PS:额,看不懂),去翻译,彻底惊呆了!比特币勒索(PS:心里一万个小马跑过)
接着百度发现在17年的时候又一波比特币勒索事件,好多数据库都被黑了,要求花钱赎回......这件事情让我认识到数据库备份的重要性,哎,赶紧重新搞一套出来,幸亏博文都有备份,哈哈...
由于本人使用的是
docker
部署的MySQL
,所以去百度命令
1 docker exec -it mysql mysqldump -uroot -p123456 databases > /backup/data_`date +%Y%m%d%H%M%S`.sql;
拿着命令去跑了下,可以备份,完美
想着不能每天手动处理吧,然后就做了一个定时任务,每天备份一次,不说了,上操作:
1 | # mysql 为安装mysql的docker |
docker_name
:MySQL
容器名称data_dir
:保存路径--all-databases
:表示所有的数据库都备份,可以备份固定的数据库,直接替换就可以了-mtime +7
: 按照文件的更改时间来查找文件,+7表示文件更改时间距现在7天以前;如果是 -mmin +7表示文件更改时间距现在7分钟以前-exec rm -rf {}
; 表示执行一段shell命令,exec选项后面跟随着所要执行的命令或脚本,然后是一对儿{},一个空格和一个,最后是一个分号2.在定时任务中添加新的任务规则,crontab -e
,将下面命令写入并报存:wq
1 | 0 0 * * * /bin/sh /data/mysql_bak.sh >> /data/logs/mysql_bak.log 2>&1 |
学习是一种进步态度,笔记是一种成长的动作
]]>Spring Boot
启用事务:1.开启事务管理
在启动类上面加事务管理注解:
1 >@EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />2.事务注解详解
在service方法上面添加@Transactional
注解
@Transactional
注解失效说明数据库引擎中MyIsam
不支持事务,必须是InnnoDB
引擎
@Transactional
所注解的方法只有是public
才起作用
@Transactional
所注解的方法所在的类,必须注解@Service
或@Component
等
需要调用该方法,且需要支持事务特性的调用方是在 @Transactional
所在的类的外面。注意:类内部的其他方法调用这个注解了@Transactional
的方法,事务是不会起作用的。
@Transactional注解事务范围,并不是所有异常都可以进行数据回滚,他只有是RuntimeException
类及其子类(中文称为:运行时异常/unchecked
异常/未检异常)异常的时候才会进行数据回滚。简单的说@Transactional
注解只有抛出RuntimeException
类及其子类异常(中文称为:运行时异常/unchecked异常/未检异常)才能回滚,其他的所有异常都不行,当然出现Error
的时候也是会回滚的
如果希望一般的异常也能触发事务回滚,需要在注解了@Transactional的方法上,将@Transactional回滚参数设为:
1 >@Transactional(rollbackFor=Exception.class)手动回滚事务:可以在service层方法的
catch
加
1 >TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
]]>待补充…….
使用的`docker`安装`MySQL`,版本`8.0.12`
docker
的MySQL
容器:使用命令
docker exec -it mysql /bin/bash
MySQL
里面:
1
2 >root@1f88cc6f4013:/# mysql -u root -p
>Enter password:
1 mysql> select host,user,authentication_string from mysql.user;
host: 允许用户登录的ip‘位置’%表示可以远程;
user:当前数据库的用户名;
authentication_string: 用户密码(后面有提到此字段);
重点
1
2 >use mysql;
>update user set authentication_string='' where user='root';
- 使用ALTER修改root用户密码,方法为 ALTER user ‘root‘@’localhost’ IDENTIFIED BY ‘新密码’:
1
2 >ALTER user 'root'@'localhost' IDENTIFIED BY '123456';
>ALTER user 'root'@'%' IDENTIFIED BY '123456'修改成功,退出重启容器,重新连接就可以了
PS:MySQL8.0
以后使用的秘钥插件是caching_sha2_password
,以前使用的都是mysql_native_password
,所以有可能出现Navicat
链接失败的错误,可以使用如下命令:
]]>
1
2 ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '新密码';
FLUSH PRIVILEGES;
今天写项目的时候发现实体对象中的字段有
Date
类型的,这样进行直接输出来的格式不是前端需要,每次都需要使用工具类进行转化,感觉很麻烦!而且在前端进行传递对应字段是Date
类型的值得时候,也不好出来(PS:自己前端比较渣),在测试接口的时候,使用postman发现其也是没办法传Date
类型的参数,当然也是有传递时间戳的方式,但是测试而已没必要那么麻烦,所以就在网上查阅资料,找到了对应的Jackson
相关配置!
SpringBoot JSON工具包默认是Jackson,只需要引入spring-boot-starter-web依赖包,自动引入相应依赖包
之前我们都是直接进行格式化日期,然后输出才是前端需要的(图1,详情输出\图2新增参数报错)
jackson支持的时间格式:
"yyyy-MM-dd'T'HH:mm:ss.SSSZ" "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" "EEE, dd MMM yyyy HH:mm:ss zzz" "yyyy-MM-dd"
类型倒是挺丰富的,但就是没有常用的yyyy-MM-dd HH:mm:ss格式
1.可以直接在字段上加上注解:@JsonFormat( pattern=”yyyy-MM-dd HH:mm:ss”)
直接在application.yml文件中添加如下配置:
1 | spring: |
上面配置对应的properties文件版本:
1 | #jackson相关配置 |
]]>不需要使用工具类转化;
今天启动项目的时候注意到Spring Boot的启动日志图案,感觉不太好看,想到之前浏览过一篇文章# 新年彩蛋:Spring Boot自定义Banner,介绍了Spring Boot启动Banner可以使用自定义图案,学习下,随手记录在此:
上图就是Spring Boot 正常启动Banner
的效果
`Banner`是`SpringBoot`框架一个特色的部分,其设计的目的无非就是一个框架的标识,其中包含了版本号、框架名称等内容,既然`SpringBoot`为我们提供了这个模块,它肯定也是可以更换的这也是`Spring`开源框架的设计理念。
简单创建一个`SpringBoot`框架:在Spring Boot工程的`/src/main/resources`目录下创建一个`banner.txt`文件,然后将ASCII字符画复制进去,就能替换默认的banner了,比如上图中的输出,就采用了下面的`banner.txt`内容:
永不宕机佛祖
1 | ${AnsiColor.BRIGHT_YELLOW} |
从上面的内容中可以看到,还使用了一些属性设置:
${AnsiColor.BRIGHT_RED}
:设置控制台中输出内容的颜色${application.version}
:用来获取MANIFEST.MF
文件中的版本号${application.formatted-version}
:格式化后的${application.version}
版本信息${spring-boot.version}
:Spring Boot的版本号${spring-boot.formatted-version}
:格式化后的${spring-boot.version}
版本信息如果让我们手工的来编辑这些字符画,显然是一件非常困难的差事。所以,我们可以借助下面这些工具,轻松地根据文字或图片来生成用于Banner输出的字符画。
1 | ${AnsiColor.BRIGHT_RED} ! 天地山青 ${AnsiColor.BRIGHT_YELLOW} ! |
1 | ${AnsiColor.BRIGHT_YELLOW} |
在项目开发中,不可避免会遇到跨域问题!本文简述下遇到跨域解决办法:
首先,我们需要了解一下一个URL是怎么组成的:
// 协议 + 域名(子域名 + 主域名) + 端口号 + 资源地址
1 | http: + // + www.baidu.com + :8080/ |
只要协议,子域名,主域名,端口号这四项组成部分中有一项不同,就可以认为是不同的域,不同的域之间互相访问资源,就被称之为跨域。
随着前后端分离开发的越来越普及,会经常遇到跨域的问题,比如用ajax从`http://localhost:8080/test.html`页面向后台接口`http://localhost:8888/user/list`发起请求,由于两个url端口不同,所以属于跨域,在console打印台会报No 'Access-Control-Allow-Origin' header is present on the requested resource
前端使用ajax,将dataType设置成jsonp,这里不做太多说明!
重点通过 跨域访问技术CORS Cross-Origin Resource Sharing来解决的,具体的实现原理不做深入的探究,目的是解决跨域问题~
在Spring Boot 中给我们提供了一个注解@CrossOrigin
来实现跨域,这个注解可以实现方法级别的细粒度的跨域控制。我们可以在类或者方法上添加该注解,如果在类上添加该注解,该类下的所有接口都可以通过跨域访问,如果在方法上添加注解,那么仅仅只限于加注解的方法可以访问。
1 | @RestController |
这里可以通过实现WebMvcConfigurer
接口中的addCorsMappings()
方法来实现跨域。
1 | @Configuration |
我们可以通过实现Fiter接口在请求中添加一些Header来解决跨域的问题:
1 | @Component |
如果我们在项目中使用了Nginx,可以在Nginx中添加以下的配置来解决跨域(原理其实和Filter类似,只不过把活儿丢给了运维🤔)
1 | location / { |
关于网站页脚展示网站的运行时间问题:
直接将下面代码粘贴在页脚就行(Solo管理)
1 | <span id="sitetime"></span> |
crond服务未启动
crontab不是Linux内核的功能,而是依赖一个crond服务,这个服务可以启动当然也可以停止。如果停止了就无法执行任何定时任务了,解决的方法是打开它:
service crond start
如果提示crond命令不存在,可能被误删除了,CentOS下可以通过这个命令重新安装:
yum -y install crontabs
权限问题
比如:脚本没有x执行权限,解决方法:
增加执行权限,或者用bash abc.sh的方法执行(chmod 744 test.sh)
Linux系统中,每个用户的角色和权限划分的很细致也很严格,每个文件(目录)都设有访问许可权限,利用这种机制来决定某个用户通过某种方式对文件(目录)进行读、写、执行等操作。
操作文件或目录的用户,有3种不同类型:文件所有者、群组用户、其他用户。最高位表示文件所有者的权限值,中间位表示群组用户的权限值,最低位则表示其他用户的权限值,所以,chmod 777中,三个数字7分别对应上面三种用户,权限值都为7。
文件或目录的权限又分为3种:只读、只写、可执行。
权限 | 权限数值 | 二进制 | 具体作用 |
---|---|---|---|
r | 4 | 00000100 | read,读取。当前用户可以读取文件内容,当前用户可以浏览目录。 |
w | 2 | 00000010 | write,写入。当前用户可以新增或修改文件内容,当前用户可以删除、移动目录或目录内文件。 |
x | 1 | 00000001 | execute,执行。当前用户可以执行文件,当前用户可以进入目录。 |
依照上面的表格,权限组合就是对应权限值求和,如下:
7 = 4 + 2 + 1 读写运行权限
5 = 4 + 1 读和运行权限
4 = 4 只读权限
因此,大家也就明白了 chmod 744 filename 命令的含义了。
路径问题
有的命令在shell中执行正常,但是在crontab执行却总是失败。有可能是因为crontab使用的sh未正确识别路径,比如:
以root身份登录shell后执行一个/data/test.sh,进入到data目录,只要执行 ./test.sh
就可以了。
但是在crontab中,就会找不到这个脚本,必须写完整:/data/test.sh
时差问题
因为服务器与客户端时差问题,所以crontab的时间以服务器时间为准。
变量问题
有时候命令中含有变量,但crontab执行时却没有,也会造成执行失败。
crontab默认是不加载变量的,不要假定cron知道所需要的特殊环境,它其实并不知道。
所以你要保证在shelll脚本中提供所有必要的路径和环境变量,除了一些自动设置的全局变量
1 | cat test.sh |
* 3 * * * /bin/sh /data/test.sh >> /data/test.log 2>&1