持久化组件数据
持久化组件数据
由于某些组件或服务的数据需要在idea重启后依然能使用,所以IntelliJ Platform 提供了持久化API,您可以使用简单的 API 来保存一些值,也可以使用 PersistentStateComponent 接口来保存更复杂的数据
注意
如何需要保存加密数据,例如密码,可以查看 持久化加密数据
使用PersistentStateComponent
com.intellij.openapi.components.PersistentStateComponent 接口可以灵活地配置持久化数据的格式,位置等。
操作步骤:
定义一个实现 PersistentStateComponent 接口的 服务
定义持久化数据类 (持久化数据类,以下通称为 State)
使用 @com.intellij.openapi.components.State 指定持久化位置
注意:服务才能被持久化,扩展是不能被持久化的,如果你的扩展需要持久化数据,需要定义一个单独的服务来管理这些数据
实现PersistentStateComponent接口
PersistentStateComponent 的实现类需要指定泛型的类型,泛型类就是持久化数据类( State ),可以是一个 JavaBean ,也可以是 PersistentStateComponent 的子类。
如果 持久化数据类( State ) 设置为一个 JavaBean ,通常 持久化数据类( State ) 会作为 PersistentStateComponent 的成员变量,如下:
@State(...)
class MyService implements PersistentStateComponent<MyService.State> {
public static MyService getInstance() {
// implementation according to Application/Project level service
}
static class State {
public String value;
}
private State myState = new State();
public State getState() {
return myState;
}
public void loadState(State state) {
myState = state;
}
}
如果 State 设置为PersistentStateComponent 的子类,可以通过重写 getState() 和 loadState() 方法来实现,如下:
@State(...)
class MyService implements PersistentStateComponent<MyService> {
public static MyService getInstance() {
// implementation according to Application/Project level service
}
public String stateValue;
public MyService getState() {
return this;
}
public void loadState(MyService state) {
XmlSerializerUtil.copyBean(state, this);
}
}
实现持久化数据类(State)类
State 设置为PersistentStateComponent 的子类, 会把 PersistentStateComponent 的 public 成员变量和annotated 中的注解标记的 private 成员变量(其它请查看 自定义持久化数据的Xml格式 )自动序列化到 xml文件 里来持久化。
如果某一个public成员变量或某些属性不需要持久化,可以使用 @com.intellij.util.xmlb.annotations.Transient 来标记成员变量或成员变量的 getter 方法。
提示
注意:持久化数据类(State)类必须有默认的构造器,如果不需要持久化任何数据的时候,将会调用 State 的默认构造器。
持久化数据类(State)应该重写 equals() 方法,如果不重写equals() 方法,将会比较每一个成员变量。 如果使用 Kotlin 语言开发,可以使用 Data Classes
成员变量的类型是以下几种的时候,才能被持久化:
numbers (原始类型和包装类型的数字都可以)
booleans
strings
collections
maps
enums
针对其它的数据类型,可以定义一个转换器,转换器需要继承 com.intellij.util.xmlb.Converter 类,例如:
class LocalDateTimeConverter extends Converter<LocalDateTime> {
public LocalDateTime fromString(String value) {
long epochMilli = Long.parseLong(value);
ZoneId zoneId = ZoneId.systemDefault();
return Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime();
}
public String toString(LocalDateTime value) {
ZoneId zoneId = ZoneId.systemDefault();
long toEpochMilli = value.atZone(zoneId).toInstant().toEpochMilli();
return Long.toString(toEpochMilli);
}
}
转换器类定义完成后,在成员变量上添加 @com.intellij.util.xmlb.annotations.OptionTag 或 @com.intellij.util.xmlb.annotations.Attribute 注解:
class State {
@OptionTag(converter = LocalDateTimeConverter.class)
public LocalDateTime dateTime;
}
定义持久化位置
在 PersistentStateComponent 类上添加 @State 注解可以指定持久化位置,@State 注解有以下几个属性:
name (必填),指定 持久化数据类(State)的名字 (xml文件中的root 名称)
storages 使用一个或多个 @com.intellij.openapi.components.Storage 注解来指定存储位置。对 project级别的值,该字段为可选配置,在这个示例中,使用标准项目文件
reloadable 可选,如果设置为False, 那么当持久化数据类(State)或存储的xml文件发生改变时, 整个项目或应用需要重新加载,
也可以使用下面的简单方式来指定存储位置
@Storage("yourName.xml") 组件是project-level的 (对基于 .ipr 的项目不需要指定其它东西,可以自动配置完成)
@Storage(StoragePathMacros.WORKSPACE_FILE) 会存储到 工作空间(workspace)文件里。(更多值可以查看 StoragePathMacros 类 )
提示
对于application-level的持久化,推荐使用自定义存储文件,不建议使用过期的StoragePathMacros.NON_ROAMABLE_FILE
@Storage 注解中的 roamingType 属性可以指定在使用 Settings Repository 插件时,持久化数据的共享类型
自定义持久化数据文件的Xml格式
如果你想使用默认的序列化方法,但是想自定义存储数据的Xml的格式(例如:兼容老版本插件或外部定义的 XML 格式) 可以使用 @Tag, @Attribute, @Property, @MapAnnotation, @XCollection 等注解。
了解更新请查看 com.intellij.util.xmlb.annotations's package.html
如果你序列化的数据不能 反序列化成 JavaBean ,你可以使用 org.jdom.Element 作为持久化数据类(State)。在这种情况下,你可以使用 getState() 方法构建任意结构的 xml 元素,并直接保存在 Xml 文件里。 通过 loadState() 方法,你可以自定义逻辑来反序列化 JDOM 元素树,注意:应尽量避免使用这种方案。
迁移持久化数据
如果持久化数据模型或存储格式发生了变化,ConverterProvider 能提供一个 ProjectConverter ,ProjectConverter 的 getAdditionalAffectedFiles() 方法可以返回受影响的文件,方便进行迁移。
持久化组件的生命周期
当持久化组件 PersistentStateComponent 创建的时候或持久化Xml文件发生变化的时候,会调用 loadState() 方法(仅当组件存在一些非默认持久化数据时),
每一次保存 settings 时,都会调用 getState() 方法。如果 getState() 方法返回的 State 和默认 State (默认State指的是通过默认构造器创建的)相等,Xml 文件将不会被修改,否则返回的State 就会被序列化存储到 Xml 文件里。
使用PropertiesComponent实现不可共享的持久化
如果插件只需要持久化一些简单的数据,最方便的方案是 com.intellij.ide.util.PropertiesComponent 服务。它可以在工作空间的文件里存储 应用级(application-level )和项目级(project-level )的数据。这些数据是临时的,不能被所有安装的ide共享的。
**PropertiesComponent.getInstance()**可以存储应用级(application-level ) 的数据,PropertiesComponent.getInstance(Project) 可以存储 项目级(project-level )的数据.
由于所有的插件共享命令空间,所以强烈建议为存储的Key添加前缀 (例如前缀设置为插件id)
旧版 API (JDOMExternalizable)
老版本的组件使用 JDOMExternalizable 接口来持久化数据。readExternal() 方法用来从 JDOM 元素解析数据,writeExternal() 方法用来写入数据
实现类可以手动持久化存储在属性和子元素中的数据,也可以使用 DefaultJDOMExternalizer 自动存储public成员变量
数据被持久化在以下文件中:
项目级(project-level ) :项目 (.ipr) 文件。如果插件中 plugin.xml 中的 workspace 选项被设置为true, 数据将会被持久在 workspace (.iws) 文件中
模块级(Module-level ): module (.iml) file.