持久化组件数据

走着路睡觉大约 7 分钟

持久化组件数据

由于某些组件或服务的数据需要在idea重启后依然能使用,所以IntelliJ Platform 提供了持久化API,您可以使用简单的 API 来保存一些值,也可以使用 PersistentStateComponentopen in new window 接口来保存更复杂的数据

注意

如何需要保存加密数据,例如密码,可以查看 持久化加密数据

使用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 的子类, 会把 PersistentStateComponentpublic 成员变量和annotatedopen in new window 中的注解标记的 private 成员变量(其它请查看 自定义持久化数据的Xml格式 )自动序列化到 xml文件 里来持久化。

如果某一个public成员变量或某些属性不需要持久化,可以使用 @com.intellij.util.xmlb.annotations.Transient 来标记成员变量或成员变量的 getter 方法。

提示

注意:持久化数据类(State)类必须有默认的构造器,如果不需要持久化任何数据的时候,将会调用 State 的默认构造器。

持久化数据类(State)应该重写 equals() 方法,如果不重写equals() 方法,将会比较每一个成员变量。 如果使用 Kotlin 语言开发,可以使用 Data Classesopen in new window

成员变量的类型是以下几种的时候,才能被持久化:

  • numbers (原始类型和包装类型的数字都可以)

  • booleans

  • strings

  • collections

  • maps

  • enums

针对其它的数据类型,可以定义一个转换器,转换器需要继承 com.intellij.util.xmlb.Converteropen in new window 类,例如:

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 Repositoryopen in new window 插件时,持久化数据的共享类型

自定义持久化数据文件的Xml格式

如果你想使用默认的序列化方法,但是想自定义存储数据的Xml的格式(例如:兼容老版本插件或外部定义的 XML 格式) 可以使用 @Tag, @Attribute, @Property, @MapAnnotation, @XCollection 等注解。

了解更新请查看 com.intellij.util.xmlb.annotations's package.htmlopen in new window

如果你序列化的数据不能 反序列化成 JavaBean ,你可以使用 org.jdom.Element 作为持久化数据类(State)。在这种情况下,你可以使用 getState() 方法构建任意结构的 xml 元素,并直接保存在 Xml 文件里。 通过 loadState() 方法,你可以自定义逻辑来反序列化 JDOM 元素树,注意:应尽量避免使用这种方案。

迁移持久化数据

如果持久化数据模型或存储格式发生了变化,ConverterProvideropen in new window 能提供一个 ProjectConverteropen in new windowProjectConvertergetAdditionalAffectedFiles() 方法可以返回受影响的文件,方便进行迁移。

持久化组件的生命周期

当持久化组件 PersistentStateComponent 创建的时候或持久化Xml文件发生变化的时候,会调用 loadState() 方法(仅当组件存在一些非默认持久化数据时),

每一次保存 settings 时,都会调用 getState() 方法。如果 getState() 方法返回的 State 和默认 State (默认State指的是通过默认构造器创建的)相等,Xml 文件将不会被修改,否则返回的State 就会被序列化存储到 Xml 文件里。

使用PropertiesComponent实现不可共享的持久化

如果插件只需要持久化一些简单的数据,最方便的方案是 com.intellij.ide.util.PropertiesComponentopen in new window 服务。它可以在工作空间的文件里存储 应用级(application-level )和项目级(project-level )的数据。这些数据是临时的,不能被所有安装的ide共享的。

**PropertiesComponent.getInstance()**可以存储应用级(application-level ) 的数据,PropertiesComponent.getInstance(Project) 可以存储 项目级(project-level )的数据.

由于所有的插件共享命令空间,所以强烈建议为存储的Key添加前缀 (例如前缀设置为插件id)

旧版 API (JDOMExternalizable)

老版本的组件使用 JDOMExternalizableopen in new window 接口来持久化数据。readExternal() 方法用来从 JDOM 元素解析数据,writeExternal() 方法用来写入数据

实现类可以手动持久化存储在属性和子元素中的数据,也可以使用 DefaultJDOMExternalizer 自动存储public成员变量

数据被持久化在以下文件中:

  • 项目级(project-level ) :项目 (.ipr) 文件。如果插件中 plugin.xml 中的 workspace 选项被设置为true, 数据将会被持久在 workspace (.iws) 文件中

  • 模块级(Module-level ): module (.iml) file.

上次编辑于:
贡献者: zhaojingbo
Loading...