mirror of
https://github.com/halo-dev/docs.git
synced 2025-10-21 02:04:01 +00:00
docs: update documentations for 2.21 (#502)
Signed-off-by: Ryan Wang <i@ryanc.cc>
This commit is contained in:
@@ -0,0 +1,331 @@
|
||||
---
|
||||
title: 依赖其他插件
|
||||
description: 介绍如何在 Halo 插件中声明和管理插件依赖关系,及提供插件依赖的项目结构示例。
|
||||
---
|
||||
|
||||
在插件开发过程中,依赖管理是确保插件间协作和功能扩展的关键。
|
||||
通过正确的依赖声明,插件能够依赖其他插件提供的功能,避免重复实现或资源浪费。
|
||||
Halo 插件框架允许开发者通过 `plugin.yaml` 文件中的 `pluginDependencies` 字段来声明插件的依赖关系,从而确保插件在运行时能够正确加载所需的其他插件。
|
||||
|
||||
本节将介绍如何在 `plugin.yaml` 文件中声明插件依赖,包括强制依赖、可选依赖及其版本管理规则。我们还将讨论依赖关系如何影响插件的加载顺序及运行时行为。
|
||||
|
||||
## 插件依赖管理
|
||||
|
||||
Halo 插件系统支持在 `plugin.yaml` 文件中通过 `pluginDependencies` 字段声明插件依赖关系。这些依赖关系可以是强制性的,也可以是可选的。
|
||||
依赖声明的规则非常灵活,能够满足不同的插件需求。
|
||||
|
||||
以下是如何在 `plugin.yaml` 文件中声明依赖的具体方式。
|
||||
|
||||
### 依赖声明方式
|
||||
|
||||
依赖关系通过 `pluginDependencies` 字段声明,采用 YAML 列表格式。每个依赖项包含插件名称和可选的版本号。
|
||||
|
||||
> 以下示例中将省略 `plugin.yaml` 文件中的其他字段,仅展示 `pluginDependencies` 字段。
|
||||
>
|
||||
> 下列提到的**插件名称**指的是 `plugin.yaml` 文件中的 `metadata.name` 字段。
|
||||
|
||||
以下是几种常见的依赖声明方式:
|
||||
|
||||
- **固定版本依赖**:如果插件依赖于另一个插件的特定版本,可以指定精确版本号。例如,`pluginA` 依赖于 `pluginB` 的版本 1.0.0:
|
||||
|
||||
```yaml
|
||||
# 省略其他字段
|
||||
spec:
|
||||
pluginDependencies:
|
||||
pluginB: 1.0.0
|
||||
```
|
||||
|
||||
- **版本范围依赖**:如果插件对版本有一定的要求,可以指定版本范围。通过 `>=`(大于等于)和 `<`(小于)等符号,可以表示插件版本的区间。例如,`pluginA` 依赖于版本在 1.0.0 到 2.0.0 之间(不包括 2.0.0):
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
pluginDependencies:
|
||||
pluginB: >=1.0.0 & <2.0.0
|
||||
```
|
||||
|
||||
- **可选依赖**:某些情况下,插件的依赖是可选的,即使依赖未被满足,插件仍然可以加载。通过在插件名称后加上 `?` 来声明可选依赖。例如,`pluginA` 可选依赖于 `pluginB` 的 1.0 版本:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
pluginDependencies:
|
||||
pluginB?: 1.0
|
||||
```
|
||||
|
||||
- **多个依赖声明**:一个插件可能依赖多个插件,所有依赖可以在同一列表中列出,使用逗号分隔。例如,`pluginA` 依赖于 `pluginB` 的 1.0.0 到 2.0.0 版本区间,以及 `pluginC` 的 0.0.1 到 0.1.0 版本区间:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
pluginDependencies:
|
||||
pluginB: >=1.0.0 & <=2.0.0
|
||||
pluginC: >=0.0.1 & <=0.1.0
|
||||
```
|
||||
|
||||
> **可选依赖** 功能需要在 Halo 2.20.11 及以上版本才可以使用。
|
||||
|
||||
### 依赖加载逻辑
|
||||
|
||||
在 Halo 插件系统中,插件的依赖关系会影响其加载顺序和运行时行为。具体来说,插件的加载只有在其所有强制依赖都得到满足时才会进行。
|
||||
|
||||
以下是依赖关系加载的关键点:
|
||||
|
||||
- **强制依赖**:插件的强制依赖必须在插件加载前完成。只有当所有指定的强制依赖被满足(即对应的插件及其指定版本存在并加载完成)时,插件才会被加载。否则,插件无法被启动。
|
||||
|
||||
- **可选依赖**:插件可以依赖某些插件,但这些依赖项是可选的。即便可选依赖没有被满足,插件仍会被加载并运行。在某些情况下,如果缺少某个可选插件的依赖,插件的某些功能可能会受到限制或无法启用,但插件本身不会失败。
|
||||
|
||||
- **版本匹配**:当插件依赖于其他插件的特定版本时,Halo 插件框架会根据声明的版本范围匹配所需的插件。如果版本范围要求匹配的插件版本不可用,插件将无法加载。如果插件指定了多个版本范围,框架会选择最适合的版本。如果多个版本都能满足要求,开发者需要确保没有版本冲突,以避免不一致的行为。
|
||||
|
||||
通过合理的依赖声明,插件的加载和运行将更加顺畅,能够确保所有必需的功能模块按预期协同工作。
|
||||
|
||||
### 依赖声明注意事项
|
||||
|
||||
- **版本管理**:在声明版本时,推荐使用明确的版本号或版本范围,避免使用过于宽松的版本要求(如 `pluginB: *`),以确保插件在不同环境中一致性地运行。
|
||||
- **依赖冲突**:多个插件可能依赖于不同版本的同一插件,这可能导致版本冲突。为避免这种情况,开发者应该尽量保持插件版本的兼容性,必要时在 `plugin.yaml` 文件中使用具体的版本号范围进行严格控制。
|
||||
- **插件间的依赖层级**:插件可能依赖于其他插件,而这些插件又可能依赖于其他插件。建议开发者清晰地管理依赖链,避免过于复杂的依赖层级。合理规划插件之间的依赖关系有助于减少维护难度和潜在的运行时问题。
|
||||
|
||||
通过以上方式,开发者可以灵活地声明和管理插件的依赖关系,确保插件的高效运行和模块化扩展。
|
||||
|
||||
## 提供依赖的插件项目结构
|
||||
|
||||
为了确保插件的可维护性、模块化和可扩展性,设计合理的项目结构是非常重要的。使用 Gradle 作为项目管理工具时,可以通过规范的目录结构来清晰地管理插件的依赖和代码。特别是当插件需要提供类型共享或者支持多个插件互相依赖时,合理的项目结构能够大大提高开发效率和代码复用性。
|
||||
|
||||
本节将介绍如何使用 Gradle 构建插件项目,并提供一些最佳实践,以确保插件项目结构清晰、模块化,**并便于其他插件进行引用**。
|
||||
|
||||
### 推荐项目结构 {#project-structure}
|
||||
|
||||
在 Gradle 项目中,推荐使用标准的项目结构,以便于插件代码的管理和依赖的声明。以下是一个典型的 Halo 插件项目结构示例:
|
||||
|
||||
```plaintext
|
||||
my-halo-plugin/
|
||||
├── build.gradle # 项目的构建配置
|
||||
├── settings.gradle # Gradle 设置文件
|
||||
├── src/
|
||||
│ ├── main/
|
||||
│ │ ├── java/ # Java 源代码目录
|
||||
│ │ └── resources/ # 插件资源文件目录
|
||||
│ │ └── plugin.yaml # 插件元数据配置文件
|
||||
│ └── test/ # 测试代码目录
|
||||
└── README.md # 项目说明文件,提供项目的介绍、安装和使用文档
|
||||
```
|
||||
|
||||
参考 [插件项目结构](../basics/structure.md) 了解更多关于 Halo 插件项目结构的信息。
|
||||
|
||||
### 类型定义文件的组织
|
||||
|
||||
当插件需要为其他插件提供类型时,项目结构需要进一步优化。特别是在使用 Gradle 构建时,可以将共享的类型和接口定义放置到专门的模块中,使其成为一个独立的依赖模块,供其他插件使用。
|
||||
|
||||
一个常见的做法是创建一个独立的 `api` 模块,专门存放所有的类型定义(例如接口、抽象类等)。这样,其他插件就可以通过引用这个 `api` 模块来共享这些类型,而不需要直接依赖实现模块。
|
||||
|
||||
以下是一个优化后的项目结构示例,包含 `api` 和 `plugin` 模块:
|
||||
|
||||
```plaintext
|
||||
my-halo-plugin/
|
||||
├── build.gradle # 根项目构建配置文件
|
||||
├── settings.gradle # 设置文件,声明子模块
|
||||
├── api/ # 存放 API 类型的模块
|
||||
│ ├── build.gradle # API 模块的构建配置
|
||||
│ ├── src/
|
||||
│ │ ├── main/
|
||||
│ │ │ └── java/ # API 类型定义文件
|
||||
│ │ └── resources/ # API 模块的资源文件(如果有的话)
|
||||
│ └── README.md # API 模块说明文档
|
||||
├── plugin/ # 插件实现模块
|
||||
│ ├── build.gradle # 插件实现模块的构建配置
|
||||
│ ├── src/
|
||||
│ │ ├── main/
|
||||
│ │ │ ├── java/ # 插件实现代码
|
||||
│ │ │ └── resources/ # 插件资源文件,包括 plugin.yaml
|
||||
│ │ └── test/ # 插件的测试代码
|
||||
│ └── README.md # 插件模块说明文档
|
||||
└── README.md # 项目说明文件
|
||||
```
|
||||
|
||||
#### API 模块构建配置
|
||||
|
||||
`api` 模块仅包含接口和类型定义,没有具体的实现逻辑。你可以通过 Gradle 将这个模块发布到 Maven 仓库,让其他插件能够依赖它。
|
||||
|
||||
`api/build.gradle` 配置如下:
|
||||
|
||||
```gradle
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
group = 'com.example'
|
||||
version = rootProject.version
|
||||
|
||||
repositories {
|
||||
mavenCentral() // 使用 Maven Central 仓库
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
// 包含源码 JAR 包
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// API 模块可能需要的一些依赖
|
||||
// 例如:如果需要一些常用的库
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
from components.java // 发布 Java 项目组件
|
||||
artifactId = 'my-halo-api' // API 模块的 artifactId
|
||||
version = project.hasProperty('version') ? project.property('version') : 'unspecified'
|
||||
|
||||
// pom 额外信息,这是可选的
|
||||
pom {
|
||||
// POM 中的插件信息,一般为插件的 displayName
|
||||
name = 'My Halo Plugin'
|
||||
// POM 中的插件描述
|
||||
description = 'A sample plugin for Halo'
|
||||
// 插件的官网 URL
|
||||
url = 'https://example.com/my-halo-plugin'
|
||||
|
||||
licenses {
|
||||
license {
|
||||
// 插件的开源许可协议
|
||||
name = 'Apache License 2.0'
|
||||
// 许可协议的 URL
|
||||
url = 'https://opensource.org/licenses/Apache-2.0'
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id = 'guqing' // 开发者的 ID
|
||||
name = 'guqing' // 开发者姓名
|
||||
email = 'guqing@example.com' // 开发者邮箱
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
// Git 仓库连接
|
||||
connection = 'scm:git:git@github.com:halo-sigs/my-halo-plugin.git'
|
||||
// Git 仓库连接,用于开发者
|
||||
developerConnection = 'scm:git:git@github.com:halo-sigs/my-halo-plugin.git'
|
||||
// Git 仓库 URL
|
||||
url = 'https://github.com/halo-sigs/my-halo-plugin'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
// 以 -SNAPSHOT 结尾的版本发布到快照仓库,否则发布到正式仓库
|
||||
url = version.endsWith('-SNAPSHOT') ? 'https://s01.oss.sonatype.org/content/repositories/snapshots/' :
|
||||
'https://s01.oss.sonatype.org/content/repositories/releases/'
|
||||
credentials {
|
||||
// 从 gradle.properties 中读取 或者从环境变量中读取
|
||||
username = project.findProperty("ossr.user") ?: System.getenv("OSSR_USERNAME")
|
||||
password = project.findProperty("ossr.password") ?: System.getenv("OSSR_PASSWORD")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
API 模块的代码应该是接口或抽象类,用于插件之间共享。例如:
|
||||
|
||||
```java
|
||||
// src/main/java/com/example/api/MyApi.java
|
||||
package com.example.api;
|
||||
|
||||
public interface MyApi {
|
||||
void doSomething();
|
||||
}
|
||||
```
|
||||
|
||||
当你完成上述配置后,可以通过 Gradle 发布 API 模块到 Maven 仓库。以下是发布的步骤:
|
||||
|
||||
1. Maven 现已支持通过 GiHub 账号注册并发布包,你可以在 [Maven Central](https://central.sonatype.org/register/central-portal) 注册账号并获取凭证。
|
||||
2. 得到你可以通过在 `~/.gradle/gradle.properties` 文件中添加以下内容来配置 Maven 仓库的凭证:
|
||||
|
||||
```properties
|
||||
ossr.user=your-username
|
||||
ossr.password=your-password
|
||||
```
|
||||
|
||||
3. 发布 API 模块到 Maven 仓库:
|
||||
|
||||
```bash
|
||||
./gradlew :api:publish
|
||||
```
|
||||
|
||||
除了手动发布到 Maven 仓库,你也可以使用 GitHub Actions 等 CI/CD 工具自动发布 API 模块,以避免出错。
|
||||
|
||||
以下是一个 GitHub Actions 的示例配置:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/publish-api.yml
|
||||
name: Publish plugin API module to Maven
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Cache Gradle packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
|
||||
- name: Extract version from GitHub tag
|
||||
id: extract_version
|
||||
run: |
|
||||
tag_name=${{ github.event.release.tag_name }}
|
||||
VERSION=${tag_name#v}
|
||||
echo "Extracted Version: $VERSION"
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean build -Pversion=${{ env.VERSION }}
|
||||
|
||||
- name: Publish to Maven
|
||||
env:
|
||||
OSSR_USERNAME: ${{ secrets.MAVEN_USERNAME }}
|
||||
OSSR_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
||||
run: ./gradlew :api:publish -Pversion=${{ env.VERSION }}
|
||||
```
|
||||
|
||||
它会在创建新的 Release 时自动发布 API 模块到 Maven 仓库。
|
||||
|
||||
你需要在仓库的 `Settings` -> `Secrets and variables` -> `Actions` -> `New Repository secret` 中添加 `MAVEN_USERNAME` 和 `MAVEN_PASSWORD` 两个密钥,分别对应 Maven 仓库的用户名和密码。
|
||||
|
||||
#### plugin 模块构建配置
|
||||
|
||||
`plugin` 模块是插件的具体实现,它依赖于 `api` 模块,并包含插件的具体功能和业务逻辑。
|
||||
|
||||
它的结构与 [推荐项目结构](#project-structure) 中的 `plugin` 模块基于一致,但需要在 `build.gradle` 中声明对 `api` 模块的依赖:
|
||||
|
||||
需要注意的是 `run.halo.plugin.devtools` Gradle 插件应该始终在 `plugin` 模块中引入,不能放在项目根目录的 `build.gradle` 或 `api` 模块的 `build.gradle` 中,以确保插件在开发模式下能够正确运行。
|
||||
|
||||
## 版本管理
|
||||
|
||||
API 模块和插件模块的版本应该保持一致,以确保插件在不同环境中的一致性。
|
||||
|
||||
版本的发布应该遵循 [插件语义化版本规范](../publish.md#version-control),以确保插件的版本号能够清晰地表达插件的变化和向后兼容性。
|
@@ -0,0 +1,90 @@
|
||||
---
|
||||
title: 插件如何被扩展
|
||||
description: 了解如何在 Halo 中定义扩展点接口、声明扩展点,并在插件中实现这些扩展点。
|
||||
---
|
||||
在 Halo 插件开发中,扩展点(Extension Point)是一种灵活的机制,允许插件在定义的接口上进行功能扩展,使其他插件可以基于该接口实现自定义逻辑。
|
||||
|
||||
本章节将详细介绍如何在 Halo 中定义和声明扩展点,为插件提供可扩展的接口,支持插件间的扩展与集成。
|
||||
|
||||
## 定义扩展点
|
||||
|
||||
在 Halo 中,扩展点由接口和相关的 YAML 文件定义。
|
||||
|
||||
以下是定义扩展点的详细步骤:
|
||||
|
||||
### 创建扩展点接口
|
||||
|
||||
在 Halo 核心代码中,扩展点接口表示了该插件可扩展的功能模块。开发者需要定义一个接口,并继承 ExtensionPoint 接口,以此作为扩展点的标志。
|
||||
|
||||
接口中定义的方法将成为插件实现时需扩展的具体方法。
|
||||
|
||||
**示例:** 定义文件处理扩展点接口
|
||||
|
||||
```java
|
||||
import org.pf4j.ExtensionPoint;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ReactiveNotifier extends ExtensionPoint {
|
||||
|
||||
Mono<Void> notify(NotificationContext context);
|
||||
}
|
||||
```
|
||||
|
||||
在以上示例中,`ReactiveNotifier` 接口定义了发送通知的方法,该方法将允许其他插件用于扩展通知方式如邮件、短信或 Webhook。
|
||||
|
||||
### 声明扩展点
|
||||
|
||||
定义了扩展点接口之后,需要声明扩展点用于在用户界面展示和查找扩展点的具体实现,参考 [声明自定义模型](../api-reference/server/extension.md#declare-extension-object)
|
||||
|
||||
**示例:** 在 YAML 文件中定义文件处理扩展点
|
||||
|
||||
```yaml
|
||||
apiVersion: plugin.halo.run/v1alpha1
|
||||
kind: ExtensionPointDefinition
|
||||
metadata:
|
||||
name: reactive-notifier
|
||||
spec:
|
||||
className: run.halo.app.notification.ReactiveNotifier
|
||||
displayName: "消息通知器"
|
||||
description: "用于扩展消息通知方式,以向用户发送通知"
|
||||
type: MULTI_INSTANCE
|
||||
icon: "notifier.svg"
|
||||
```
|
||||
|
||||
在此 YAML 配置中:
|
||||
|
||||
- name:reactive-notifier 是该扩展点的唯一标识,建议加上插件名称作为前缀以避免冲突,参考 [metadata.name 命名规范](../api-reference/server/extension.md#naming-spec-for-metadata-name)
|
||||
- className:指定扩展点接口的完整类路径,用于通过类加载器加载实现类。
|
||||
- displayName:扩展点的显示名称,用于描述其功能。
|
||||
- description:简要描述扩展点的功能。
|
||||
- type:标明扩展点的类型,可选择 `SINGLE_INSTANCE`(单实例)或 `MULTI_INSTANCE`(多实例),单实例就是只能有一个启用的扩展如评论组件扩展,多示例则是可以同时启用多个如过滤器。
|
||||
- icon: 扩展点的图标,非必填,用于在用户界面中展示,它应该是一个可访问的链接,参考 [ReverseProxy](../api-reference/server/reverseproxy.md)。
|
||||
|
||||
至此,扩展点已在 Halo 中定义并注册,可供其他插件进行实现。
|
||||
|
||||
然后需要将包含此扩展点类型的包发布到 Maven 供其他插件引用来实现扩展,参考 [插件 API 模块项目结构](dependency.md#提供依赖的插件项目结构)。
|
||||
|
||||
## 查找扩展
|
||||
|
||||
插件定义了扩展点之后就可以在需要的地方查找扩展并调用对应方法来对功能进行扩展和增强。
|
||||
|
||||
`ExtensionGetter` 用于获取和管理 Halo 或其他插件提供的扩展。
|
||||
|
||||
**示例:** 查找通知器扩展
|
||||
|
||||
```java
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class NotificationSender {
|
||||
private final ExtensionGetter extensionGetter;
|
||||
|
||||
@Override
|
||||
public Mono<Void> sendNotification(NotificationContext context) {
|
||||
return extensionGetter.getEnabledExtensions(ReactiveNotifier.class)
|
||||
.flatMap(notifier -> notifier.notify(context))
|
||||
.then();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
参考 [获取扩展](../api-reference/server/extension-getter.md) 了解更多关于 `ExtensionGetter` 的使用。
|
@@ -0,0 +1,161 @@
|
||||
---
|
||||
title: 事件共享
|
||||
description: 介绍 Halo 与插件以及插件与插件之间的事件共享机制。
|
||||
---
|
||||
|
||||
在 Halo 插件开发中,事件机制是实现插件间通信和功能解耦的重要工具。
|
||||
`@SharedEvent` 注解使得插件可以方便地在 Halo 主程序和其他插件间共享自定义的事件。
|
||||
当插件为自定义的 Spring Event 类型标注 `@SharedEvent` 注解时,Halo 将自动将该事件共享到事件总线,其他依赖了此插件的组件即可订阅并监听该事件。
|
||||
通过这一机制,插件不仅可以监听 Halo 提供的共享事件,也可以发布自己的共享事件,从而实现插件与插件之间的事件通信机制。
|
||||
|
||||
## 监听 Halo 提供的共享事件
|
||||
|
||||
Halo 提供了一些内置共享事件,插件开发者可以利用这些事件响应系统中的特定行为。以下步骤说明如何在插件中监听这些事件。
|
||||
|
||||
### 注册监听器
|
||||
|
||||
在插件中,要监听 Halo 提供的共享事件,首先需要注册事件监听器。事件监听器通常通过 `@EventListener` 注解来注册。
|
||||
|
||||
Halo 在检测到事件触发时会调用被标注的监听方法。
|
||||
|
||||
```java
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.event.SomeSharedEvent;
|
||||
|
||||
@Component
|
||||
public class HaloEventListener {
|
||||
|
||||
@EventListener
|
||||
public void handleSomeSharedEvent(SomeSharedEvent event) {
|
||||
// 处理事件的逻辑
|
||||
System.out.println("Received shared event: " + event.getMessage());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在上面的示例中,handleSomeSharedEvent 方法被注册为 SomeSharedEvent 的监听器。
|
||||
当 SomeSharedEvent 事件触发时,Halo 将自动调用此方法并传入事件实例,开发者可以在该方法中编写业务逻辑来响应事件。
|
||||
|
||||
当然也可以通过实现 `org.springframework.context.ApplicationListener` 接口来监听,这与 [Spring 事件监听](https://docs.spring.io/spring-framework/reference/core/beans/context-introduction.html#context-functionality-events) 的方式一致。
|
||||
|
||||
### Halo 内置共享事件
|
||||
|
||||
#### 文章
|
||||
|
||||
- PostPublishedEvent:文章被发布
|
||||
- PostUnpublishedEvent:文章被取消发布
|
||||
- PostUpdatedEvent:文章被更新
|
||||
- PostDeletedEvent:文章被删除
|
||||
- PostVisibleChangedEvent:文章的可见性(spec.visible)被修改
|
||||
|
||||
#### 第三方登陆
|
||||
|
||||
- UserConnectionDisconnectedEvent:用户解绑第三方登陆方式时触发的事件
|
||||
|
||||
#### 用户
|
||||
|
||||
- UserLoginEvent:用户登录成功
|
||||
- UserLogoutEvent:用户登出成功
|
||||
|
||||
## 发布自定义共享事件
|
||||
|
||||
除了监听已有的共享事件,插件也可以定义和发布自定义的共享事件,使得其他依赖该插件的组件能够监听和响应。
|
||||
|
||||
### 定义共享事件类型
|
||||
|
||||
要定义一个共享事件,首先需要创建一个事件类,并使用 `@SharedEvent` 注解对其进行标注。通常这个类需要继承自 Spring 的 ApplicationEvent 类或其他类似的事件基类。
|
||||
|
||||
示例代码:
|
||||
|
||||
```java
|
||||
import run.halo.app.plugin.SharedEvent;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
@SharedEvent
|
||||
public class CustomSharedEvent extends ApplicationEvent {
|
||||
|
||||
private final String message;
|
||||
|
||||
public CustomSharedEvent(Object source, String message) {
|
||||
super(source);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在上面的例子中,CustomSharedEvent 是一个自定义的共享事件,带有 message 属性,用于传递信息。
|
||||
|
||||
### 发布共享事件到事件总线
|
||||
|
||||
要发布事件,可以通过 Spring 的 `ApplicationEventPublisher` 发布自定义事件到事件总线,从而触发其他插件的监听器。
|
||||
|
||||
示例代码:
|
||||
|
||||
```java
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CustomEventPublisher {
|
||||
|
||||
@Autowired
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
public void publishCustomEvent(String message) {
|
||||
CustomSharedEvent event = new CustomSharedEvent(this, message);
|
||||
eventPublisher.publishEvent(event);
|
||||
System.out.println("Published custom shared event with message: " + message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在这里,`publishCustomEvent` 方法会创建一个 CustomSharedEvent 实例并将其发布到事件总线。任何依赖此事件提供者插件的插件监听 CustomSharedEvent 都将收到该事件并响应。
|
||||
|
||||
这需要以下步骤作为前提:
|
||||
|
||||
1. 插件 A 将包含了 `CustomSharedEvent` 这个类的依赖 `plugin-a-api` 发布到 Maven 仓库
|
||||
2. 插件 B 引入 `plugin-a-api` 作为项目依赖并将其作为 `compileOnly`,必须是 `compileOnly` 依赖
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
compileOnly "run.halo.example:plugin-a-api:1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
3. 配置插件 B 的 `plugin.yaml` 中的 `pluginDependencies` 依赖插件 A,参考 [插件依赖声明](dependency.md#依赖声明方式)
|
||||
|
||||
:::info
|
||||
关于为什么必须将插件 A 的 `plugin-a-api` 声明为 `compileOnly`?
|
||||
|
||||
插件类加载的顺序是:
|
||||
|
||||
先从当前插件找 -> 不存在则从 Halo 找 -> 不存在则从依赖插件找。
|
||||
|
||||
插件 B 要监听到插件 A 的事件,必须确保是同一个类型也就是同一个类加载器加载的类。
|
||||
那么只有声明为 compileOnly,插件 B 监听事件时才能从依赖的插件 A 中查找已加载的类
|
||||
:::
|
||||
|
||||
## 最佳实践
|
||||
|
||||
- 事件命名与分类:使用清晰且具有描述性的事件名称,避免让使用者产生困惑如 MomentCreatedEvent 不应该在 Moment 创建之前触发因为事件名称表示是创建后的事件。
|
||||
- 避免冲突与重复订阅:确保事件逻辑集中处理,防止监听器重复触发导致性能问题。
|
||||
- 性能与资源管理:避免频繁触发事件或长时间占用资源的事件处理逻辑,以确保系统稳定性。
|
||||
- 异步监听:监听事件时应该尽可能的使用 `@Async` 注解让 Listener 的处理逻辑是异步的,避免阻塞其他监听器处理
|
||||
|
||||
```java
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
|
||||
@Async
|
||||
@EventListener
|
||||
public void handleCustomSharedEvent(CustomSharedEvent event) {
|
||||
// do something...
|
||||
}
|
||||
```
|
||||
|
||||
- 只发布自己定义的**共享事件**:插件应该始终只去发布自己定义的共享事件避免出现循环,如插件 A 定义的 CustomSharedEvent 只应该由插件 A 去发布,插件 B 中不应该去发布 CustomSharedEvent 事件。
|
Reference in New Issue
Block a user