TL;DR
JetBrains IDEA 基本上是目前最好的 Java 开发 IDE ,根据自己的一些个性化需求定制插件也是可行的。本篇会通过修改一个开源插件的的方式,描述一些简单修改插件的方法以及可能遇到的问题。
此处感谢 ArthasHotSwap 插件,这一个插件为开发工作提供了便利。
场景
此处以一个场景作为案例。
ArthasHotSwap 插件是一个使用 Aliyun OSS 作为加密后 class 文件作为中转,近似“一键”完成热更新操作的 IDEA 插件。
插件功能很简单,仅在右侧菜单提供一个 Swap this class
选项:
查看插件源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// com.xxxtai.arthas.facade.impl.OssFacadeImpl#uploadString OSS ossClient = new OSSClientBuilder().build(ossInfo.endpoint, ossInfo.accessKeyId, ossInfo.accessKeySecret); PutObjectRequest putObjectRequest = new PutObjectRequest(ossInfo.bucketName, DIRECTORY + key, new ByteArrayInputStream(content.getBytes())); ObjectMetadata metadata = new ObjectMetadata(); metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString()); metadata.setObjectAcl(CannedAccessControlList.PublicRead); putObjectRequest.setMetadata(metadata); ossClient.putObject(putObjectRequest); ossClient.shutdown(); |
可以看到实际上是生成了一个 OSS 上的公共读对象,通过复杂的文件名来提供一定的安全性,并且满足可以在服务器上访问的的链接。
那么下面修改的目标就是提供一个选项,决定是否生成带有过期时间的链接
。
开发
开发环境搭建
需求明确后先要搭建开发环境。
ArthasHotSwap 插件使用 gradle 进行工程的组织。
使用 gradle 可能会遇到依赖下载速度缓慢的问题,考虑将 build.gradle
文件中调整一下源:
1 2 3 4 5 6 7 8 |
repositories { // mavenCentral() maven{ url 'https://maven.aliyun.com/repository/central/'} maven{ url 'https://maven.aliyun.com/repository/public/'} maven{ url 'https://maven.aliyun.com/repository/google/'} maven{ url 'https://maven.aliyun.com/repository/gradle-plugin/'} } |
开发插件还会用到 gradle-intellij-plugin 。在配置文件中声明的版本可能也需要下载,这个速度也可能很慢,考虑直接替换成本地的 IDEA:
1 2 3 4 5 6 7 8 9 10 |
// See https://github.com/JetBrains/gradle-intellij-plugin/ intellij { // version '2020.3.3' // type 'IC' version '2021.2' localPath '/Users/l/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/212.4746.92/IntelliJ IDEA.app' updateSinceUntilBuild false sameSinceUntilBuild false } |
其中 localPath
替换成自己的目录即可,version
也是必须配置的。
这两步完成后可能可以加快投入开发的时间。
插件结构
开发首先看文档:https://plugins.jetbrains.com/docs/intellij/plugin-structure.html
关注 src/main/resources/META-INF/plugin.xml
。
一些常规的字段之外,本次修改最关键的部分,就是配置窗口的声明:
1 2 3 4 |
<projectConfigurable parentId="tools" instance="com.xxxtai.arthas.dialog.SettingDialog" id="com.xxxtai.arthas.dialog.SettingDialog" displayName="ArthasHotSwap"/> <projectService serviceImplementation="com.xxxtai.arthas.domain.AppSettingsState"/> |
从这里可以看到入口是 com.xxxtai.arthas.dialog.SettingDialog
这个类。
同时因为目标是提供修改配置的功能,参考文档:https://plugins.jetbrains.com/docs/intellij/settings-guide.html 。
结合文档以及配置,ArthasHotSwap 插件提供的是项目级别的配置功能,并且目录中位于 Tools 目录下:
修改方法
增加一个配置,整体方法就明确了:
- 选择合适的显示组件
- 增加配置字段
- 功能层面读取字段完成功能分支
选择合适的显示组件
可以考虑 CheckBox 形式,勾选上表示 true。
注意 com.xxxtai.arthas.dialog.SettingDialog
是配置文件中的入口,而此类会调用 com.xxxtai.arthas.dialog.AppSettingsComponent
对象构建面板。
修改 com.xxxtai.arthas.dialog.AppSettingsComponent
,增加对象以及获取以及赋值方法:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public class AppSettingsComponent { private final JPanel myMainPanel; private final JBTextField ossEndpointText = new JBTextField(); // ... private final JBCheckBox privateAccessRadioButton = new JBCheckBox(); public AppSettingsComponent() { myMainPanel = FormBuilder.createFormBuilder() .addLabeledComponent(new JBLabel("Enter OSS Endpoint: "), ossEndpointText, 1, false) // ... .addLabeledComponent(new JBLabel("Private OSS access: "), privateAccessRadioButton, 1, false) .addComponentFillVertically(new JPanel(), 0) .getPanel(); } public JPanel getPanel() { return myMainPanel; } public JComponent getPreferredFocusedComponent() { return ossEndpointText; } @NotNull public String getOssEndpointText() { return ossEndpointText.getText(); } public void setOssEndpointText(@NotNull String newText) { ossEndpointText.setText(newText); } // ... public boolean getPrivateAccess() { return privateAccessRadioButton.isSelected(); } public void setPrivateAccess(boolean privateAccess) { privateAccessRadioButton.setSelected(privateAccess); } } |
增加配置字段
com.xxxtai.arthas.dialog.AppSettingsComponent
会在 com.xxxtai.arthas.dialog.SettingDialog
中通过配置文件中的 com.xxxtai.arthas.domain.AppSettingsState
读取和存储配置文件。
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 27 28 29 30 31 |
@State( name = "com.xxxtai.arthas.domain.AppSettingsState", storages = {@Storage("setting.xml")} ) public class AppSettingsState implements PersistentStateComponent<AppSettingsState> { public String endpoint = ""; public String accessKeyId = ""; public String accessKeySecret = ""; public String bucketName = ""; public String selectJavaProcessName = ""; public String specifyJavaHome = ""; public boolean privateAccess = false; public static AppSettingsState getInstance(@NotNull Project project) { return ServiceManager.getService(project, AppSettingsState.class); } @Nullable @Override public AppSettingsState getState() { return this; } @Override public void loadState(@NotNull AppSettingsState state) { XmlSerializerUtil.copyBean(state, this); } } |
增加变量之余,上面的 @State
注解也声明了存储位置。
功能层面读取字段完成功能分支
此处不多描述,即 com.xxxtai.arthas.facade.impl.OssFacadeImpl#uploadString
会给脚本返回 OSS 对象链接,需要加密也是需要在此处理。即读取配置后,决定是否返回加密链接。
测试
逻辑开发完成之后,项目 gradle 任务中有一个 runIde
任务,运行此任务之后,会启动一个测试 IDEA 实例,可以在此 IDE 中进行功能的测试。
此处可见刚才的私有访问配置已经添加成功:
配置也存储到@State
注解配置对应文件中:
至此,一个插件的修改过程结束。安装上可以通过 gradle 打包 fatJar 再进入 IDEA 进行安装即可。
至于如何增加全局配置等其他功能的开发,之后有时间再记录。