HackToTech

Hack To Technology

SpringBootで@Repositoryがついたクラスのメソッドから投げられた例外を変換して投げ直す

個人的な備忘録

レイヤードアーキテクチャでアプリケーションを作っていて、
賛否両論あると思うがドメイン層のバリデーションを作成時とDBからの再構成時にチェックしていて、
作成時は例外をそのまま投げたいが、再構成時はそうしたくないと思っていたので、表題のようなことをしたかった
(変なデータが入ったりしなければこんなことをしなくても良いが、特殊なケースで起こったりする為、モヤモヤしていたので調べていた)

例えば、↓みたいなクラスをドメインとして持っていて、 init でバリデーションを行っているとする
バリデーションに失敗した場合は、 DomainException をスローする

data class UserId(val value: UUID) {
    companion object {
        fun generate(): UserId {
            return UserId(UUID.randomUUID())
        }
    }
}

data class User(
    val id: UserId,
    val name: UserName,
) {
    companion object {
        fun of(
            name: UserName,
        ): User {
            return User(
                id = UserId.generate(),
                name = name,
            )
        }
    }
}

data class UserName(val value: String) {
    companion object {
        private const val MIN_LENGTH = 1
        private const val MAX_LENGTH = 256

        fun of(value: String): UserName {
            return UserName(value)
        }
    }

    init {
        if (MIN_LENGTH > value.length ||
            MAX_LENGTH < value.length) {
            throw DomainException(
                message = "ユーザ名は${MIN_LENGTH}以上${MAX_LENGTH}以下の長さである必要があります",
            )
        }
    }
}

リポジトリのインターフェースはとりあえず保存とID検索だけ用意する

interface UserRepository {
    fun findById(id: UserId): User
    fun save(user: User)
}

実装は以下

@Repository
class UserRepositoryImpl: UserRepository {
    private val user1 = UserEntity(
        id = UUID.fromString("a687b0bc-0887-483c-8851-4604657162c1"),
        name = "", // なんかしらの理由で元々入っていたとする
    )

    private val user2 = UserEntity(
        id = UUID.fromString("bb1304ea-8d78-4b99-b4d1-ce4000c1c9ab"),
        name = "テストユーザ",
    )

    private val map = ConcurrentHashMap(
        mapOf(
            Pair(
                user1.id,
                user1,
            ),
            Pair(
                user2.id,
                user2,
            ),
        )
    )
    override fun findById(id: UserId): User {
        return map.getOrElse(id.value) { throw RuntimeException("ユーザが存在しません") }
            .toUser()
    }

    override fun save(user: User) {
        val entity = UserEntity(
            id = user.id.value,
            name = user.name.toString(),
        )

        if(map.putIfAbsent(user.id.value, entity) != null) {
            throw RuntimeException("ユーザがすでに存在します")
        }
    }
}

今回はDBを使ってないが、DBを使った場合を想定して、DB用のモデルを用意する
ここでDBのモデルからドメインモデルの変換をするが、バリデーションに失敗した場合は DomainException がスローされる

data class UserEntity (
    val id: UUID,
    val name: String,
) {
    fun toUser(): User {
        return User(
            id = UserId(value = id),
            name = UserName(value = name),
        )
    }
}

モデルの変換をするのは @Repository のアノテーションがついたクラスなので、
AOPを使って @Repository 内のメソッドで DomainException が投げられたら DomainMappingException に変換して投げ直すことにした
あとはそれぞれの例外に応じて、例外ハンドラーを設定すればいい感じに作成時と再構成時で振る舞いを変えられる

@Aspect
@Component
class RepositoryExceptionTranslator {
    @AfterThrowing(value = "@within(org.springframework.stereotype.Repository)", throwing = "e")
    fun translate(e: Throwable) {
        throw when(e) {
            is DomainException -> DomainMappingException(e)
            else -> e
        }
    }
}

こういうときはやっぱりAOPが便利だなといった感想
試していたソースのコードは↓

github.com

/procに潜る

完全に個人的なメモなので、間違っている可能性あり
書いたっきりslack上で放置していたので、自分で後で見返せるように供養しておく

まあそもそもman読めよって話ではあるのだけれど潜るだけ潜ってみる https://man7.org/linux/man-pages/man5/procfs.5.html

/proc/<pid>/attr

このディレクトリの内容はセキュリティ関連の属性を設定するために読み書きできるファイル
SELinuxをサポートするために追加されたけど他のセキュリティモジュールをサポートすることを意図して作られた
このディレクトリはKernelがCONFIG_SECURITYで構成されている場合にのみ存在する

/proc/<pid>/attr/current

プロセスの現在のセキュリティ属性を表す

/proc/<pid>/attr/exec

後続のexecveでプロセスに割り当てる属性を表す

/proc/<pid>/attr/fscreate

open/mkdir/symlink/mknodへの後続の呼び出しによって作成されたファイルに割り当てる属性を表す

/proc/<pid>/attr/keycreate

プロセスがセキュリティコンテキストをこのファイルに書き込むと、その後に作成されるすべてのキーにこのコンテキストのラベルが付けられる
詳しくは下を読む
https://github.com/torvalds/linux/blob/master/Documentation/security/keys/core.rst

/proc/<pid>/attr/prev

最後のexecveの前のプロセスのセキュリティコンテキストが含まれている

/proc/<pid>/attr/socketcreate

プロセスがセキュリティコンテキストをこのファイルに書き込む場合、その後に作成されるすべてのソケットにこのコンテキストがラベル付けされる

/proc/<pid>/autogroup

プロセスのautogroup(タスクグループ)メンバーシップファイル
このファイルを使ってautogroupに割り当てられたcpuの帯域幅を変えられるらしい(nice値で+19(低優先度) ~ -20(高優先度)で制御できる)
https://man7.org/linux/man-pages/man7/sched.7.html

/proc/<pid>/auxv

実行時にプロセスに渡されるELFインタープリター情報

/proc/<pid>/cgroup

linux control groups
https://man7.org/linux/man-pages/man7/cgroups.7.html

/proc/<pid>/clear_refs

書き込み専用で、プロセスの少勇者のみが書き込み可能
smapsのアウトプットに表示されているページが参照しているビットをクリアする

/proc/<pid>/cmdline

実行しているコマンドの引数を表示

/proc/1 # cat cmdline
/sbin/tini--node/opt/src/server.js

/proc/<pid>/comm

プロセスに関連付けられたコマンド名を表示する
同じプロセス内の異なるスレッドは /proc/<pid>/comm/<tid>/comm を介してアクセスできる異なるcomm valuesを持つ場合がある

/proc/1 # cat comm
tini

coredump_filter

プロセスに対してコアダンプが実行された場合に、コアダンプファイルに書き込まれるメモリセグメントを制御できる
https://man7.org/linux/man-pages/man5/core.5.html

cpuset

そのプロセスを実行するために仕様できるCPUセットに関しての情報がある
https://man7.org/linux/man-pages/man7/cpuset.7.html

/proc/1 # cat cpuset
/docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d

cwd

現在の作業ディレクトリへのリンク
今回であれば /opt/src となる

/proc/1 # ls -l | grep cwd
lrwxrwxrwx    1 root     root             0 Feb 20 12:06 cwd -> /opt/src

environ

環境変数の値

/proc/1 # cat environ
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=bf7809b99cdcNODE_VERSION=15.8.0YARN_VERSION=1.22.5HOME=/root

exe

このプロセスの実行可能ファイルへのリンク

/proc/1 # ls -l | grep exe
lrwxrwxrwx    1 root     root             0 Feb 20 12:06 exe -> /sbin/tini

fd

全てのファイル記述子(FileDescriptors)を含むディレクトリ

fdinfo

ファイル記述子に関する情報を取得できる
内容は、ファイル記述子によって参照されるファイルのタイプによって異なる

pos: ファイルオフセットを示す10進数
flags: ファイルアクセスモードとファイルステータスフラグを表示する8進数(https://man7.org/linux/man-pages/man2/open.2.html)
mnt_id: マウントポイントのid(詳しくはmountinfo参照)

/proc/1/fdinfo # cat 0
pos:    0
flags:  0100002
mnt_id: 487

/proc/1/fd # ls -alh | grep "0 ->"
lrwx------    1 root     root          64 Feb 20 12:02 0 -> /dev/null

gid_map

プロセスpidのユーザー名前空間内のグループIDのマッピングを公開する(uid_mapも同様にユーザIDのマッピング)
https://man7.org/linux/man-pages/man7/user_namespaces.7.html

/proc/1 # cat gid_map
         0          0 4294967295

io

プロセスのI/Oの統計

rchar(characters read): このタスクによってストレージから読み取られたバイト数(terminal I/O等が含まれるので実際の物理ディスクが必要かどうかには影響されない(ページキャッシュからの読み取りで満たされている場合がある))
wchar(characters written): このタスクによってディスクに書き込まれた、または書き込まれるバイト数
syscr(read syscalls): 読み取りI/O操作数(要するにread/pread等のsyscallの回数)
syscw(write syscalls): 書き込みI/O操作数(要するにwrite/pwrite等のsyscallの回数)
read_bytes(bytes read): ストレージレイヤーからフェッチされたバイト数をカウントする
write_bytes(bytes write): ストレージレイヤーへ送信されたバイト数をカウントする
cancelled_write_bytes: ページキャッシュのトランケートによってこのプロセスが発生しなかったバイト数を表す(ちょっと怪しい)

/proc/1 # cat io
rchar: 68666
wchar: 46
syscr: 45
syscw: 5
read_bytes: 28672
write_bytes: 0
cancelled_write_bytes: 0

limits

プロセスの各リソース制限のソフト/ハード制限及び単位が表示される
https://man7.org/linux/man-pages/man2/getrlimit.2.html

/proc/1 # cat limits
Limit                     Soft Limit           Hard Limit           Units
Max cpu time              unlimited            unlimited            seconds
Max file size             unlimited            unlimited            bytes
Max data size             unlimited            unlimited            bytes
Max stack size            8388608              unlimited            bytes
Max core file size        0                    unlimited            bytes
Max resident set          unlimited            unlimited            bytes
Max processes             unlimited            unlimited            processes
Max open files            1048576              1048576              files
Max locked memory         65536                65536                bytes
Max address space         unlimited            unlimited            bytes
Max file locks            unlimited            unlimited            locks
Max pending signals       7418                 7418                 signals
Max msgqueue size         819200               819200               bytes
Max nice priority         0                    0
Max realtime priority     0                    0
Max realtime timeout      unlimited            unlimited            us

loginuid

プロセスのユーザID 4294967295はunsigned longで-1なので、デーモンとかだとデフォルトこれになるらしい
そのプロセスを誰が作ったかとか見るのに便利そうではある
https://prefetch.net/blog/2016/10/26/tracking-linux-uids-across-process-creation/

/proc/1 # cat loginuid
4294967295

map_files

メモリマップとファイルに対応するエントリが含まれている(https://man7.org/linux/man-pages/man2/mmap.2.html)
エントリは、メモリ領域の開始アドレスと終了アドレスのペアで名前付けされ、マップされたファイル自体へのシンボリックリンクとなる

/proc/1/map_files # ls -alh
total 0
dr-x------    2 root     root           0 Feb 20 12:06 .
dr-xr-xr-x    9 root     root           0 Feb 20 12:02 ..
lr--------    1 root     root          64 Feb 20 21:08 56272c442000-56272c444000 -> /sbin/tini
lr--------    1 root     root          64 Feb 20 21:08 56272c444000-56272c446000 -> /sbin/tini
lr--------    1 root     root          64 Feb 20 21:08 56272c446000-56272c448000 -> /sbin/tini
lr--------    1 root     root          64 Feb 20 21:08 56272c448000-56272c449000 -> /sbin/tini
lr--------    1 root     root          64 Feb 20 21:08 56272c449000-56272c44a000 -> /sbin/tini
lr--------    1 root     root          64 Feb 20 21:08 7f84b6128000-7f84b613d000 -> /lib/ld-musl-x86_64.so.1
lr--------    1 root     root          64 Feb 20 21:08 7f84b613d000-7f84b6184000 -> /lib/ld-musl-x86_64.so.1
lr--------    1 root     root          64 Feb 20 21:08 7f84b6184000-7f84b61b8000 -> /lib/ld-musl-x86_64.so.1
lr--------    1 root     root          64 Feb 20 21:08 7f84b61b9000-7f84b61ba000 -> /lib/ld-musl-x86_64.so.1
lr--------    1 root     root          64 Feb 20 21:08 7f84b61ba000-7f84b61bb000 -> /lib/ld-musl-x86_64.so.1

maps

実行可能ファイルとライブラリファイルへのメモリのマップ

/proc/1 # cat maps
56272c442000-56272c444000 r--p 00000000 fe:01 3021452                    /sbin/tini
56272c444000-56272c446000 r-xp 00002000 fe:01 3021452                    /sbin/tini
56272c446000-56272c448000 r--p 00004000 fe:01 3021452                    /sbin/tini
56272c448000-56272c449000 r--p 00005000 fe:01 3021452                    /sbin/tini
56272c449000-56272c44a000 rw-p 00006000 fe:01 3021452                    /sbin/tini
7f84b6128000-7f84b613d000 r--p 00000000 fe:01 1979988                    /lib/ld-musl-x86_64.so.1
7f84b613d000-7f84b6184000 r-xp 00015000 fe:01 1979988                    /lib/ld-musl-x86_64.so.1
7f84b6184000-7f84b61b8000 r--p 0005c000 fe:01 1979988                    /lib/ld-musl-x86_64.so.1
7f84b61b9000-7f84b61ba000 r--p 00090000 fe:01 1979988                    /lib/ld-musl-x86_64.so.1
7f84b61ba000-7f84b61bb000 rw-p 00091000 fe:01 1979988                    /lib/ld-musl-x86_64.so.1
7f84b61bb000-7f84b61be000 rw-p 00000000 00:00 0
7fffaae31000-7fffaae52000 rw-p 00000000 00:00 0                          [stack]
7fffaaee5000-7fffaaee8000 r--p 00000000 00:00 0                          [vvar]
7fffaaee8000-7fffaaeea000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

mem

このプロセスによって保持されるメモリ(上記mapsと組み合わせるとseekしてメモリダンプしたりできるっぽい)
https://unix.stackexchange.com/questions/6301/how-do-i-read-from-proc-pid-mem-under-linux
https://blog.cloudflare.com/diving-into-proc-pid-mem/

mountinfo

プロセスのマウント名前空間のマウントポイント似関しての情報

左から順に
mount ID: マウントの一意のID(umount後に再利用可能)
parent ID: 親マウントのID(このマウント名前空間のツリーのルートの場合は自分自身/chrootとかで親マウントポイントがプロセスのルートディレクトリのそれにある場合は一致する親IDのレコードが表示されない)
major:miner: st_devの値(https://man7.org/linux/man-pages/man2/stat.2.html)
root: マウントのルートを形成するファイルシステム内のディレクトリのパス名
mount point: プロセスのルートディレクトリからの相対的なマウントポイントのパス名
mount options: https://man7.org/linux/man-pages/man2/mount.2.html
optional fields: "tag[:value]"形式で0個以上のフィールド(ない場合は表示すらされてなさそう)
separator: オプションフィールドの終わり(ハイフン)
filesystem: "type[.subtype]"形式でファイルシステムのタイプ
mount source: ファイルシステム固有の情報 or "none"
super options: スーパーブロック毎のオプション(https://man7.org/linux/man-pages/man2/mount.2.html)

/proc/1 # cat mountinfo
441 324 0:109 / / rw,relatime master:122 - overlay overlay rw,lowerdir=/var/lib/docker/overlay2/l/BDY7PMJU4CCICFFJZWBOXOJGBA:/var/lib/docker/overlay2/l/HVQZRB6QCR34A4LTMUYK7WUQIG:/var/lib/docker/overlay2/l/3E7SUGFIJZW4UVSM47THOR56GH:/var/lib/docker/overlay2/l/P354VKRAJXJOIEL4ENNME7AUB7:/var/lib/docker/overlay2/l/DLC43UAJ4KLVGILIWQ6GOIOJHA:/var/lib/docker/overlay2/l/6PG626FG6T6TEG5N4PTYOWTQWK:/var/lib/docker/overlay2/l/ORG2QBNC67IG6FMZVVFI2MJI3D,upperdir=/var/lib/docker/overlay2/1aa0c289cd2c7724c005b570f28f07c6a9b803855d3867e1fc97aa53ea7ba0e3/diff,workdir=/var/lib/docker/overlay2/1aa0c289cd2c7724c005b570f28f07c6a9b803855d3867e1fc97aa53ea7ba0e3/work
486 441 0:111 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw
487 441 0:112 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
488 487 0:113 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
489 441 0:114 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro
490 489 0:115 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755
491 490 0:29 /docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d /sys/fs/cgroup/cpuset ro,nosuid,nodev,noexec,relatime master:17 - cgroup cpuset rw,cpuset
492 490 0:30 /docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d /sys/fs/cgroup/cpu ro,nosuid,nodev,noexec,relatime master:18 - cgroup cpu rw,cpu
493 490 0:31 /docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d /sys/fs/cgroup/cpuacct ro,nosuid,nodev,noexec,relatime master:19 - cgroup cpuacct rw,cpuacct
494 490 0:32 /docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d /sys/fs/cgroup/blkio ro,nosuid,nodev,noexec,relatime master:20 - cgroup blkio rw,blkio
495 490 0:33 /docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime master:21 - cgroup memory rw,memory
496 490 0:34 /docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d /sys/fs/cgroup/devices ro,nosuid,nodev,noexec,relatime master:22 - cgroup devices rw,devices
497 490 0:35 /docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d /sys/fs/cgroup/freezer ro,nosuid,nodev,noexec,relatime master:23 - cgroup freezer rw,freezer
498 490 0:36 /docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d /sys/fs/cgroup/net_cls ro,nosuid,nodev,noexec,relatime master:24 - cgroup net_cls rw,net_cls
499 490 0:37 /docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d /sys/fs/cgroup/perf_event ro,nosuid,nodev,noexec,relatime master:25 - cgroup perf_event rw,perf_event
500 490 0:38 /docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d /sys/fs/cgroup/net_prio ro,nosuid,nodev,noexec,relatime master:26 - cgroup net_prio rw,net_prio
501 490 0:39 /docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d /sys/fs/cgroup/hugetlb ro,nosuid,nodev,noexec,relatime master:27 - cgroup hugetlb rw,hugetlb
502 490 0:40 /docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d /sys/fs/cgroup/pids ro,nosuid,nodev,noexec,relatime master:28 - cgroup pids rw,pids
503 490 0:41 / /sys/fs/cgroup/rdma ro,nosuid,nodev,noexec,relatime master:29 - cgroup rdma rw,rdma
504 490 0:42 /docker/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d /sys/fs/cgroup/systemd ro,nosuid,nodev,noexec,relatime master:30 - cgroup cgroup rw,name=systemd
505 487 0:110 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw
506 487 0:116 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k
507 441 254:1 /docker/containers/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vda1 rw
508 441 254:1 /docker/containers/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw
509 441 254:1 /docker/containers/bf7809b99cdc4e35f51463dbc091bacc2b54bb6bbeddce56c3566b3d3553552d/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw
325 486 0:111 /bus /proc/bus ro,relatime - proc proc rw
326 486 0:111 /fs /proc/fs ro,relatime - proc proc rw
327 486 0:111 /irq /proc/irq ro,relatime - proc proc rw
328 486 0:111 /sys /proc/sys ro,relatime - proc proc rw
329 486 0:111 /sysrq-trigger /proc/sysrq-trigger ro,relatime - proc proc rw
330 486 0:117 / /proc/acpi ro,relatime - tmpfs tmpfs ro
331 486 0:112 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
332 486 0:112 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
333 486 0:112 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
334 486 0:112 /null /proc/sched_debug rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
335 489 0:118 / /sys/firmware ro,relatime - tmpfs tmpfs ro

mounts

プロセスのマウント名前空間に現在マウントされているすべてのファイルシステムの一覧
https://man7.org/linux/man-pages/man7/mount_namespaces.7.html
https://man7.org/linux/man-pages/man5/fstab.5.html

/proc/1 # cat mounts
overlay / overlay rw,relatime,lowerdir=/var/lib/docker/overlay2/l/BDY7PMJU4CCICFFJZWBOXOJGBA:/var/lib/docker/overlay2/l/HVQZRB6QCR34A4LTMUYK7WUQIG:/var/lib/docker/overlay2/l/3E7SUGFIJZW4UVSM47THOR56GH:/var/lib/docker/overlay2/l/P354VKRAJXJOIEL4ENNME7AUB7:/var/lib/docker/overlay2/l/DLC43UAJ4KLVGILIWQ6GOIOJHA:/var/lib/docker/overlay2/l/6PG626FG6T6TEG5N4PTYOWTQWK:/var/lib/docker/overlay2/l/ORG2QBNC67IG6FMZVVFI2MJI3D,upperdir=/var/lib/docker/overlay2/1aa0c289cd2c7724c005b570f28f07c6a9b803855d3867e1fc97aa53ea7ba0e3/diff,workdir=/var/lib/docker/overlay2/1aa0c289cd2c7724c005b570f28f07c6a9b803855d3867e1fc97aa53ea7ba0e3/work 0 0
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
tmpfs /dev tmpfs rw,nosuid,size=65536k,mode=755 0 0
devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666 0 0
sysfs /sys sysfs ro,nosuid,nodev,noexec,relatime 0 0
tmpfs /sys/fs/cgroup tmpfs rw,nosuid,nodev,noexec,relatime,mode=755 0 0
cpuset /sys/fs/cgroup/cpuset cgroup ro,nosuid,nodev,noexec,relatime,cpuset 0 0
cpu /sys/fs/cgroup/cpu cgroup ro,nosuid,nodev,noexec,relatime,cpu 0 0
cpuacct /sys/fs/cgroup/cpuacct cgroup ro,nosuid,nodev,noexec,relatime,cpuacct 0 0
blkio /sys/fs/cgroup/blkio cgroup ro,nosuid,nodev,noexec,relatime,blkio 0 0
memory /sys/fs/cgroup/memory cgroup ro,nosuid,nodev,noexec,relatime,memory 0 0
devices /sys/fs/cgroup/devices cgroup ro,nosuid,nodev,noexec,relatime,devices 0 0
freezer /sys/fs/cgroup/freezer cgroup ro,nosuid,nodev,noexec,relatime,freezer 0 0
net_cls /sys/fs/cgroup/net_cls cgroup ro,nosuid,nodev,noexec,relatime,net_cls 0 0
perf_event /sys/fs/cgroup/perf_event cgroup ro,nosuid,nodev,noexec,relatime,perf_event 0 0
net_prio /sys/fs/cgroup/net_prio cgroup ro,nosuid,nodev,noexec,relatime,net_prio 0 0
hugetlb /sys/fs/cgroup/hugetlb cgroup ro,nosuid,nodev,noexec,relatime,hugetlb 0 0
pids /sys/fs/cgroup/pids cgroup ro,nosuid,nodev,noexec,relatime,pids 0 0
rdma /sys/fs/cgroup/rdma cgroup ro,nosuid,nodev,noexec,relatime,rdma 0 0
cgroup /sys/fs/cgroup/systemd cgroup ro,nosuid,nodev,noexec,relatime,name=systemd 0 0
mqueue /dev/mqueue mqueue rw,nosuid,nodev,noexec,relatime 0 0
shm /dev/shm tmpfs rw,nosuid,nodev,noexec,relatime,size=65536k 0 0
/dev/vda1 /etc/resolv.conf ext4 rw,relatime 0 0
/dev/vda1 /etc/hostname ext4 rw,relatime 0 0
/dev/vda1 /etc/hosts ext4 rw,relatime 0 0
proc /proc/bus proc ro,relatime 0 0
proc /proc/fs proc ro,relatime 0 0
proc /proc/irq proc ro,relatime 0 0
proc /proc/sys proc ro,relatime 0 0
proc /proc/sysrq-trigger proc ro,relatime 0 0
tmpfs /proc/acpi tmpfs ro,relatime 0 0
tmpfs /proc/kcore tmpfs rw,nosuid,size=65536k,mode=755 0 0
tmpfs /proc/keys tmpfs rw,nosuid,size=65536k,mode=755 0 0
tmpfs /proc/timer_list tmpfs rw,nosuid,size=65536k,mode=755 0 0
tmpfs /proc/sched_debug tmpfs rw,nosuid,size=65536k,mode=755 0 0
tmpfs /sys/firmware tmpfs ro,relatime 0 0

mountstats

プロセスのマウント名前空間のマウントポイントに関する情報(統計、構成情報)
左から順に
マウントされたデバイス(対応するデバイスがなければnodevice)
ファイルシステムツリー内のマウントポイント
ファイルシステムのタイプ
オプションの統計や構成情報

/proc/1 # cat mountstats
device overlay mounted on / with fstype overlay
device proc mounted on /proc with fstype proc
device tmpfs mounted on /dev with fstype tmpfs
device devpts mounted on /dev/pts with fstype devpts
device sysfs mounted on /sys with fstype sysfs
device tmpfs mounted on /sys/fs/cgroup with fstype tmpfs
device cpuset mounted on /sys/fs/cgroup/cpuset with fstype cgroup
device cpu mounted on /sys/fs/cgroup/cpu with fstype cgroup
device cpuacct mounted on /sys/fs/cgroup/cpuacct with fstype cgroup
device blkio mounted on /sys/fs/cgroup/blkio with fstype cgroup
device memory mounted on /sys/fs/cgroup/memory with fstype cgroup
device devices mounted on /sys/fs/cgroup/devices with fstype cgroup
device freezer mounted on /sys/fs/cgroup/freezer with fstype cgroup
device net_cls mounted on /sys/fs/cgroup/net_cls with fstype cgroup
device perf_event mounted on /sys/fs/cgroup/perf_event with fstype cgroup
device net_prio mounted on /sys/fs/cgroup/net_prio with fstype cgroup
device hugetlb mounted on /sys/fs/cgroup/hugetlb with fstype cgroup
device pids mounted on /sys/fs/cgroup/pids with fstype cgroup
device rdma mounted on /sys/fs/cgroup/rdma with fstype cgroup
device cgroup mounted on /sys/fs/cgroup/systemd with fstype cgroup
device mqueue mounted on /dev/mqueue with fstype mqueue
device shm mounted on /dev/shm with fstype tmpfs
device /dev/vda1 mounted on /etc/resolv.conf with fstype ext4
device /dev/vda1 mounted on /etc/hostname with fstype ext4
device /dev/vda1 mounted on /etc/hosts with fstype ext4
device proc mounted on /proc/bus with fstype proc
device proc mounted on /proc/fs with fstype proc
device proc mounted on /proc/irq with fstype proc
device proc mounted on /proc/sys with fstype proc
device proc mounted on /proc/sysrq-trigger with fstype proc
device tmpfs mounted on /proc/acpi with fstype tmpfs
device tmpfs mounted on /proc/kcore with fstype tmpfs
device tmpfs mounted on /proc/keys with fstype tmpfs
device tmpfs mounted on /proc/timer_list with fstype tmpfs
device tmpfs mounted on /proc/sched_debug with fstype tmpfs
device tmpfs mounted on /sys/firmware with fstype tmpfs

net

めちゃくちゃ多いので割愛
https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/6/html/deployment_guide/s2-proc-dir-net

ns

名前空間 https://man7.org/linux/man-pages/man2/setns.2.html
https://man7.org/linux/man-pages/man7/namespaces.7.html

/proc/1/ns # ls -l
total 0
lrwxrwxrwx    1 root     root             0 Feb 21 17:20 cgroup -> cgroup:[4026531835]
lrwxrwxrwx    1 root     root             0 Feb 21 17:20 ipc -> ipc:[4026532282]
lrwxrwxrwx    1 root     root             0 Feb 21 17:20 mnt -> mnt:[4026532280]
lrwxrwxrwx    1 root     root             0 Feb 21 17:20 net -> net:[4026532285]
lrwxrwxrwx    1 root     root             0 Feb 21 17:20 pid -> pid:[4026532283]
lrwxrwxrwx    1 root     root             0 Feb 21 17:20 pid_for_children -> pid:[4026532283]
lrwxrwxrwx    1 root     root             0 Feb 21 17:20 user -> user:[4026531837]
lrwxrwxrwx    1 root     root             0 Feb 21 17:20 uts -> uts:[4026532281]

oom_adj

OOMに使用されるスコアを調整できる
指定できる範囲は-16 ~ +15で-17はOOMキルを完全に無効にする
デフォルトは0
更新時はCAP_SYS_RESOURCEが必要
2.6.36以降は /proc/<pid>/oom_socre_adj が優先される(deprecated)

/proc/1 # cat oom_adj
0

oom_score

現在のOOMスコアが表示される
スコアが高いほどプロセスがOOM-killerに殺される可能性が高まる
スコアの基準はメモリの使用量だが、減少・増加は次のような要因で行われる

プロセスに特権(privileged)があるかどうか?(減少)
2.6.36以前だと以下も
プロセスがforkを使用して多くの子プロセスを作成するかどうか(増加)
プロセスが長時間実行されているか or CPU時間を大量に使用しているか(減少)
プロセスのnice値が低いかどうか(>0の場合増加)
プロセスがハードウェアに直接アクセスするかどうか(減少)

また、oom_scoreはプロセスのoom_score_adj or oom_adjで指定された値も反映する

/proc/1 # cat oom_score
0

oom_score_adj

基本はoom_adjと似たようなもの
ただ値のrangeが -1000(タスクの許可されたメモリの指定した割合をタスクに対するスコアリングとみなされないようにすることとほぼ同等) ~ 0(強制終了しない) ~ 1000(常に強制終了)
プロセスに割り当て可能なメモリに対しての比率(半分使ってたら500になる)
oom_adj or oom_score_adj をいじるともう片方が変更される

/proc/1 # cat oom_score_adj
0

pagemap

ページテーブル
https://www.kernel.org/doc/Documentation/admin-guide/mm/pagemap.rst

root

このプロセスのルートディレクトリへのリンク

/proc/1 # ls -l | grep "root ->"
lrwxrwxrwx    1 root     root             0 Feb 20 12:06 root -> /

smaps

mapsの拡張
マッピング毎のメモリ消費量とそれに関連するフラグを表示する

/proc/1 # cat smaps
56272c442000-56272c444000 r--p 00000000 fe:01 3021452                    /sbin/tini
Size:                  8 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   8 kB
Pss:                   8 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         8 kB
Private_Dirty:         0 kB
Referenced:            8 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd mr mw me dw
56272c444000-56272c446000 r-xp 00002000 fe:01 3021452                    /sbin/tini
Size:                  8 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   8 kB
Pss:                   8 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         8 kB
Private_Dirty:         0 kB
Referenced:            8 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd ex mr mw me dw
56272c446000-56272c448000 r--p 00004000 fe:01 3021452                    /sbin/tini
Size:                  8 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   8 kB
Pss:                   8 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         8 kB
Private_Dirty:         0 kB
Referenced:            8 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd mr mw me dw
56272c448000-56272c449000 r--p 00005000 fe:01 3021452                    /sbin/tini
Size:                  4 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   4 kB
Pss:                   4 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         4 kB
Referenced:            4 kB
Anonymous:             4 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd mr mw me dw ac
56272c449000-56272c44a000 rw-p 00006000 fe:01 3021452                    /sbin/tini
Size:                  4 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   4 kB
Pss:                   4 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         4 kB
Referenced:            4 kB
Anonymous:             4 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd wr mr mw me dw ac
7f84b6128000-7f84b613d000 r--p 00000000 fe:01 1979988                    /lib/ld-musl-x86_64.so.1
Size:                 84 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                  80 kB
Pss:                  20 kB
Shared_Clean:         80 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:           80 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd mr mw me dw
7f84b613d000-7f84b6184000 r-xp 00015000 fe:01 1979988                    /lib/ld-musl-x86_64.so.1
Size:                284 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                 280 kB
Pss:                  77 kB
Shared_Clean:        280 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:          280 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd ex mr mw me dw
7f84b6184000-7f84b61b8000 r--p 0005c000 fe:01 1979988                    /lib/ld-musl-x86_64.so.1
Size:                208 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                  60 kB
Pss:                  16 kB
Shared_Clean:         60 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:           60 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd mr mw me dw
7f84b61b9000-7f84b61ba000 r--p 00090000 fe:01 1979988                    /lib/ld-musl-x86_64.so.1
Size:                  4 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   4 kB
Pss:                   4 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         4 kB
Referenced:            4 kB
Anonymous:             4 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd mr mw me dw ac
7f84b61ba000-7f84b61bb000 rw-p 00091000 fe:01 1979988                    /lib/ld-musl-x86_64.so.1
Size:                  4 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   4 kB
Pss:                   4 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         4 kB
Referenced:            4 kB
Anonymous:             4 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd wr mr mw me dw ac
7f84b61bb000-7f84b61be000 rw-p 00000000 00:00 0
Size:                 12 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   8 kB
Pss:                   8 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         8 kB
Referenced:            8 kB
Anonymous:             8 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd wr mr mw me ac
7fffaae31000-7fffaae52000 rw-p 00000000 00:00 0                          [stack]
Size:                132 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   4 kB
Pss:                   4 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         4 kB
Referenced:            4 kB
Anonymous:             4 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd wr mr mw me gd ac
7fffaaee5000-7fffaaee8000 r--p 00000000 00:00 0                          [vvar]
Size:                 12 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   0 kB
Pss:                   0 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:            0 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd mr pf io de dd
7fffaaee8000-7fffaaeea000 r-xp 00000000 00:00 0                          [vdso]
Size:                  8 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   4 kB
Pss:                   0 kB
Shared_Clean:          4 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:            4 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd ex mr mw me de
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Size:                  4 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                   0 kB
Pss:                   0 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:            0 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd ex

smaps_rollup

プロセスの全てのマッピングの累積のsmaps統計
smapsから導出できるが、より高速で便利

/proc/1 # cat smaps_rollup
56272c442000-7fffaaeea000 ---p 00000000 00:00 0                          [rollup]
Rss:                 476 kB
Pss:                 166 kB
Shared_Clean:        424 kB
Shared_Dirty:          0 kB
Private_Clean:        24 kB
Private_Dirty:        28 kB
Referenced:          476 kB
Anonymous:            28 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB

stack

スタックトレースを表示する
CONFIG_STACKTRACEで有効

stat

プロセスのステータス

/proc/1 # cat stat
1 (tini) S 0 1 1 0 -1 1077952768 837 0 1 0 6 8 0 0 20 0 1 0 5608068 798720 1 18446744073709551615 94726246375424 94726246390548 140736060529872 0 0 0 0 3145728 0 0 0 0 17 0 0 0 0 0 0 94726246402880 94726246405259 94726276997120 140736060530492 140736060530530 140736060530530 140736060530669 0

statm

プロセスのメモリステータス

/proc/1 # cat statm
195 1 0 4 0 38 0

status

人間が読める形式のプロセスステータス

/proc/1 # cat status
Name:   tini
Umask:  0022
State:  S (sleeping)
Tgid:   1
Ngid:   0
Pid:    1
PPid:   0
TracerPid:  0
Uid:    0   0   0   0
Gid:    0   0   0   0
FDSize: 64
Groups: 0 1 2 3 4 6 10 11 20 26 27
NStgid: 1
NSpid:  1
NSpgid: 1
NSsid:  1
VmPeak:      780 kB
VmSize:      780 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:         4 kB
VmRSS:         4 kB
RssAnon:           4 kB
RssFile:           0 kB
RssShmem:          0 kB
VmData:       20 kB
VmStk:       132 kB
VmExe:        16 kB
VmLib:       284 kB
VmPTE:        32 kB
VmSwap:        0 kB
HugetlbPages:          0 kB
CoreDumping:    0
Threads:    1
SigQ:   1/7418
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000300000
SigCgt: 0000000000000000
CapInh: 00000000a80c25fb
CapPrm: 00000000a80c25fb
CapEff: 00000000a80c25fb
CapBnd: 00000000a80c25fb
CapAmb: 0000000000000000
NoNewPrivs: 0
Seccomp:    2
Speculation_Store_Bypass:   vulnerable
Cpus_allowed:   f
Cpus_allowed_list:  0-3
Mems_allowed:   1
Mems_allowed_list:  0
voluntary_ctxt_switches:    2172
nonvoluntary_ctxt_switches: 54

wchan

CONFIG_KALLSYMS=yが存在する場合、これはタスクがブロックされているKernel関数のシンボルを示す ブロックされていない場合は0

/proc/1 # cat wchan
0

whiptailでlsしてファイル選択した結果を取得する

スクリプト書いてるとたまーに欲しくなるやつ
検索してもなんかぱっといい感じのやつが出てこないので(長すぎてコピペするのに躊躇うやつとか)、主に自分のローカルで使うように書いた

↓は /sbin で試したやつ

macで使うなら brew install coreutils して、 gls 使わないと動かないので注意

相変わらず bash 使ってるけど、いい加減他のシェルにしたほうが良いか悩ましい

LocalVariableTableParameterNameDiscovererが削除されてAutowireに失敗するようになった話

github.com

SpringBootを3.2.0に上げた際に、
同じ型のBeanを定義していて、名前による(Qualifierではない)Autowireが失敗するようになってしまったので、それの備忘録
とりあえず複数のBeanがマッチするところまでの話は以下の記事に書いてるので端折る

www.hacktotech.net

DependencyDescriptor#getDependencyName が呼ばれる
https://github.com/spring-projects/spring-framework/blob/v6.1.2/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java#L1728

MethodParameter#getParameterName が呼ばれる
https://github.com/spring-projects/spring-framework/blob/v6.1.2/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java#L356

ここでパラメータ名を解決するわけだが、
参照している parameterNameDiscoverer
KotlinReflectionParameterNameDiscoverer
StandardReflectionParameterNameDiscoverer
LocalVariableTableParameterNameDiscoverer
の順番でパラメータ名の解決を行うようになっていたのが、削除されたことによって失敗するようになっていた
(元のバージョンでも上2つのdiscovererは最終的にnullを返していたので、そのままでは解決できなくなった)
https://github.com/spring-projects/spring-framework/blob/v6.1.2/spring-core/src/main/java/org/springframework/core/MethodParameter.java#L714

で、困ったことに自分たちのコードはKotlinで書いているが、
外部ライブラリのstarterがJavaで書かれており、wikiの対応がまだされていなかったので、自分たちのSpringBootのバージョンをあげたら壊れてしまったという話だった
(自分たちのコードで、Kotlin内で同じ型のBeanが複数構成されていた場合の名前による解決はうまく動いたので、少なくともこのパターンだけは対応しないとうまく動かないとかな気がする)

↓はPoC

github.com

jmodule は外部ライブラリをイメージして書いていて、
src 配下は自分たちのコードをイメージして書いている

org.springframework.boot3.1.7 であれば
jmodule 側の build.gradle に↓を書かなくても問題なく動くが、

tasks.withType(JavaCompile).configureEach {
    options.compilerArgs.add("-parameters")
}

3.2.1 など spring-core6.1 系になると失敗するようになる
最初出会したときは、自分たちの build.gradle.kts だけ対応すればいけるのかと思っていたが、
そんなことはなかったのでバージョンを上げる際は注意したほうが良さそう

一応コンパイルオプションつけたときの ResolverAutoConfiguration.java のclassファイルの違いもみてみた

無効

public class org.example.resolver.ResolverAutoConfiguration
  minor version: 0
  major version: 61
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #12                         // org/example/resolver/ResolverAutoConfiguration
  super_class: #2                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 2
Constant pool:
   #1 = Methodref          #2.#3          // java/lang/Object."<init>":()V
   #2 = Class              #4             // java/lang/Object
   #3 = NameAndType        #5:#6          // "<init>":()V
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Class              #8             // org/example/resolver/Resolver
   #8 = Utf8               org/example/resolver/Resolver
   #9 = Methodref          #7.#10         // org/example/resolver/Resolver."<init>":(Lorg/example/resolver/ParameterNameResolver;)V
  #10 = NameAndType        #5:#11         // "<init>":(Lorg/example/resolver/ParameterNameResolver;)V
  #11 = Utf8               (Lorg/example/resolver/ParameterNameResolver;)V
  #12 = Class              #13            // org/example/resolver/ResolverAutoConfiguration
  #13 = Utf8               org/example/resolver/ResolverAutoConfiguration
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               this
  #18 = Utf8               Lorg/example/resolver/ResolverAutoConfiguration;
  #19 = Utf8               resolver
  #20 = Utf8               (Lorg/example/resolver/ParameterNameResolver;)Lorg/example/resolver/Resolver;
  #21 = Utf8               parameterNameResolver
  #22 = Utf8               Lorg/example/resolver/ParameterNameResolver;
  #23 = Utf8               RuntimeVisibleAnnotations
  #24 = Utf8               Lorg/springframework/context/annotation/Bean;
  #25 = Utf8               SourceFile
  #26 = Utf8               ResolverAutoConfiguration.java
  #27 = Utf8               Lorg/springframework/context/annotation/Configuration;
{
  public org.example.resolver.ResolverAutoConfiguration();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lorg/example/resolver/ResolverAutoConfiguration;

  public org.example.resolver.Resolver resolver(org.example.resolver.ParameterNameResolver);
    descriptor: (Lorg/example/resolver/ParameterNameResolver;)Lorg/example/resolver/Resolver;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=3, locals=2, args_size=2
         0: new           #7                  // class org/example/resolver/Resolver
         3: dup
         4: aload_1
         5: invokespecial #9                  // Method org/example/resolver/Resolver."<init>":(Lorg/example/resolver/ParameterNameResolver;)V
         8: areturn
      LineNumberTable:
        line 10: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lorg/example/resolver/ResolverAutoConfiguration;
            0       9     1 parameterNameResolver   Lorg/example/resolver/ParameterNameResolver;
    RuntimeVisibleAnnotations:
      0: #24()
        org.springframework.context.annotation.Bean
}
SourceFile: "ResolverAutoConfiguration.java"
RuntimeVisibleAnnotations:
  0: #27()
    org.springframework.context.annotation.Configuration

有効

public class org.example.resolver.ResolverAutoConfiguration
  minor version: 0
  major version: 61
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #12                         // org/example/resolver/ResolverAutoConfiguration
  super_class: #2                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 2
Constant pool:
   #1 = Methodref          #2.#3          // java/lang/Object."<init>":()V
   #2 = Class              #4             // java/lang/Object
   #3 = NameAndType        #5:#6          // "<init>":()V
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Class              #8             // org/example/resolver/Resolver
   #8 = Utf8               org/example/resolver/Resolver
   #9 = Methodref          #7.#10         // org/example/resolver/Resolver."<init>":(Lorg/example/resolver/ParameterNameResolver;)V
  #10 = NameAndType        #5:#11         // "<init>":(Lorg/example/resolver/ParameterNameResolver;)V
  #11 = Utf8               (Lorg/example/resolver/ParameterNameResolver;)V
  #12 = Class              #13            // org/example/resolver/ResolverAutoConfiguration
  #13 = Utf8               org/example/resolver/ResolverAutoConfiguration
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               this
  #18 = Utf8               Lorg/example/resolver/ResolverAutoConfiguration;
  #19 = Utf8               resolver
  #20 = Utf8               (Lorg/example/resolver/ParameterNameResolver;)Lorg/example/resolver/Resolver;
  #21 = Utf8               parameterNameResolver
  #22 = Utf8               Lorg/example/resolver/ParameterNameResolver;
  #23 = Utf8               MethodParameters
  #24 = Utf8               RuntimeVisibleAnnotations
  #25 = Utf8               Lorg/springframework/context/annotation/Bean;
  #26 = Utf8               SourceFile
  #27 = Utf8               ResolverAutoConfiguration.java
  #28 = Utf8               Lorg/springframework/context/annotation/Configuration;
{
  public org.example.resolver.ResolverAutoConfiguration();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lorg/example/resolver/ResolverAutoConfiguration;

  public org.example.resolver.Resolver resolver(org.example.resolver.ParameterNameResolver);
    descriptor: (Lorg/example/resolver/ParameterNameResolver;)Lorg/example/resolver/Resolver;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=3, locals=2, args_size=2
         0: new           #7                  // class org/example/resolver/Resolver
         3: dup
         4: aload_1
         5: invokespecial #9                  // Method org/example/resolver/Resolver."<init>":(Lorg/example/resolver/ParameterNameResolver;)V
         8: areturn
      LineNumberTable:
        line 10: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lorg/example/resolver/ResolverAutoConfiguration;
            0       9     1 parameterNameResolver   Lorg/example/resolver/ParameterNameResolver;
    MethodParameters:
      Name                           Flags
      parameterNameResolver
    RuntimeVisibleAnnotations:
      0: #25()
        org.springframework.context.annotation.Bean
}
SourceFile: "ResolverAutoConfiguration.java"
RuntimeVisibleAnnotations:
  0: #28()
    org.springframework.context.annotation.Configuration

MethodParameters を見れば分かる通り、オプションによって有無の違いが見て取れる

docs.oracle.com

SpringBootでAutowireする際に複数のBeanがマッチした際の挙動をみたのでメモ

雰囲気で挙動を理解していたのをちゃんと見たのでメモ

spring.pleiades.io

@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
        @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
        ...
        // Step 3b: direct bean matches, possibly direct beans of type Collection / Map
        Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); // ここで型に対して一致するbeanの一覧を取得する
        ...

        // Step 4: determine single candidate
        if (matchingBeans.size() > 1) {
            autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); // 複数のbeanが当てはまる場合はどれを使うかを決定する
            if (autowiredBeanName == null) {
                if (isRequired(descriptor) || !indicatesArrayCollectionOrMap(type)) {
                    // Raise exception if no clear match found for required injection point
                    return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
                }
                else {
                    // In case of an optional Collection/Map, silently ignore a non-unique case:
                    // possibly it was meant to be an empty collection of multiple regular beans
                    // (before 4.3 in particular when we didn't even look for collection beans).
                    return null;
                }
            }
            instanceCandidate = matchingBeans.get(autowiredBeanName);
        }
        ...
}
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
    Class<?> requiredType = descriptor.getDependencyType();
    String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); // @Primaryがないかを優先して探す
    if (primaryCandidate != null) {
        return primaryCandidate; // 見つかったら返す
    }
    String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType); // @Priorityで指定した中で最も優先度の高い(値の低い)ものを探す
    if (priorityCandidate != null) {
        return priorityCandidate; // 見つかったら返す
    }
    // Fallback: pick directly registered dependency or qualified bean name match
    for (Map.Entry<String, Object> entry : candidates.entrySet()) {
        String candidateName = entry.getKey();
        Object beanInstance = entry.getValue();
        if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
                matchesBeanName(candidateName, descriptor.getDependencyName())) {
            return candidateName; // Autowireする際の変数名が一致するものが見つかったら返す
        }
    }
    return null;
}

@Priority でInjectするbeanを操作しようとした際に、↓は一見 @Priority が評価されそうに見えるが、
実際は評価されず exampleAexampleB どちらを使えば良いかわからずに終了する

import jakarta.annotation.Priority
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered


interface MarkerExample
data class ExampleImpl1(val value: String): MarkerExample
data class ExampleImpl2(val value: String): MarkerExample

@Configuration
class ExampleConfig {
    @Bean
    @Priority(Ordered.LOWEST_PRECEDENCE)
    fun exampleA(): MarkerExample {
        return ExampleImpl1(
            value = "ExampleA",
        )
    }

    @Bean
    @Priority(Ordered.HIGHEST_PRECEDENCE)
    fun exampleB(): MarkerExample {
        return ExampleImpl2(
            value = "ExampleB",
        )
    }

    @Bean
    fun exampleC(example: MarkerExample): String {
        return "ExampleC"
    }
}

逆に↓は動く(Bで解決される)

import jakarta.annotation.Priority
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered


interface MarkerExample
@Priority(Ordered.LOWEST_PRECEDENCE)
data class ExampleImpl1(val value: String): MarkerExample
@Priority(Ordered.HIGHEST_PRECEDENCE)
data class ExampleImpl2(val value: String): MarkerExample

@Configuration
class ExampleConfig {
    @Bean
    fun exampleA(): MarkerExample {
        return ExampleImpl1(
            value = "ExampleA",
        )
    }

    @Bean
    fun exampleB(): MarkerExample {
        return ExampleImpl2(
            value = "ExampleB",
        )
    }

    @Bean
    fun exampleC(example: MarkerExample): String {
        return "ExampleC"
    }
}

こういったパターンはほぼ @Primary があれば足りるし使うことはないが、
思っていた挙動と違ったので意外だった