2019 读书记录

2019 年没有系统性阅读,只是把早先想看的书,看到什么感兴趣就直接看了,也没有目的性,有政治的,有历史的,也有专业书,还有小说。

原来是想在 2019 年 12 月底来整理这篇文章的,大纲已经列好,但是一直躺在了草稿里面,现在是 2020 年年底的时候突然想起来,就着大纲,以及今年在重新整理自己的笔记之后,重新回顾一下自己的 2019 。

之间几年的读书笔记:

通识

言论的边界

五星推荐,这本书是我关于言论自由进行认知的第一本启蒙书,之后陆陆续续看过的书,《言论的边界》,《九人》,《牛津通识读本:托克维尔》,《大法官说了算 - 美国司法观察笔记》,《牛津通识读本 - 法哲学》,《美国宪政历程 影响美国的 25 个司法大案》,《美国国会》,《联邦党人文集》,《美国政党与选举》大多也能够进行关联。

关于言论自由的边界,从美国建国之初到今天,虽然都一个基本的共识,但细节部分仍然在不断地被完善。

说自由会带来混乱的人,必定是没有了解过美国的宪政历程,关于言论自由的方方面面,不对言论进行事先的限制是多少人花了多少代时间争取到的权利。纽约时报诉沙利文案中的法院意见,「在自由辩论中,错误意见不可避免,如果自由表达要找到赖以生存的呼吸空间,就必须保护错误意见。」

而对言论的事后审查则多多少少走过一些弯路,其中霍姆斯大法官在其中扮演着重要的角色,他首先使用了,对于事后的言论审查中,只有「明显而即刻的危险」才能进行惩罚。而另一位影响深远的勒尼德汉德法官认为,只有直接煽动暴力的言论才应当受到惩罚,仅只是批评政府政策的言论,无论是委婉的还是露骨的,都不能惩罚。意见的自由表达是权力的最终来源。

而对于媒体,法院的判决更加强了媒体的重要性。人民有权获得政府作为或者不作为的全部信息,公共意见的交流和传播是遏制腐败最有效的方法。而媒体正是在这样的过程中扮演着至关重要的角色。布伦南大法官这样写到,「对公共事务的讨论不应当受到阻碍,即使那些对政府和公职人员的言论包含激烈、苛刻、有时甚至令人不悦的言论,这些言论也不应该被阻止」。

关于言论自由和隐私权上,虽然最高法院已经将媒体的言论自由对象扩展到了公众人物,包括社会名流,影视明星等等,但对于普通人,是否可以声称自己的隐私权而状告媒体,最高法院虽然至今依然偏向媒体,但无疑隐私权已经作为一个实际上的权利被不断的提起。言论和出版的自由对于保障人类的自由来说,无疑是重要的;但对于构建一个健康的社会,这不是唯一需要考量的。

虽然宪法给予了媒体,以及从事媒体行业的记者以巨大的权利,但遇到记者需要保守信息来源,以及法庭命令其公布信息来源的时候,也会遇到冲突。记者拥有的只是「有限特权」,在遇到具体的案例时,依然有可能失败。

书中的最后几章在讲述思想的自由。思想自由和言论自由同样重要,政府不得仅仅以为社会中认为某种思想令人厌恶就禁止人们对这种思想的表达。霍姆斯大法官有一句话流传甚广,那些为我们所痛恨的思想,同样自由。

论美国的民主

托克维尔在非常年轻的时候根据自己的游历就写出了如此观察细致的巨作。这可能是我系统的观察美国而看的第一本书,但也正是这本书为我在 2020 年看其他相关著作的时候打下了基础,托克维尔以非常平和的心态写下了对美国的观察,既没有过分的赞美,也没有恶意的贬损。更甚至比谁都看出美国制度中可能存在的问题。

《论美国的民主》分成了上下两部,托克维尔在上部中描绘了他看到的美国现状,包括风土人情,制度设计,司法权利,出版自由,公民自治等等,下部中将自己的观察和思考进一步的凝练,描述了美国的思想潮流,以及宗教信仰,科学艺术,文学戏剧等等方面可能的影响。也进一步的预言了在这样的社会环境中对生活在其中的人可能带来的改表。

艾希曼在耶路撒冷

汉娜阿伦特在这本书中提出了著名的「平庸之恶」,一个人不是想要做极恶的事情才会实施暴行,恶行的发生可能仅仅是一个人不思考,这个人不是道德败坏或者心理变态,而只是屈服了自己的惰性,放弃了思考,选择了无条件的顺从,这样的人同样会做出极恶的事情。

在一个高度技术化和官僚化的世界里,无声无息地铲除「多余」的民众同时又不引起公众的道德愤怒,可能会变得司空见惯。

阅读这本书,最大的收获便是获知了汉娜阿伦特这位作者,在编辑完其中文的维基百科后,又陆陆续续的看了她的其他作品,《极权主义的起源》,《过去与未来之间》。

牛津通识读本 美国国会

美国制度中的三权分立,行政,立法和司法,最早看得是司法分支的《牛津通识读本 最高法院》,觉得牛津通识读本这一系列的书,短小精炼,不失为入门首选。所以就在 Kindle 上看完了这一部。这一本书也相对完整的介绍了国内的诞生,竞选方式,参众两院的运行方式,以及国会在整个美国的位置,以及和其他两个分支的相互制约,最后一章讲述了国会大厦。相较于白宫,国会大厦和最高院出现在影视剧作中的画面没有那么多,但围绕在议会大厦周围的建筑群还是别具特色的。

论法的精神

孟德斯鸠花了 20 年时间撰写的著作,在这部书中提出了著名的三权分立,但孟德斯鸠所说远不止于此,在政治理论方面提出了:

  • 关于政治分类,将政体划分为共和、君主、专制,原则分别是品德、荣誉和恐惧
  • 分权说和君主立宪,行政、立法和司法分权理论,相互制衡,才能保障公民的自由
  • 地理说,地理环境,尤其是气候、土壤等因素与人民的性格、感情相关,法律应该考虑这些因素

在法律理论上,反对酷刑、主张量刑适度,刑罚应该富有教化意义,舆论威慑可以作为阻止犯罪的工具,只惩罚行为,不惩罚思想、语言。在经济理论上,认为私有财产是人类的自然权利,主张工业和贸易。这是一本让我看完之后第一次感受到我们确确实实活在了过去思想家的思考中。孟德斯鸠通过对历史和当时社会的观察便能思考出如此超前的观点,着实令人钦佩。

极权主义的起源

汉娜阿伦特的另外一部作品,对我来说有些艰深,如果不了解那个时代,或者不了解阿伦特本人,第一遍读的时候对我来说很难提炼出阿伦特的观点,虽然这几个名词已经被无数的作品反复的解释过了,但要真正的去定义什么是「极权主义」,还略微有些难度,而阿伦特说,极权主义就是将世界上的所有界限抹平,不考虑人的多元性,将人认为是集体中的一员来对待。极权主义不同于以往的专制,在 20 世纪对人类历史造成的巨大的伤害,但对其的研究,这是我看的第一本。

传记

  • 命运:文在寅自传,影视作品中对卢武铉的描述要多过这一位现任的韩国总统,所以去年准备去韩国之前比较匆忙的快速过了一遍这位总统的自传,如果说是自传的话,可能还不准确,书中虽然记叙了文在寅小时候到青年时期的事,但自文在寅遇到卢武铉之后便有了大量和卢的往事。
  • 永不放弃,Trump 的传记
  • 永久档案,斯诺登的传记

摄影

一般也不会特意去找摄影相关的书籍,只是看到有对应的题材,在看其他类别的书的中间带着看了一下。

  • 摄影的精神
  • 完美构图

日本

谋划着想要去日本走一走,所以大量的阅读了相关的书籍,只不过到现在也依然只能说对日本的了解九牛一毛,对日本近现代史的了解稍微多一些。

  • 战后日本经济史
  • 日本制造
  • 日本史
  • 现代日本
  • 日本人的活法
  • 走遍日本
  • 日本
  • [[静观日本]]
  • 日本论
  • 日本常识
  • 菊与刀
  • 游牧东京

其他

  • 悲剧人偶
  • 82 年生的金智英
  • [[通过知识获得解放]]
  • 日本漫画为什么有趣
  • 而已集
  • 韩国电影
  • 一网打尽:贝佐斯与亚马逊时代
  • Lonely Planet 韩国
  • Lonely Planet 台湾
  • 从陈桥到厓山,宋朝历史
  • 韩国研究二十年
  • 畅游首尔
  • 人生的意义
  • 重新发现宋朝
  • 大韩民国史
  • 这就是韩国

2019-12-31 reading , book , douban

关于 Rime 输入法思考和整理

很多年前写过一篇 Rime 配置,这几年陆陆续续,用过搜狗,也用回过 Rime,还试过 Linux 上其他的输入法,但是最后终于在 2019 年完全的切换到 Rime 下,并且经过一系列的折腾发现 Rime 的能力远远超过我的想象。其实输入法是一个及其有依赖性的工具,输入习惯,输入的常用词等等,都累积在了输入法中。这些年输入法引入了云同步功能,但我倒是觉得带来的便利性甚至不如其带来的缺点,唯一的好处是记录输入习惯,和常用词库,但是实际上可能带来的问题更多,比如

  • 隐私没有得到有效保护,有没有发现有的时候输入了某些词,可能一会儿广告就来了
  • 安全问题,甚至有过报道输入法将用户输入的内容不加密传输到服务器,假如真的能够拿到一个用户的输入历史记录,真的可以查到这个用户非常多的习惯,甚至有可能会泄漏用户密码等等
  • 我在弃用搜狗的时候甚至不允许用户删除自己的云端词库,这一部分原本属于用户的资产,无形中变成了一个商业公司的资产

多输入法

就像 Rime 自己说的那样,Rime 是一个输入方案的合集。所以只要有合适的输入方案,就可以输入对应的语言,从方言,到世界其他各国的语言,Rime 都可以轻松的实现,甚至可以借助 Rime 来实现一套自己的输入编码方案,比如官网教学中的一套输入中文大写数字的输入方案,定义 schema, 字典,然后就实现了一套中文大写数字的输入法。

安全

Rime 输入法不会联网,所有的配置都以文本的方式保存在本地,虽然一定成都上削弱了 Rime 的便捷性,但实际上就加强了 Rime 的安全性。只要保证自己的电脑安全,所有的数据都是安全的。

完全可配置

Rime 输入法从外观到词库,到甚至到输入方案都可以自己定制。熟悉 Rime 的机制后,完全可以使用 Git 将一整套配置管理起来。你可以使用一个输入法输入多国语言,Ctrl + ` 切换 Schema,非常轻松可以实现。

自定义短语

输入的候选词也能够配置,

可以在 custom_phrase.txt 中配置:

地平线:黎明时分	hzd	2

这样在输入法中输入 hzd 就可以直接出现前面的短语,只要在配置方案中配置了 custom_phrase,就可以使用。

custom_phrase:
  db_class: stabledb
  dictionary: ""
  enable_completion: false
  enable_sentence: false
  initial_quality: 1
  user_dict: custom_phrase

然后在 translators 中加入该词典:

translators:
  - xxxxx
  - "table_translator@custom_phrase"

2019-12-30 rime , linux , input-method , fcitx

终端的输入历史以及管理

配置终端历史文件地址

bash 会将所有终端的输入历史保存在 ~/.bash_history 中,同理,zsh 会保存在 ~/.zsh_history 中。

zsh 使用 HISTFILE 变量来管理保存的 zsh_history 文件,默认一般保存在 ~/.zsh_history 中。

配置 zsh 中记录的终端历史条数

在 zsh 的配置中:1

说明:

  • HISTSIZE 是终端历史中保存的最大行数 2
  • SAVEHIST 是保存在历史文件中的最大行数
  1. https://unix.stackexchange.com/a/273863/115007

    HISTFILE=”$HOME/.zsh_history” HISTSIZE=10000000 SAVEHIST=10000000 setopt BANG_HIST # Treat the ‘!’ character specially during expansion. setopt EXTENDED_HISTORY # Write the history file in the “:start:elapsed;command” format. setopt INC_APPEND_HISTORY # Write to the history file immediately, not when the shell exits. setopt SHARE_HISTORY # Share history between all sessions. setopt HIST_EXPIRE_DUPS_FIRST # Expire duplicate entries first when trimming history. setopt HIST_IGNORE_DUPS # Don’t record an entry that was just recorded again. setopt HIST_IGNORE_ALL_DUPS # Delete old recorded entry if new entry is a duplicate. setopt HIST_FIND_NO_DUPS # Do not display a line previously found. setopt HIST_IGNORE_SPACE # Don’t record an entry starting with a space. setopt HIST_SAVE_NO_DUPS # Don’t write duplicate entries in the history file. setopt HIST_REDUCE_BLANKS # Remove superfluous blanks before recording entry. setopt HIST_VERIFY # Don’t execute immediately upon history expansion. setopt HIST_BEEP # Beep when accessing nonexistent history. 

  2. http://zsh.sourceforge.net/Doc/Release/Parameters.html#index-HISTSIZE 


2019-12-26 terminal , history , clear , command , cli

重拾 Wikipedia

时隔这么多年再来说 Wikipedia, 甚至显得有些落伍,毕竟这个时代,甚至说起 Wikipedia 就像是再谈论一个上世纪的东西,但是其实想想 Wikipedia 诞生也快靠近 20 年。就像之前发出的感慨一样,这些年用了很多的应用,很多的网络服务,但仔细想想曾经 Web2.0 所谓的人人可贡献,就我自己的感受来说,那些我曾经大量使用的服务, 在 Google+ 上分享的内容,在 Google Reader 上分享的文章,都随着关闭烟消云散,反而是我曾经不经意间在 Wikipedia 写下的词条,上传的照片这么多年还依然在,并且曾经的词条在经过更多人修订过后更加可信,更加丰富。

所以上个月开始,我不再给豆瓣添加词条,而把所有的条目内容整理到了 Wikipedia,真的开始了解 Wikipedia 之后我才发现, Wikipedia 的世界看似无秩序,但实际对词条的撰写有着比较严格的要求。

想象在这个世界里每个人都能自由分享所有人类知识的总和。 – 吉米·威尔士

在此,推荐每一个人都去尝试编辑一下 Wikipedia,在这里你能学到尊重他人,你能学到如何搜寻信息,你能学会如何书写可靠来源,你能学到如何保持中立,能学到如何培养版权意识。

可靠来源

在编辑 Wikipedia 的时候最常被编辑提及的就是缺乏“可靠来源”,在编辑每个词条内容的时候,每一句写下的话都要有可靠来源,Wikipedia 毕竟不是个人抒发观点的地方,所以给每句话写下可靠的引用。而可靠的引用又指的什么呢,不能是用户产生的内容,我被拒的其中一个原因就是,引用过多的豆瓣信息,而我也知道豆瓣的条目都是用户贡献的。

The fact that the info is not user-generated is certainly encouraging.

说实话要做到“正确”是非常困难的,但是要做到 Fact-Check 还是比较容易的。某一些内容可能放在现在是正确的,但是可能过一段时间就不正确了,所以 Wikipedia 要时时刻刻准备着被修改。

关于版权保护

之所以上面提到为什么编辑 Wikipedia 会学习到版权保护,那是因为,每一个编辑都需要为自己写下的每一句话负责,严禁复制粘贴原文,并且引用的图片必须要做到合理使用,在上传每一个文件时,Wikipedia 都会列出非常详细的说明。也就是在这个过程中,我知道了维基百科对版权重视。

版权法保护的是思想的表现形式(比如一段介绍“相对论”的文字敘述),而不是思想本身(比如相对论本身這一理論)

创建新条目

像我一样的新手,有些时候看到中文 Wikipedia 没有一个词条,便直接新建了一个词条,不妨用新词条向导来新建,如果不清楚该词条是否满足关注度等等要求,甚至可以先编辑 Draft 然后让其他编辑来审核,或者来一同修改。

reference


2019-12-26 wikipedia , wiki , knowledge

kswapd0 占用 99% 的 CPU

这两天办公用的 Ubuntu 总是在内存将用尽的时候死机,所有的界面卡住不同,进 tty 用 top 看一眼后发现 kswapd0 这个进程竟然占用 90~100% 左右的 CPU,网上一查说这个进程是用来管理虚拟内存的。

一般的 Linux 都会有 RAM,swap, 和 EXT4 这几个部分,EXT4 分区就是用来存放一般的文件,可以在机械硬盘或者 SSD 上划分出 ext4 分区来保存文件,相对 RAM(内存)来说要稍微慢一些,RAM 就是日常所说的内存,用来做程序运行时的高速缓存,而 SWAP 是交换分区,一般在物理内存比较小的机器上会划分一块物理磁盘来作为 swap 分区。

swap top

swap 分区是一款虚拟的 RAM,一般在 HDD/SSD 上,当运行比较小的物理内存时,可能经常缺内存,那么系统就会使用 swap 分区,将物理内存中的内容搬迁到 swap 分区中暂存。当可用物理内存比较小的时候,kswapd0 进程就会将相对比较不常用的程序移动到 swap 分区中,这个时候就可能造成这些程序比较卡顿。加入又一台机器物流内存是 4G,而要运行一个需要 5G RAM 的游戏,那么至少有 1G 的内存会放到 swap 分区中。kswapd0 移动内存的过程就会造成 CPU 的大量使用。要解决这个问题有这样几个方式。

在网络上有些教程会提醒用户,当计算机使用了比较大的物理内存的时候,就不需要划分 swap 分区了,一定程度上这种说法也没问题,但是如果 disable swap 分区后,如果遇到程序内存泄漏或者物理内存用完的情况,那么整个计算机就会进入卡死状态。所以推荐还是不要禁用 swap,虽然 swap 分区不会有 RAM 那么快,但是当 RAM 耗尽的时候,swap 分区可以提供一层额外的保护。如果对 swap 比较感兴趣,可以参考这里 阅读更多的相关知识。1

在 askubuntu 的回答中有句话说的很好

Just realize that the SWAP is a failsafe for the RAM.

failsafe 及时发生了故障也要有故障保护机制,否则系统就会 fail.

配置 /etc/sysctl.conf

配置 /etc/sysctl.conf 文件,来告诉 kswapd0 进程只有当物理内存用尽的时候再做移动内存的事情

echo vm.swappiness=0 | sudo tee -a /etc/sysctl.conf

这里 0 表示物理内存还剩余的百分比,这个值的取值范围是 0-100,配置 0 也就意味着只有当没有物理内存可用时再执行 kswapd0.

关闭一些不用的程序

降低或者减少物理内存的使用,也可以降低 kswapd0 过于频繁执行的问题。

购买更多物理内存

一劳永逸的解决内存短缺的问题。也就是换内存条。

手动添加 swap 分区

因为工作的 Ubuntu 是从一盘机械硬盘复制到 SSD 中的,当时 swap 分区没有划分,如果要手动添加 swap 分区可以如下操作。

根据详细的步骤,分区,以及格式化参考之前的文章

编辑 /etc/fstab 文件,增加新的 swap 分区:

sudo vi /etc/fstab

增加类似这样一行:

UUID=735b3be3-779c-4d21-a944-b033225f3ab4 none   swap    sw      0       0

分区的 UUID,可以通过 blkid 命令来获取:

sudo blkid /dev/sda2

假设 /dev/sda2 就是划分的新的 swap 分区。


2019-12-23 swap , linux , memory

优秀开源项目推荐:BookStack 一款高效简单的 Wiki 系统

已经忘记了什么契机,BookStack 就存在了我的 ToDo List 中,一直想要找一款能够快速记录一些常用到的,但是却不太容易记住的东西,原来的方法就是用 markdown + git, 或者如果手边有笔记本,或者手机就记录在 WizNote 中,但这样一来很多内容都散布在各个不同的软件或者应用中,以前也整理过 gitbook 但是一直没有那么系统的想要说整理出一本书这样,所以一直依赖还是用这个博客,记录点点滴滴的学习笔记

Info

GitHub 地址:

开源协议: MIT license

安装依赖条件:

  • PHP & Laravel
  • MySQL

浏览 BookStack 的官网 主页上列举了一些 Feature 都是一些非常贴心的功能。

  • 完整的权限管理
  • WYSIWYG 编辑器,避免了每次我都要 Vim 打开,也使得图片等其他资源管理比较方便
  • 搜索,索引,不用每次都要我 rg 先搜索下在编辑了
  • 可订制化,从名字到 Logo,到注册选项,虽然我个人用并用不到
  • Optional Markdown Editor,这一点非常舒服
  • Multi-lingual, 其实我自己用 EN 就够了,不过有时间也给这个项目翻译一下好了
  • 支持不同格式的文件导出,赞

这一些功能集合到一起就像是整合版本的 GitBook,支持多人编辑,还有权限管理,完美的解决了使用 markdown + git 管理当文件太多的情况下管理的不便。基于这些理由,所以就在我的 NAS 上,用 Docker 搞了一个。目前先自己用着记录一些暂时未整理好,或者暂时不能公开的内容吧。

安装的过程也非常简单了,官网给出的教程非常详细了,请参考官网.

QNAP 上使用 Docker 安装

首先开启 QNAP 上的 MySQL 服务,然后给 bookstack 新建一个数据库 bookstack,并且给 bookstack 这个数据单独创建一个用户 bookstack。然后因为我的 Docker 配置的网关地址是 10.0.3.1 所以就写了宿主机的地址。然后就不用上面提到的教程单独给 BookStack 开一个 MySQL 实例了,共享宿主机的 MySQL 就行了。

version: "2"
services:
  bookstack:
	image: linuxserver/bookstack
	container_name: bookstack
	environment:
	  - PUID=1000
	  - PGID=1000
	  - DB_HOST=10.0.3.1:3306
	  - DB_USER=bookstack
	  - DB_PASS=<password>
	  - DB_DATABASE=bookstack
	volumes:
	  - /share/Container/bookstack_config:/config
	ports:
	  - 6875:80
	restart: unless-stopped

然后等待启动即可。

如果遇到这个问题:

nc: getaddrinfo: Name does not resolve

可以参考 stackoverflow


2019-12-20 qnap , qnap-tutorial , github , bookstack , open-source , self-host , wiki

BT 站点收集整理

不以引进正版为理由的打击盗版都是文化审查。

BT

eztv

  • http://eztv.ag

rss

  • http://eztv.ag/ezrss.xml

btbtt

广告很多,记得开启浏览器的广告屏蔽插件,AdBlock, uBlock。

  • https://www.btbtt.us/

unblock

  • https://unblockit.ca/

The Pirate Bay

  • https://www.thepiratebay.org/

RARBG Torrents

  • https://rarbg.is/torrents.php

PT

Private tracker

最后如果有很多 PT 站,推荐 PT Plugin Plus 这个浏览器扩展来管理。

字幕


2019-12-17 bt , bittorrent , movie , piratebay , copyright

Spring AOP 笔记

Spring AOP 的几个常用的使用场景:

  • 事务,transaction management
  • 日志,logging
  • 安全,security

AOP 提供了不同于 OOP 的另一种全新的软件架构思考方式。

Spring 中有两种方式来使用 AOP

  • schema-based approach, 基于 XML 方式配置
  • @Aspect annotation approach, 基于注解

Terms

一些关键性的术语:

  • join-point: a point during the execution of a program, in spring AOP always represents a method execution
  • pointcut:is a predicate or expression that matches join-point
  • advice: actions taken by aspect at a particular join-point, is associated with a pointcut expression and runs at any join point matched by the pointcut
  • weaving: linking aspects with other application types or objects to create an advised object.

基于上面的认知,知道 join-point 可以认为是方法调用的时刻,所以 Spring 中有 5 种类型的 Advice 时机:

  • Before advice, 方法执行前(无法阻止方法执行,除非抛出异常)
  • After returning advice, 正常方法(无异常)返回后执行
  • After throwing advice, 抛出异常时执行
  • After advice, 不管方法正常或者抛出异常后执行
  • Around advice, 方法调用前后

Spring 中 AOP 实现原理

Spring 中 AOP 的实现主要是通过 JDK [[动态代理]]和 [[CGLIB]] 动态代理完成。1可以通过注解 [[Spring @EnableAspectJAutoProxy]] 的参数来指定。

  • JDK 动态代理通过反射来代理类,要求被代理的类实现一个接口,JDK 动态代理的核心是 InvocationHandlerProxy
  • 如果目标类没有实现接口,Spring 会采用 CGLIB 来动态代理目标类,CGLIB 是一个代码生成的类库,可以在运行时动态生成类的子类,CGLIB 通过继承方式代理,所以如果一个类被标记为 final,是无法通过 CGLIB 来做动态代理的

Spring Boot 中使用 AOP

引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

在启动类上使用 [[Spring @EnableAspectJAutoProxy]] 注解,但其实如果不配置该注解,spring.aop.auto 属性也是默认开启的。

Spring Boot 中指定 CGLIB 实现 AOP。

在注解中指定:

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {}

或者配置属性:

spring.aop.proxy-target-class=true

Maven

具体的版本可以自行搜索使用。

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-support</artifactId>
	<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aop</artifactId>
	<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjrt</artifactId>
	<version>1.6.11</version>
</dependency>
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.6.11</version>
</dependency>

相关的注解

@Aspect 注解将 Java 类定义为切面,使用 @Pointcut 定义切点。

在不同的位置切入,可以使用 @Before, @After, @AfterReturning, @Around, @AfterThrowing 等等。

Pointcut Designators

如何定义切点,以及切点表达式的编写是学习 AOP 的一个重点。

Pointcut expression 由一个 pointcut designator(PCD) 开头,来告诉 Spring 什么时候匹配。Spring 支持很多个 pointcut designators ,最常见的就是 execution 了。

execution

matching method execution join points

匹配某一个特定方法:

@Pointcut("execution(public String info.einverne.FooDao.get(Long))")

假如要匹配 FooDao 中所有方法:

@Pointcut("execution(* info.einverne.FooDao.*(..))")

第一个* 匹配所有的返回值,(..) 表示匹配任意数量的参数。

within

limits matching to join points within certain types

使用 within 也能够达到上面的效果,将类型限定到 FooDao

@Pointcut("within(info.einverne.springboot.demo.dao.FooDao)")
public void logWithClass(JoinPoint jp) {}

或者匹配某个包下所有

@Pointcut("within(info.einverne.springboot.demo.dao..*)")
public void logWithPackage(JoinPoint jp) {}

this and target

  • this - limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type
  • target - limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type

this 匹配 bean reference 是给定类型的实例,target 匹配 target Object 是给定类型的实例。this 适用于 Spring AOP 创建 CGLIB-based proxy, target 适用于 JDK-based proxy.

@Pointcut("target(info.einverne.springboot.demo.dao.BaseDao)")
public void logBaseDao(JoinPoint jp) {}

@Pointcut("this(info.einverne.springboot.demo.dao.FooDao)")
public void logThis(JoinPoint jp) {}

args

limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types

匹配特定方法参数

// 匹配方法参数是 Long 的方法
@Pointcut("args(Long)")
public void argsMatchLong() {}

args 后面加类名,表示入参是该类的方法。

@target

limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type

@Pointcut("@target(org.springframework.stereotype.Repository)")

@args

limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given type(s)

// 匹配所有使用了 SomeCustomAnnotation 注解的参数的方法
@Pointcut("@args(info.einverne.SomeCustomAnnotation)")
public void args() {}

@args 需要接注解的类名,表示方法运行时入参标注了指定的注解。

@within

limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP)

@Pointcut("@within(org.springframework.stereotype.Repository)")

等于:

@Pointcut("within(@org.springframework.stereotype.Repository *)")

@target@within 的区别:Spring AOP 基于 dynamic proxies, 它仅仅提供了 public, non-static 方法执行的 interception. 而使用 CGLIB proxies, 你可以 intercept package-scoped, non-static 方法。然而 AspectJ 甚至可以 intercept 方法的调用(而不仅仅是方法的执行),member field access (静态或者非静态),constructor call/execution, static class initialisation 等等。2

  • @within() is matched statically, requiring the corresponding annotation type to have only the CLASS retention
  • @target() is matched at runtime, requiring the same to have the RUNTIME retention

@annotation

@annotation 可以用来表示被注解引用的时机。

limits matching to join points where the subject of the join point (method being executed in Spring AOP) has the given annotation

比如自定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecutionTime {
}

那在定义 Pointcut 时可以使用:

@Around("@annotation(com.package.LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {}

Order

可以使用 @Order 来指定先后执行顺序。

execution expression

execution 在使用时有自己的语法规则:

execution(modifiers-pattern? return-type-pattern declaring-type-pattern?method-name-pattern(param-pattern) throws-pattern?)

          public/private       void/String/...         com.xxxx.SomeClass     .saveUser                     throws *Exception

带问号的可以省略,其他可以支持正则。

组合使用

所有的 Pointcut 之间都可以使用 &&||! 来连接。

@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}

@Pointcut("within(com.xyz.someapp.trading..*)")
private void inTrading() {}

@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}

代码地址:https://github.com/einverne/thrift-swift-demo/tree/master/spring-boot-demo

reference


2019-12-12 spring , spring-boot , spring-framework , aop , spring-mvc

wp-cli 使用

Install

wget https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
sudo chmod +x wp-cli.phar
sudo ln -s /var/www/www.einverne.info/html/wp-cli.phar /usr/local/bin/wp

wp --info

To run any command with WP CLI, you must be in the public directory of your WordPress instance installed.

Usage

Check version

wp core version

Check update

wp core check-update

Update

sudo -u www-data wp core update
sudo -u www-data wp core update-db

Plugin

Check plugins

wp plugin list
sudo -u www-data wp plugin deactivate wordpress-seo
sudo -u www-data wp plugin uninstall wordpress-seo
sudo -u www-data wp plugin update --all

Theme

wp theme search twentyfourteen
sudo -u www-data wp theme install twentyfourteen
sudo -u www-data wp theme activate twentyfourteen
sudo -u www-data wp theme update twentyfourteen
sudo -u www-data wp theme update --all
wp theme list
sudo -u www-data wp theme activate twentyseventeen
sudo -u www-data wp theme uninstall twentyfourteen

Post

wp post list
wp post create --post_type=post --post_title='A sample post'
wp post update 123 --post_status=draft
wp post delete 123

reference


2019-12-11 wordpress , cli , linux , command

专业医学信息网站整理

前两天鼻炎又犯了,去过很多次医院,也去过不同的医院,有说是慢性鼻炎,有说是过敏性鼻炎,这次检查说是鼻窦炎,总之就是鼻炎了。也尝试过各种药,各种滴鼻液,各种喷雾剂,各种洗鼻水,总是能缓解一段时间,然后到秋冬季节就又会差很多。也不知道是不是环境导致,毕竟大学毕业后还没有在不同城市生活过。所以上一次去了医院之后就在想,对于有些病,或者有些病理信息,是不是我可以在互联网上获得一些信息,然后自我调理,而这两天正好看到 Twitter 上有人提起,所以就整理一下。

医学知识

专业的医学或健康知识可信获取信息源:

  1. 默沙东诊疗手册 https://www.msdmanuals.com/
  2. 维基百科 https://wikipedia.org
  3. WHO;世界卫生组织 (World Health Organization) http://who.int
  4. 腾讯医典(与 WebMD 联合) https://baike.qq.com/
  5. 美国食品药品监督管理局:https://fda.gov

CDC

美国疾病控制与预防中心 https://www.cdc.gov/ 世界各国的 CDC https://en.wikipedia.org/wiki/CDC

NIH

美国国立卫生研究院 https://www.nih.gov/

糖尿病 https://dtc.ucsf.edu/zh-hans/

延伸阅读


2019-12-08 mediacal , collection

电子书

最近文章

  • 读书是否是唯一重要的事? 不久之前和朋友约去了趟植物园,聊起读书是否是第一要务的时候产生了一些分歧,关于是否要去学习如何学习这一件事情产生了一些分歧。我站在的立场是读书是必须的,而我朋友则认为在有限的时间里面实践要优先于读书。而关于要不要学习如何学习这一件事情,他仍然坚持自己的实践而非去了解如何学习。
  • Android 上的 RIME 输入法 trime 同文输入法使用 早之前就已经在 Linux 和 macOS 上配置了 RIME 并且一直使用到现在,但是在主力的 Android 上从最早的触宝输入法,后来切换成 Gboard,日常使用倒是没什么大问题,就是有一些词总是需要翻页才能找到,这让我非常不爽,就想手机上能不能用 RIME,于是就有了这篇水文。
  • Obsidian 使用篇一:使用 markdown-clipper 全文保存网页 之前使用整理 Evernote 代替品 的时候就提出了我自己的一个需求,就是有一个完善的 Web Clip 系统,Evernote 和 WizNote 都做的比较不错。但 Obsidian 并没有提供类似的工具,不过幸好 Obsidian 使用 Markdown 来管理文档,这样的开放程度使得我可以寻找一个将网页变为 Markdown 的浏览器扩展就能做到。
  • 使用了半年 macOS 之后 我又回到了 Linux 的怀抱 我在使用了半年 macOS 之后,又回到了 Linux 的怀抱,虽然 macOS 有其自身的优势,我也不否认 macOS 系统上软件生态的友好,但我发现即使我将日常开发主力机器装回到 Linux,也没有丧失操作系统的便捷性和易用性。这或许和我下意识的只使用跨平台的软件有关,并且最长使用的软件几乎都是一套快捷键。
  • 重置 macOS S.M.C 和 NVRAM 今天用得好好的电脑突然三次黑屏,两次发生在早上刚刚使用的时候,一次发生在晚上回家之后。所以一怒之下就直接上官网联系了 Apple Support,但是也不知道是不是我直接登录的 .com 网站,在我提交了 Support 之后一分钟一个外国小哥打了电话过来,我一下子没反应过来,只能用着不那么熟练的英语开始了 macOS 修复之路。