8条经验轻松上手IDEA插件开发

2020 年 11 月 5 日 阿里技术

阿里妹导读:本文从IDEA插件的基本概念讲起,通过一个简单的开发实例,介绍IDEA插件开发的过程,并总结了8条实战经验。更详细的IDEA插件开发介绍,可参见官方说明或到官方论坛讨论。


文末福利:轻量应用服务器优惠,新用户专享


IDEA作为我们(后端Java开发者)必不可少的IDE,以其智能的代码提示、多样的框架支持、简洁的开发界面等特性,被业界公认为最好的Java开发工具之一。而一款IDE是否强大,最简单的衡量标准就是查看其插件生态环境的发展情况,多种多样的插件既丰富了IDE自身的功能,同时大大提高了开发人员的工作效率。

一  概念简介

插件类型

IDEA的插件根据功能分为以下4种类型:

  • 自定义语言支持,例如Go语言插件。这种插件包括文件类型识别、格式化、语言保留字支持、编译、运行等语言开发必备功能。属于比较重量级的插件。


  • 开发框架支持,例如Spring框架插件。这种插件包括框架特殊代码识别、框架功能支持等。不同开发框架开发量、难度不同。


  • 工具集成,例如我司内容的云雀,就是这种插件,也是最常用的插件,后续的开发实例也属于这种类型。这种插件一般包括额外的功能、功能相关的UI以及访问外部资源。


  • UI附加,主要限于UI的修改。


开发目录结构


组成部分

  • 配置文件,配置文件就是插件对IDE的自我介绍,IDEA中是META-INF/plugin.xml,详细的配置信息请参见官方文档。


  • ClassLoader,每个插件对应一个ClassLoader,彼此之间隔离(类似于Pandora的插件机制)。


  • Component(组件),插件内部可以有三个级别的组件:Applciation、Project、Module,分别需要在plugin.xml文件配置,并实现不同的接口。


  • 扩展和扩展点(Extesions and Extension Points),扩展用于扩展IDEA自身或者其他组件扩展点的功能,例如添加一个自定义的JavaDoc中的Tag。扩展点是插件提供给其他插件使用的。


  • 动作(Action),动作在配置文件中配置,由某个菜单项触发。


  • 图标(Icon),插件使用到的图标。


  • 服务(Service),用于后端运行的某些服务实现。


  • 依赖(Dependencies),插件可能依赖的其他插件、三方包等。


二  开发过程简述

此部分以一个简单插件开发实例进行说明。

1  创建项目

IDEA插件项目开发时,有两种创建方式,一种是IntelliJ Platform Plugin,另一种是Gradle下的IntelliJ Platform Plugin(在Gradle插件安装的情况下)。推荐使用第二种方式,使用Gradle的方式可以方便的添加第三方依赖库,同时也是官方推荐的方式。

选择好创建方式后,根据需要填写信息即可完成创建。

2  设置创建入口

由于实例插件是一个工具集成类型的插件,我们需要在IDEA的UI添加插件的入口,这部分在配置文件plugin.xml中添加如下内容:

<actions>    <!-- Add your actions here -->    <group id="分组id" text="显示文本1" description="鼠标驻留时的显示">        <add-to-group group-id="MainMenu(这个id指的是IDEA的顶部菜单)" anchor="位置(last等)"/>        <action class="动作类全路径" id="动作类id" text="显示文本2" description="鼠标驻留时的显示"/>    </group></actions>

我们可以发现,入口就是一个Action。需要申明Action的位置和处理类。以上声明的UI效果:


3  编写处理逻辑

在配置文件中指明的动作处理类中添加处理逻辑。具体逻辑根据实际需要。

三  使用总结

1  弹出对话框

使用:
Messages.showErrorDialog(myTabbedPane.getComponent()," 弹出文本内容");


2  提示信息

使用 new Notification(groupId 自定义, 标题, 内容, 类型(info、warning、error)).notify(项目对象实例);


3  扩展点使用

在配置文件中,添加扩展点配置,其他扩展点类型:

<extensions defaultExtensionNs="com.intellij">    <!-- 添加自定义扩展标签,这里的customJavadocTagProvider是IDEA自身申明的 -->    <customJavadocTagProvider implementation="扩展点实现类"/></extensions>

自定义JavaDoc的扩展效果:


4  自定义LiveTemplate

(1)在plugin.xml中配置liveTemplate扩展点的相关实现:

<!-- 自定义LiveTemplate --><defaultLiveTemplatesProvider    implementation="DefaultLiveTemplatesProvider接口的实现类"/><!-- 自定义LiveTemplate上下文,以及上下文可以使用的配置 --><liveTemplateContext    implementation="TemplateContextType类的子类"/>

(2)在 DefaultLiveTemplatesProvider 接口的实现类的 getDefaultLiveTemplateFiles 方法中注册LiveTemplate定义文件:

@Overridepublic String[] getDefaultLiveTemplateFiles() {    //文件名不需要有后缀,例如a.xml,这里只需要输入a    return new String[]{"liveTemplates/文件1", "liveTemplates/文件2"};}

(3)在 TemplateContextType 类的子类的构造方法中定义上下文名称,以及 isInContext 方法中定义上下文可以使用的位置。例如:

public XXXJavaInlineCommentContextType() {    super("上下文id", "名称", 上下文基础类型);}
@Overridepublic boolean isInContext(@NotNull final PsiFile file, final int offset) { if (PsiUtilCore.getLanguageAtOffset(file, offset).isKindOf(JavaLanguage.INSTANCE)) { PsiElement element = file.findElementAt(offset); if (element instanceof PsiWhiteSpace && offset > 0) { element = file.findElementAt(offset-1); } if (null == element) { return false; } return (element.getParent() instanceof PsiInlineDocTag && element.getParent().getParent() instanceof PsiDocTag) || (element.getParent() instanceof PsiInlineDocTag && PsiTreeUtil.getParentOfType(element, PsiField.class, false) != null); } return false;}

(4)编写LiveTemplate定义xml文件,例如:

<templateSet group="分组名"><template name="模板名" value="模板值,可以使用$VAR1$来指代变量位置" description="描述信息" toReformat="false" toShortenFQNames="true">    <variable name="变量名" expression="" defaultValue="" alwaysStopAt="true" />    <context>        <option name="自定义的或者预定义的template上下文id" value="true" />    </context></template></templateSet>

5  插件中调用Dubbo

(1)在Gradle的构建文件build.gradle中的dependencies内添加如下配置:
 
 compile 'org.apache.dubbo:dubbo:2.7.7' compile 'org.apache.dubbo:dubbo-dependencies-zookeeper:2.7.7'

(2)在接口调用处进行如下编码:
 
//将当前线程的classloader备份,并设置当前线程的classloader为当前类的classloader//当前线程的classloader是IDEA的,当前类的classloader是当前插件的//不进行如此设置会造成Dubbo扩展点实现无法在ClassLoader中找到ClassLoader backCl = Thread.currentThread().getContextClassLoader();Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());// 以下内容参考Dubbo的泛化调用// 当前应用配置ApplicationConfig application = new ApplicationConfig();application.setName("应用名");
// 连接注册中心配置RegistryConfig registry = new RegistryConfig();registry.setAddress("zookeeper://127.0.0.1:2181");
// 引用远程服务ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏reference.setApplication(application);reference.setRegistry(registry); // 多个注册中心可以用setRegistries()reference.setInterface("服务全类名");reference.setVersion("服务版本号");reference.setGeneric(true);
// 和本地bean一样使用xxxServiceGenericService genericService = reference.get();//泛化调用Object result = genericService.$invoke("方法名", new String[]{"参数类型"}, new Object[]{"参数值"});System.out.println(result);//恢复classloader设置Thread.currentThread().setContextClassLoader(backCl);

6  发布插件的指定IDEA仓库

(1)在build.gradle文件中进行如下配置

publishPlugin {    host = 'https://xxxx.com'  //仓库地址    username 'onepublish' //仓库指定用户名    password 'onepublish' //仓库密码    token 'onepublish'  //仓库验证token}

(2)执行gradle中publishPlugin任务。

7  发布到指定IDEA插件失败

当我们开发完成后,通过publishPlugin任务发布时,可能会出现以下报错信息:

The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.

这个问题的原因是我们使用的org.jetbrains.intellij版本较高,请使用2.x的版本。或者参照插件的源码自己写一个没有accept的上传方法即可。

相关代码:

package idea;
import retrofit.RestAdapter;import retrofit.client.Request;import retrofit.client.Response;import retrofit.client.UrlConnectionClient;import retrofit.converter.SimpleXMLConverter;import retrofit.mime.TypedFile;import retrofit.mime.TypedString;
import java.io.File;import java.io.IOException;import java.net.HttpURLConnection;
/** * @author lijie * @date 2019/1/17 */public class PublishPluginTest { public static void main(String[] args) { PluginRepositoryService service = new RestAdapter.Builder().setEndpoint("https://插件仓库链接").setClient(new UrlConnectionClient() { @Override protected HttpURLConnection openConnection(Request request) throws IOException { HttpURLConnection connection = super.openConnection(request); connection.setReadTimeout(10 * 60 * 1000); return connection; } }).setLogLevel(RestAdapter.LogLevel.BASIC) .setConverter(new SimpleXMLConverter()) .build() .create(PluginRepositoryService.class); Response response = service.uploadByXmlId(new TypedString(""), new TypedString(""), new TypedString(pluginId), new TypedString("default"), new TypedFile("application/octet-stream", new File(plugin压缩文件路径))); System.out.println(response.getBody()); }}

package idea;
import retrofit.client.Response;import retrofit.http.*;import retrofit.mime.TypedFile;import retrofit.mime.TypedString;
/** * @author lijie * @date 2019/1/17 */public interface PluginRepositoryService { @Multipart @POST("/plugin/uploadPlugin") public Response uploadByXmlId(@Part("userName")TypedString username, @Part("password")TypedString password, @Part("pluginId")TypedString pluginId, @Part("channel")TypedString channel,                                  @Part("file")TypedFile file);}

8  如何在插件中引入本地jar包

在build.gradle的dependencies里边添加如下内容:

compile fileTree(dir:'src/main/resources/lib',includes:['*jar'])

然后,将本地jar包放到指定目录即可。



轻量应用服务器
新用户专享


阿里云开发者成长计划面向全年龄段开发者,依托免费资源、免费体验、免费学习、免费实践 4 大场景,全面助力开发者轻松掌握云上技能。新用户专享轻量应用服务器,内置WordPress等8种主流环境,5M峰值带宽,40GBSSD云盘,1000G流量包,满足学习、搭建应用等场


识别下方二维码,或点击 “阅读原文” ,快去优惠购买吧~


戳我,享优惠。
登录查看更多
0

相关内容

粤港澳大湾区数字经济研究院是一家面向人工智能、数字经济产业和前沿科技的国际化创新型研究机构,坐落于深圳市深港科技创新合作区内。IDEA正与 MSR、Google Brain、DeepMind、OpenAI 等同行者一起推动人类 AI 技术前沿的发展。IDEA 的使命是立足社会需求,研发颠覆式创新技术并回馈社会,让更多的人从数字经济发展中获益。IDEA 秉承共享共赢共生的开源开放精神,积极营造自由而富有激情的创新工作环境,聚集全世界最聪慧的大脑一起创造人类社会最需要的价值。我们坚持科技擎天,产业立地,相信最好的研究从需求中来,到需求中去,最终惠及广大企业和受众。 IDEA 目前已聚集一批包括院士、世界著名大学教授、世界知名开源系统发明人在内的国际一流技术专家,致力于在 AI 基础技术与开源系统、人工智能金融科技、区块链技术与可信计算、企业级 AI 系统、产业智能物联网与智能机器人等领域研发国际顶尖成果,并培育一批国际领先科技企业,带动深圳乃至大湾区万亿级数字经济产业发展。 AIPT(AI 平台技术研究中心)致力于建设支撑人工智能算法、算力和数据的平台,通过具体项目的研发、实施和部署来推进 AI 技术的落地和产业化,团队成立以来,已发布 ReadPaper 论文阅读平台、BIOS 医疗知识图谱两款产品。AIPT 负责人-谢育涛曾任微软公司技术合伙人兼微软(中国)操作系统工程院院长。谢育涛在微软公司工作 20 余年,先后在微软美国总部的 Microsoft Office 产品组、必应团队、微软亚洲互联网工程院以及微软(中国)操作系统工程院、人工智能和云计算等多个研发部门担任重要职务。他在操作系统、搜索技术、人工智能、应用及服务领域拥有丰富的技术与管理经验。
专知会员服务
143+阅读 · 2020年12月28日
专知会员服务
91+阅读 · 2020年12月26日
【2020新书】使用Kubernetes开发高级平台,519页pdf
专知会员服务
66+阅读 · 2020年9月19日
【2020新书】高级Python编程,620页pdf
专知会员服务
235+阅读 · 2020年7月31日
【2020新书】使用高级C# 提升你的编程技能,412页pdf
专知会员服务
57+阅读 · 2020年6月26日
【实用书】Python技术手册,第三版767页pdf
专知会员服务
234+阅读 · 2020年5月21日
【实用书】Python爬虫Web抓取数据,第二版,306页pdf
专知会员服务
117+阅读 · 2020年5月10日
【新书】Java企业微服务,Enterprise Java Microservices,272页pdf
【干货】大数据入门指南:Hadoop、Hive、Spark、 Storm等
专知会员服务
95+阅读 · 2019年12月4日
【电子书】Flutter实战305页PDF免费下载
专知会员服务
22+阅读 · 2019年11月7日
已删除
运维帮
5+阅读 · 2019年7月26日
【开源 】.NET Core MVC快速开发系统
DotNet
4+阅读 · 2019年6月29日
7 款实用到哭的App,只说一遍
高效率工具搜罗
84+阅读 · 2019年4月30日
34个最优秀好用的Python开源框架
专知
9+阅读 · 2019年3月1日
百度开源项目OpenRASP快速上手指南
黑客技术与网络安全
5+阅读 · 2019年2月12日
如何用GitLab本地私有化部署代码库?
Python程序员
9+阅读 · 2018年12月29日
Python 杠上 Java、C/C++,赢面有几成?
CSDN
6+阅读 · 2018年4月12日
6 款特别强大的黑科技App
高效率工具搜罗
4+阅读 · 2017年10月11日
VIP会员
相关VIP内容
专知会员服务
143+阅读 · 2020年12月28日
专知会员服务
91+阅读 · 2020年12月26日
【2020新书】使用Kubernetes开发高级平台,519页pdf
专知会员服务
66+阅读 · 2020年9月19日
【2020新书】高级Python编程,620页pdf
专知会员服务
235+阅读 · 2020年7月31日
【2020新书】使用高级C# 提升你的编程技能,412页pdf
专知会员服务
57+阅读 · 2020年6月26日
【实用书】Python技术手册,第三版767页pdf
专知会员服务
234+阅读 · 2020年5月21日
【实用书】Python爬虫Web抓取数据,第二版,306页pdf
专知会员服务
117+阅读 · 2020年5月10日
【新书】Java企业微服务,Enterprise Java Microservices,272页pdf
【干货】大数据入门指南:Hadoop、Hive、Spark、 Storm等
专知会员服务
95+阅读 · 2019年12月4日
【电子书】Flutter实战305页PDF免费下载
专知会员服务
22+阅读 · 2019年11月7日
相关资讯
已删除
运维帮
5+阅读 · 2019年7月26日
【开源 】.NET Core MVC快速开发系统
DotNet
4+阅读 · 2019年6月29日
7 款实用到哭的App,只说一遍
高效率工具搜罗
84+阅读 · 2019年4月30日
34个最优秀好用的Python开源框架
专知
9+阅读 · 2019年3月1日
百度开源项目OpenRASP快速上手指南
黑客技术与网络安全
5+阅读 · 2019年2月12日
如何用GitLab本地私有化部署代码库?
Python程序员
9+阅读 · 2018年12月29日
Python 杠上 Java、C/C++,赢面有几成?
CSDN
6+阅读 · 2018年4月12日
6 款特别强大的黑科技App
高效率工具搜罗
4+阅读 · 2017年10月11日
Top
微信扫码咨询专知VIP会员