archlinux安装指南

Arch Linux是什么

Arch Linux是一款滚动更新的GNU/Linux发行版,致力于提供最新的稳定版软件,相较于其他图形界面的发行版,Arch Linux只能在命令行界面进行安装,且默认不带任何桌面环境。(非特别说明,后文将GNU/Linux简称为Linux)

Arch Linux有什么优势

相较于其他基于Debian或者Redhat的发行版,Arch Linux提供了自己的包管理器——pacman,而且支持用户软件仓库(AUR)。基于这个特性,大大增加了Arch Linux系统原生软件的数量。而良好的软件生态正是Arch Linux的一大利器。

Arch Linux的安装

对于大部分非专业人士而言,习惯了Windows的图形界面之后,突然使用命令行操作软件往往变得非常困难,更不用说安装系统了,尽管Arch Linux拥有详细的由社区维护的安装指南Wiki,但也因此劝退了一些习惯了windows之后想要尝试linux系统的新手,基于此,本文特地将自己安装Arch Linux过程中的经历以及出现的问题做个说明,作为Arch Linux安装wiki的补充,不失为帮助更多的人能够接触到linux的一种方法(windows固然很优秀,但自由/开源软件才是软件开发的未来)。

1、获取最新版镜像

官方下载网站:https://archlinux.org/download/

2、制作启动盘

准备一个空的U盘,和一台已安装好linux系统的机器,使用如下命令进行写盘:

1
sudo dd bs=4MB if=你的安装镜像路径 of=你的U盘路径 status=progress

of参数通常是/dev/sdb,其中sdb是你的U盘路径,注意不要带数字后缀,类似sdb1。另外,写盘之前必须使用umount命令卸载的U盘(如果被自动挂载的话)。

3、从U盘启动,开始安装

需要注意的是,为了确保可以正常从启动盘引导,需要关闭主板的安全启动选项(可以在安装完成之后重新打开),关闭方式因主板厂商不同而有所差异。具体如何关闭可以自行百度。

重启主机之后,进入BIOS或者直接通过功能键选择启动顺序,将USB设备作为第一启动项,启动之后选择Arch Linux install medium进入系统安装界面。

4、验证启动模式

如果使用的是UEFI模式,则可以使用如下命令验证:

1
ls /sys/firmware/efi/efivars

如果命令正常打印出一系列文件和目录,且没有任何错误,则表明当前是以UEFI模式启动。否则则是以BIOS或者CMS模式启动。

5、联网

一般来说,我们可以直接插入网线联网,或者使用外置网卡。比如手机的USB网络共享。

6、调整时区

使用timedatectl可以查看并设置时区。

1
2
3
4
5
6
# 启用ntp(网络时间协议)
timedatectl set-ntp true
# 列出当前可用的时区
timedatectl list-timezones
# 设置选择的时区
timedatectl set-timezone Asia/Shanghai

7、磁盘分区

查看当前可用磁盘

1
fdisk -l

选择将要使用的磁盘,假设是/dev/sda,执行如下命令

1
fdisk /dev/sda

进入分区界面之后,使用m查看具体的分区指令。一般步骤如下:

1
2
# 创建分区表

迪迦奥特曼带给我的思考

第49集——奥特之星

剧情演说

在这一集中,大古追踪穿越时空回到过去寻找怪兽的外星人查利加,和他一同回到了圆古英二导演还在世时的1965年。随后,大古在不经意间误闯进了圆古英二导演的摄影棚,临时充当了本该由长野(博)担任的场务一职,令人没想到的是,看到一身胜利队队服的大古,愣是没有一个人发现大古其实并不是长野(博),当然,在现实中,大古的扮演者就叫做长野博,这也是导演刻意给我们开的一个玩笑,不算剧情bug。

离开摄影棚之后,大古找到了查利加,并质问他的真正意图,但查利加并不想告诉大古,反而向他发起了攻击,愤怒的大古拿出武器击退了查利加,而查利加也随之消失不见了。

接下来的剧情中,还出现了真正的长野和大古见面的场景,不得不说,这种虚实相间的拍摄手法的确能够触动观众的情绪,带给观众一种说不出的感动。

镜头给到初代奥特曼剧本创作人的金城哲夫,此时的他还在苦苦构思如何写出一个另圆古一导演(圆古英二导演的儿子)满意的剧本,但反复改了8次还是不能让导演满意,最后一次勉强让导演满意了,可是随后的一句“这故事有趣吗”再次让金城陷入了重写剧本的境遇。正当金城绞尽脑汁想剧本的时候,圆古英二导演主动和他聊起了剧本创作遇到的困难,并将自己的一颗红宝石拿给金城看,还告诉他这是外星人送给他的,而自己和这个外星人是好朋友。原来圆古英二导演曾经遇到过一个来自M78星云的奥特曼,而当时这个奥特曼正将一只怪兽沉到湖底,后来,为了表示对地球人的友好,他将一颗奥特之星送给了圆古英二导演,并告诉他这颗奥特之星会对他产生帮助。然而他们之间的对话被躲在门外的查利加偷听到了,而圆古英二提到的被奥特曼沉到湖底的怪兽正是他所要寻找的亚那加基。

得到消息的查利加立刻赶到龙森湖救出被封印的亚那加基,并命令怪兽破坏人类的住所。怪兽的出现引起了大古的注意,为了阻止怪兽继续破坏地球,大古拿起神光棒,变身成了迪迦奥特曼,和怪兽展开了搏斗,谁知怪兽丝毫不落下风,反而挟持住了迪迦,并吸取他的能量,眼看迪迦就要被打败,一旁观战的圆古英二导演凭借自己的意念竟然也变成了一道光,朝着怪兽发动攻击,击退了正在吸取迪迦能量的怪兽之后,这道光居然变成了奥特曼,并将迪迦被吸取的能量重新输入到了他的体内。得到能量后的迪迦再次回到战斗,和另一个奥特曼同时发动光线技能,一举打败了怪兽。而操纵怪兽的查利加则放出一句“后会有期”之后就逃跑了。圆古英二看着两位巨人,说道“我们需要一个英雄,金城,英雄是必要的“。而此时的金城正在埋头书写关于英雄的故事。

最后一幕是圆古英二导演拿着金城写出来的奥特英雄的剧本开始了初代奥特曼剧集的拍摄。之后的事情我们也都知道了。从此,奥特曼宇宙开启了崭新的一页。

观后感

作为一部平成年代的开山之作,《迪迦奥特曼》与圆谷制作的上一部奥特曼剧集中间已经整整间隔了16年,而16年前观看奥特曼的小孩子很多都已经长大成人,主打孩童向的奥特曼似乎已经和他们渐行渐远。或许正是基于这一考量,《迪迦奥特曼》从一开始面向的就是成人观众。还记得小时候看迪迦,更多的是关注奥特曼打小怪兽,至于为什么要打怪兽以及人类未来的命运则完全不加考虑,只知道这个奥特曼长得特别好看,还能变身成其他两种形态,真的太酷了。当我们长大后再次打开《迪迦奥特曼》,我们突然发现这个奥特曼似乎有点不一样,曾经我们心中无敌的奥特曼似乎也在慢慢变得不那么无敌,原来,奥特曼也是人变的,他也会受伤,甚至奥特曼还需要我们人类的帮助才能打败那些怪兽,不仅是奥特曼,那些被他打的怪兽也变了,似乎没有天生就是怪兽的,都是因为人类肆意破坏地球,盲目发展高科技,才使我们的身边多了这么多怪兽,而来自外太空的外星人之所以要侵略地球,毁灭人类,只是因为他们想活下去,而奥特曼既是光,同时也是人类,为了人类的生存,奥特曼必须站出来打倒怪兽。即使面对一个根本赢不了的敌人,奥特曼也要以人类的身份战斗到最后一刻。很难想象一向主打孩童向的特摄片居然能在短短的一集20多分钟的剧情中注入如此崇高的立意,也让长大后的我们知道原来《迪迦奥特曼》根本就不是一部面向孩童的特摄片。我们都误会他了。作为连接昭和和平成奥特曼的桥梁,迪迦在奥特之星中完成了这一神圣的交接仪式,当我们看到初代奥特曼和迪迦在圆谷英二导演面前握手时,我们知道,随着圆谷英二导演的去世以及平成时代的到来,未来的奥特曼系列将开启新的篇章,而这一切都将从迪迦奥特曼开始。——谨以此文献给我们的童年,永远的迪迦奥特曼。

我们为什么喜欢迪迦奥特曼——迪迦被禁播后的感想

谁能想到,2021年的9月24日,这个看似格外普通的日子,却也是陪伴我们童年一路走来的特摄剧《迪迦奥特曼》上映25周年,居然在一片举报声中无声无息的全网下架,对于奥迷来说,这是一个值得被记住的日子,而对于期盼国产动漫能早日赶超国际水平的国人而言,这次的禁令不能不让我们心寒,或许我们希望的那一天永远都不会到来了。

迪迦带给我们的感动

我们总说迪迦代表的是,其实我们喜欢迪迦的真正原因是我们信仰迪迦带给我们的希望与黑暗势力抗争到底的决心和勇气

在迪迦奥特曼中,人类不停的被外来生物入侵,作为承担保护地球责任的地球防卫队——GUTS组织,在正面对抗体型庞大的怪兽和拥有高科技的外星人时根本也是无能为力,如果没有迪迦奥特曼的帮助,人类早就被毁灭几十次了,即使在面对没有丝毫破坏力的奇杰拉时,人类的表现也是令人失望的,因为人类宁愿活在虚无缥缈的幻境中,去逃避现实中的种种不堪,也不愿为了人类的未来去努力拼搏,积极进取,人类到底是一种软弱的生物。即便是迪迦之前的超古代奥特战士在看到这样的人类之后也只能任其毁灭,因为他们不是人类,对于浩瀚的宇宙而言,人类也只是其中渺小的一份子,如同大部分宇宙生物一样,自生自灭就是最好的生存法则。但是,这一切在迪迦出现后就完全变了,作为人类的大古机缘巧合之下获得了迪迦留在地球的巨人身躯,从一个普通的人类变成了保护地球的奥特战士,一次次以人类的身份比肩神明,为了人类的生存义无反顾的同凶残的巨型怪兽和邪恶的外星生物进行战斗,甚至在人类对奥特曼失去信任,反而帮助外星人诋毁奥特曼时也坚持维护人类免受灭顶之灾,如果这样的迪迦奥特曼都不能称之为英雄,并受到人类的尊敬和爱戴,那这人间或许本就该成为奇杰拉的墓场,这样的文明即使毁灭也无妨。迪迦奥特曼不仅带给我们希望,还教会了我们很多的道理,其中不乏对于人与人,人与科技,人与环境,人与地球,人与宇宙的深度思考,而这些看似宏大的人生理念,却在迪迦奥特曼中得到了很好的诠释。如果对于这样的一部作品都要冠以暴力的称谓而加以禁播,可想而知我们的社会中还有多少看似人模狗样,实则满肚子鸡鸣狗盗,阳奉阴违之人。这样的人无论是作为个人还是父母,对于人类的进步和社会的稳定都是一大阻碍,因为在他们眼中任何表达美好事物的东西都会被他们过滤在外,而最终留给他们的只能是暴力,于人于己,这样的人都是社会的毒瘤,而为他们提供滋生土壤的人也难辞其咎。此时此刻,迪迦或许只是暂时的离开,但他的精神会永远留在我们心中。迪迦奥特曼保护人类,更是为了保护我们人类赖以生存的美丽地球。

怎么在不开机的情况下让树莓派自动连接上wifi

树莓派

什么是树莓派

树莓派(Raspberry Pi)是一款只有巴掌大小的微型计算机开发板,别看它体积小,但它确实是一个功能完备的计算机,可以做绝大部分我们个人电脑(PC)可以做的事情。而且功耗更小。

树莓派支持的系统

目前除了官方主推的Raspberry Pi OS ,还有其他第三方系统,比如基于ubuntu和debian的arm发行版,理论上可以安装微软的Windows,但不推荐,毕竟Windows是收费软件,而且对系统资源相较于linux有更高的要求,作为一款主打轻量级的开发版,Windows显然太重了。

树莓派怎么联网

  1. 树莓派自带支持802.11n的以太网网卡,因此可以直接使用支持RJ45的标准网口连接上网;
  2. 从Model 3开始,树莓派自带支持802.11n的无线网卡,对于无法通过网线直连的场合,wifi变成了我们唯一的途径。

下面具体就怎么在不开机的情况下配置无线连接做个介绍,方便大家在无线环境中使用树莓派。

不得不说,树莓派自带的无线网卡的性能确实很弱,对于大流量的场景不推荐使用,最好自备外接无线网卡。

安装系统

本教程基于Raspberry Pi OS,其他发行版系统可能不适用。下载地址:https://downloads.raspberrypi.org/raspios_armhf/images/raspios_armhf-2021-05-28/2021-05-07-raspios-buster-armhf.zip。

制作启动盘

在linux下面可以使用如下命令

1
sudo dd if=/home/your_home/raspberryOS.iso of=/dev/sd{x} bs=4MB status=progress

注意,使用上面的命令时必须确保of参数对应的值是你要写入的SD卡,一般以sd开头,if参数对应的是你下载解压后的原始镜像地址。

windows上的启动盘制作请自行百度

写入wifi配置

1
2
3
4
5
6
7
8
9
country=CN #这里是我们位于的国家的缩写
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1 #更新现有配置
network={
ssid="你的wifi名称"
scan_ssid=1
psk="你的wifi密码"
key_mgmt=WPA-PSK
}

将该文件命名为wpa_supplicant.conf,并将其保存到制作好的启动盘的最外层的boot目录下。

允许ssh访问

在启动盘的最外层的boot目录下创建ssh文件,不需要向里面写入内容。

最后将制作好的sd卡插入到树莓派中启动即可自动连接wifi,然后我们在路由器管理页面查看已连接设备中就能看到带有raspberry pi标签的设备了,找出其ip地址,尝试下使用ssh连接并管理树莓派,如果一切顺利的话,你将可以通过远程访问来管理你的树莓派设备了。

使用静态ip地址

由于树莓派内置了DHCP服务,所以,当我们每次连接上wifi时可能会被分配到不同的ip地址,这显然不利于配置外部对于树莓派后台服务的访问,因此,我们在连接上wifi之后,可以给树莓派配置一个静态ip。

打开/etc/dhcpcd.conf文件,向里面追加如下配置

1
2
3
4
interface wlan0
static ip_address=192.168.0.193/24
static routers=192.168.0.1
static domain_name_servers=192.168.0.1 192.168.1.1

其中wlan0是我们无线网卡的系统名称,后面的ip_address则是我们自定义的ip地址,routers是我们的路由,domain_name_server是我们的DNS地址,多个DNS的场合使用空格隔开。

shiro的权限管理之访问许可(permission)

1. Apache Shiro中的permission是什么

Shiro 将 Permission 定义为代表某个明确行为或动作的语句。 它是应用程序中原始功能的声明,仅此而已。 权限是安全策略中最低级别的结构,它们仅明确定义应用程序可以做什么,而不会描述谁可以做这些动作。(和subject即认证主体无关)

2. permission体现在哪些地方

  • 打开一个文件

  • 访问/user/list路径下的网页

  • 打印文档

  • 删除某个用户

定义“谁”(用户)被允许做“什么”(权限)是一种以某种方式为用户分配权限的运用,这始终由应用程序的数据模型完成,并且可能因应用程序而异。

例如,权限可以归入一个角色,并且该角色可以与一个或多个用户对象相关联。 或者一些应用程序可以有一个用户组,一个组可以分配一个角色,通过传递关联意味着该组中的所有用户都被隐式授予角色中的权限。

向用户授予权限的方式有多种变化——应用程序根据应用程序要求确定如何对此进行建模。

3. 通配符权限

上面的权限示例中,“打开文件”,“查看‘用户/列表’网页”等都是有效的权限声明。 然而,在计算上解释这些自然语言字符串并确定是否允许用户执行该行为将是非常困难的。因此,为了实现易于处理但仍然可读的权限语句,Shiro 提供了强大而直观的权限语法,我们称之为通配符权限。

3.1 简单应用

假设您想保护对公司打印机的访问,以便某些人可以打印到特定的打印机,而其他人可以查询当前队列中的作业。

一种极其简单的方法是授予用户“queryPrinter”权限。 然后您可以通过调用来检查用户是否具有 queryPrinter 权限:

1
subject.isPermitted("queryPrinter")

这等价于:

1
subject.isPermitted( new WildcardPermission("queryPrinter") )

这还不够,简单权限字符串可能适用于简单的应用程序,但它需要您拥有“printPrinter”、“queryPrinter”、“managePrinter”等权限。您也可以使用通配符授予用户“*”权限(授予此权限) 构造其名称),这意味着它们拥有整个应用程序的所有权限。

但是使用这种方法,不能只说用户拥有“所有打印机权限”。 因此,通配符权限支持多级权限。

3.2 多个部件

通配符权限支持多个级别或部分的概念。 例如,您可以通过授予用户权限来重构前面的简单示例

1
printer:query

本示例中的冒号是一个特殊字符,用于分隔权限字符串中的下一部分。

在此示例中,第一部分是正在操作的域(打印机),第二部分是正在执行的操作(查询)。 上面的其他示例将更改为:

1
2
printer:print
printer:manage

可以使用的部件数量没有限制,因此就可以在您的应用程序中使用它的方式而言,这取决于您的想象力。

3.2.1 多个值

每个部分可以包含多个值。 因此,与其同时授予用户“printer:print”和“printer:query”权限,您还可以简单地授予他们一个权限:

1
printer:print,query

这使他们能够打印和查询打印机。 并且由于他们被授予了这两个操作,您可以检查用户是否有能力通过调用来查询打印机:

1
subject.isPermitted("printer:query")

这将返回 true。

3.2.2 全部的值

如果您想授予用户特定部分的所有值怎么办? 这样做比手动列出每个值更方便。 同样,基于通配符,我们可以做到这一点。 如果打印机域有 3 个可能的操作(查询、打印和管理),则:

1
printer:query,print,manage

可以简化为:

1
printer:*

然后,对“printer:XXX”的任何权限检查都将返回 true。 以这种方式使用通配符比显式列出操作更有效,因为如果稍后向应用程序添加新操作,则无需更新在该部分使用通配符的权限。

最后,还可以在通配符权限字符串的任何部分使用通配符标记。 例如,如果您想授予用户跨所有域(不仅仅是打印机)的“查看”操作,您可以授予以下权限:

1
*:view

然后对“foo:view”的任何权限检查都将返回true。

未完待续:https://shiro.apache.org/permissions.html

分布式搜索引擎-elasticsearch

1. elatsticsearch(简写为es)是什么

它是一个实时的分布式、开源的全文本查询和分析引擎,基于java语言开发。

2. es可以做什么

它可以在SPA(single page application-单页应用)中被使用,为用户提供高速的数据查询和分析。且原生支持java应用。

3. 主要特性

它有如下几个主要特性:

  • 可拓展性强,支持高达千兆字节(GB)的数据(不管是结构性的还是非结构性的);
  • 可以作为类似MongoDB和RavenDB等文档存储数据库的替代品;
  • 使用非规范化来提高搜索性能;
  • 企业级的搜索引擎,被大型公司广泛使用;
  • 开源工具,且使用Apache许可证。

4. 关键性的概念

  • 节点:表示单个运行的es实例,es可以在单台服务器上运行多个实例,这完全取决于服务器的物理内存和处理器能力;
  • 集群:一个或者多个节点的集合,在逻辑上提供了集中的数据索引和查询能力,通常表现在横跨多个节点获取完整数据的能力。
  • 索引:不同类型的文档和他们的属性的集合。在关系型数据库中,索引一般用于提升查询性能。
  • 文档:一个使用JSON格式定义的字段集合,而这些字段定义了数据查询的规则。每个文档都属于一种类型并驻留在索引中,每个文档都与一个称为 UID 的唯一标识符相关联。
  • 分片:索引被横向拆分成不同的分片,这意味着每个分片包含了所有的文档属性,但包含的 JSON 对象数量少于索引。水平分离使分片成为一个独立的节点,且可以存储在任何节点,主分片是索引的原始水平部分,然后将这些主分片复制到副本分片中。
  • 副本:es允许用户创建索引和分片的副本。复制不仅有助于在发生故障时提高数据的可用性,而且通过在这些副本中执行并行搜索操作来提高搜索性能。

5. es的优势

  • es 是基于 Java 开发的,这使得它几乎可以兼容所有平台;
  • es 是实时的,也就是说一秒后添加的文档就可以在这个引擎中搜索了;
  • es 是分布式的,这使得它可以轻松地在任何大型组织中扩展和集成。
  • 使用 es 中的网关概念可以轻松创建完整备份。
  • 与 Apache Solr 相比,在 es 中处理多租户非常容易。
  • es 使用 JSON 对象作为响应,这使得使用大量不同的编程语言调用 es 服务器成为可能。
  • es 支持几乎所有文档类型,除了那些不支持文本渲染的文档类型。

6. es的缺点

  • 与 Apache Solr 不同,es 在处理请求和响应数据方面没有多语言支持(仅支持JSON),而Apache Solr 可以支持 CSV、XML 和 JSON 格式。
  • 偶尔,es 会出现**脑裂(Split brain)**的问题。(什么是脑裂)

7. es和关系数据库之间的区别

在 es 中,索引类似于 RDBMS(关系数据库管理系统)中的表。 每个表都是行的集合,就像 es 中的每个索引都是文档的集合一样。
下表给出了这些术语之间的直接比较 -

es RDBMS
集群 数据库
分片 分片
索引
字段
文档

php——使用pdo连接mysql数据库

PDO是什么

PDO(PHP Data Objects)拓展是一个轻量级、而且统一的接口,用来在PHP中访问数据库。

PDO提供了一个数据访问抽象层,这意味着,不管你使用的是什么数据库,你都可以使用相同的函数去执行查询并获取数据。

PDO不提供数据库抽象,无法消除不同数据库之间的语法差异,如果你想要做到做到这一点,你必须使用另外的抽象层去实现。

PDO连接MySQL数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 数据库主机地址
const DB_HOST = 'mysqldb';
// 数据库名称
const DB_NAME = 'default';
// 数据库用户名
const DB_USER = 'root';
// 数据库密码
const DB_PASSWORD = 'devstack';
// 连接对象
private $pdo = null;
// 格式化连接字符串
$conStr = sprintf("mysql:host=%s;dbname=%s", self::DB_HOST, self::DB_NAME);
try {
$this -> pdo = new PDO($conStr, self::DB_USER, self::DB_PASSWORD);
} catch (PDOException $e) {
echo $e -> getMessage();
}

使用pdo连接对象执行插入、更新或者删除

1
2
3
4
5
6
7
8
9
10
11
$sql = "insert into tasks (subject, start_date, end_date, description)
values ('%s', '%s', '%s', '%s');";
$insertStatement = "";
// 构造插入语句
// date("Y-m-d H:i:s")的输出格式为yyyy-mm-dd hh:mm:ss
for ($i = 0; $i < 10; $i++) {
$insertStatement .= sprintf($sql, "睡觉", date("Y-m-d H:i:s"), date("Y-m-d H:i:s"), "测试");
}
// 使用exec方法执行插入
$this -> pdo -> exec($insertStatement);
// 更新和删除都可以使用exec(),返回值为表的影响条数

使用pdo连接对象执行查询

1
2
3
4
5
6
$sql = "select * from tasks";
foreach ($this -> pdo -> query($sql) as $row) {
print $row['subject'] . "\t";
print $row['start_date'] . "\t";
print $row['end_date'] . "\n";
}

使用query方法可以执行查询操作。通过foreach遍历查询结果。

Spring框架中bean的前置处理和后置处理

Spring框架中Bean的创建过程如下:

Aware interfaces Callbacks in Bean Lifecycle

其中BeanPostProcessor接口包含两个执行过程,分别是在bean执行init方法之前和执行init方法之后执行。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Configuration
public class MyBeanPostProcessor implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("我在bean初始化前执行");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("我在bean初始化之后执行");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}

@Bean(name = "DBConnection", initMethod = "init", destroyMethod = "destroy")
public DatabaseConfig mysqlConnection() {
Properties properties = System.getProperties();
String url = properties.getProperty("url");
String driverName = properties.getProperty("driverName");
String userName = properties.getProperty("userName");
String password = properties.getProperty("password");
return new DatabaseConfig(driverName, url, userName, password);
}

}

上述代码新建了自定义的MyBeanPostProcessor,并实现了接口BeanPostProcessor中的两个接口(自带default方法),同时,还定义了一个名为DBConnection的bean,指定了初始化方法和销毁前执行的方法。

其中,DatabaseConfig的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Data
public class DatabaseConfig {

private static Connection connection;

private String driverName;
private String url;
private String userName;
private String password;

public DatabaseConfig(String driverName, String url, String userName, String password) {
this.driverName = driverName;
this.url = url;
this.userName = userName;
this.password = password;
System.out.println("构造连接参数...");
}
public void init() {
System.out.println("初始化数据库连接");
}
public void destroy() {
System.out.println("销毁数据库连接");
}
}

为了方便识别执行阶段,分别在构造器和init以及destroy方法中加入了文字说明。

最后的执行结果如下:

1
2
3
4
5
6
构造连接参数...
我在bean初始化前执行
初始化数据库连接
我在bean初始化之后执行
...
销毁数据库连接

由此可见,构造器内的代码是最先执行的,属于填充属性阶段(即Populate properties),接着执行MyBeanPostProcessor#postProcessBeforeInitialization方法,然后执行init方法,之后执行MyBeanPostProcessor#postProcessBeforeInitialization,最后在关闭应用时执行destroy方法。

Spring框架中bean的生命周期

使用@PostConstruct@PreDestroy注解在同一个配置类中执行bean的初始化前和销毁前的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
public class MyBeanPostProcessor {
@Bean
public String helloWorld() {
return "hello world";
}

@PostConstruct
public void init() {
System.out.println("我是初始化前执行的方法");
}

@PreDestroy
public void destroy() {
System.out.println("我是销毁前执行的方法");
}
}

上面的配置类中只定义了一个bean,且是字符串类型(方便举例),大部分情况下应该是返回一个对象。

使用@Bean(initMethod = "init", destroyMethod = "destroy")注解执行bean初始化前和销毁前的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
public class ConfigTest {
@Bean(initMethod = "init", destroyMethod = "destroy")
Test1 test1() {
return new Test1(); // 使用默认的无参构造器
}
}
// 定义Bean的内容,本质上是一个普通的java类
public class Test1 {
// 初始化前的执行方法
public void init() {
System.out.println("this is init method");
}
// 销毁前的执行方法
public void destroy() {
System.out.println("this is destroy method");
}
}