李锋镝的博客

  • 首页
  • 时间轴
  • 说说
  • 读书观影
  • 系列
  • AI工具集
  • 论坛
  • 友圈
    • 友链
    • 朋友圈
    • 游客中心
  • 留言
    • 留言
    • 走心评论
  • 关于
    • 关于本站
    • Now
    • 每日心情
    • 网站地图
    • 网站统计
    • 赞助
  • 星球穿梭
  • 🚇开往
静亦定 动亦定
惟坚韧者始能遂其志
  1. 首页
  2. 转载
  3. 技术
  4. 正文

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

2020年11月6日 252 0 0
本文最后更新于 2020年11月6日,距今已 2191 天,其中的信息可能已经发生变化,请注意甄别。

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定义文件:

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

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

public XXXJavaInlineCommentContextType() {
    super("上下文id", "名称", 上下文基础类型);
}

@Override
public 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一样使用xxxService
GenericService 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包放到指定目录即可。

 

原文链接:https://mp.weixin.qq.com/s/49ER9lf37VWgYX1pxOsZlg

参考链接

1
  1. 1https://mp.weixin.qq.com/s/49ER9lf37VWgYX1pxOsZlgmp.weixin.qq.com
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
分享到

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

也可使用浏览器菜单中的「分享」功能

微信扫一扫分享

标签: IDEA JAVA
最后更新:2020年11月6日

岁月同一天 11 月 6 日

回望过去的今天,你在写什么

  • AI
    1 年前 2025年11月6日
    AI能写代码,却造不出软件:软件工程的核心竞争力从未改变

    “我用AI拼了个Demo,就差上线了,能帮我变成产品吗?” 最近越来越多非技术从业者带着这样的需求找上门——他们可能是律…

  • 代码人生
    1 年前 2025年11月6日
    西蒙学习法实战指南:6个月攻克新领域,从入门到能用的高效路径

    作为IT从业者,我们每天都面临新技术的冲击——今天是AI大模型,明天是云原生,后天是低代码开发。有限的时间与无限的学习需…

  • AI
    1 年前 2025年11月6日
    从Token到计费:大模型背后的文本编码逻辑与商业本质

    你是否有过这样的困惑?——给大模型发一段500字的中文需求,却被扣除了800多个Token;同样一篇文章,在GPT-4中…

  • 6 年前 2020年11月6日
    8条经验轻松上手IDEA插件开发

    IDEA作为我们(后端Java开发者)必不可少的IDE,以其智能的代码提示、多样的框架支持、简洁的开发界面等特性,被业界…

  • 其他
    7 年前 2019年11月6日
    架构师究竟比高级开发厉害在哪?

    目前我在互联网公司里干了1年多,接触了多位技术和业务的架构师,由于我正在升级到架构师,所以能直观地感受到高级开发和架构的…

  • 转载
    7 年前 2019年11月6日
    如何在面试中介绍自己的项目经验

    在面试时,经过寒暄后,一般面试官会让介绍项目经验 。常见的问法是,说下你最近的(或最拿得出手的)一个项目。 根据我们的面…

相关文章
  • JAVA关键字之volatile关键字说明2019年10月11日
  • MapStruct深度解析:从原理到实战,告别BeanUtil的性能与安全痛点2025年10月22日
  • 九种常用的UML图总结2019年6月29日
  • SpringBoot基于redis的分布式锁的实现(源码)2019年7月20日
  • 从零搭建Spring Cloud Gateway网关(三)——报文结构转换2020年7月13日

李锋镝

既然选择了远方,便只顾风雨兼程。

打赏 点赞
< 上一篇
下一篇 >
1234567891112131415161718192021222324252627282930313233343536373839404142434446474849505152535455575859606162636465666769727476777879808182858687909293949596979899
取消回复
…

文章评论

还没有评论,快来抢沙发吧~

黄沙百战穿金甲,不破楼兰终不还。

听点儿音乐吧 朋友~
文章目录
最新 热点 随机
最新 热点 随机
给服务器加了WAF 又一个逆天改命的人:小小徐 AI的生成能力等于生产力吗? NGINX严重漏洞(CVE-2026-15176、CVE-2026-93479、CVE-2026-97205) Kratos-plus v1.1.27更新说明 php2shell远程执行漏洞修复(CVE-2026-109618 和 CVE-2026-109619)
给主题增加了游客中心功能心气没了,干什么都不得劲我觉得是时候公布我的导航网站了游本昌去世关于主题加载速度优化的一点儿小演进给主题增加了Now、每日心情、年度回顾、岁月同一天、随机漫步等功能
支撑全网40%网站的WordPress正在重新拥抱PHP生态 今天七夕节呀~~ 【PostgreSQL 15】PostgreSQL 15对UNIQUE和NULL的改进 深入解析 localhost 与 127.0.0.1:不止于“本地访问”的技术细节 Gemini 3 深度解析:从像素级复刻到 AGI 雏形,多模态 AI 如何重构开发与创作? 从零搭建Spring Cloud Gateway网关(三)——报文结构转换
最近评论
Hary 发布于 19 小时前(11月05日) 前几天看博友发的宝塔防火墙不就是一坨吗,还是算了。这WAF是可以直接部署在网站同一台服务器么
李锋镝 发布于 1 天前(11月05日) 感谢来访,祝越来越好~
李锋镝 发布于 1 天前(11月05日) 应该就是请求太频繁了,我看看合并一下静态资源这些
不凡 发布于 1 天前(11月05日) 我打开这篇文章时,看到评论列表中的操作系统图标、浏览器图标和国家图标这三个都挂了,楼下的头像也挂了,...
李锋镝 发布于 1 天前(11月05日) 触发CC防护了 我把限制改大一些
标签聚合
日常 K8s ElasticSearch MySQL AI编程 架构 Claude WordPress AI SQL IDEA 数据库 Theme 多线程 SpringBoot JVM Spring Redis 分布式 JAVA
友情链接
  • 韩小韩博客
  • Honesty
  • 刘永博客
  • 皮皮社
  • 志文工作室
  • lijie blog
  • 知向前端
  • 老张博客
  • sssr7844的博客
  • Mr.Sun的博客
  • 瓦匠个人小站
  • 林羽凡
  • 韩情脉脉
  • Serendipity
  • 哥斯拉
  • 搬砖日记
  • 懋和道人
  • 彬红茶日记
  • 九仞之行

COPYRIGHT © 2016-2026 lifengdi.com. ALL RIGHTS RESERVED.

lifengdi.com 十年之约虫洞

Domain age badge for lifengdi.com

Theme Kratos-plus By Dylan Li

津ICP备2024022503号-3

京公网安备11011502039375号