IDEA 插件开发实战
一. 简介
二.原理
2.1 背景
2.2 基本原理
组件模型
Application level components,在IDEA启动的时候创建和初始化,可以使用 getComponent(Class) 获取它们。 Project level components,在IDEA中每个Project实例创建的,甚至可以为未打开的项目创建组件,可以使用 getComponent(Class)方法从Project实例中获取它们。 Module level components,它们是为IDEA中加载的每个项目中每个模块创建,使用getComponent(Class)方法可以从Module实例获取模块级别组件。
创建,调用构造函数 初始化,initComponent调用该方法(如果组件实现ApplicationComponent接口) 配置,保存和加载每个组件的状态。(PersistentStateComponent和JDOMExternalizable,实例化配置)。 注册,对于模块组件,将调用接口的moduleAdded方法ModuleComponent将模块添加到项目中,对于项目组件,调用接口的projectOpened方法ProjectComponent加载项目。 保存配置,JDOMExternalizable,PersistentStateComponent的调用。 输出,disposeComponent调用输出。
线程模型
后台流程管理
讯息传递
2.3 小结
三.api
3.1 框架结构
3.2 常用API介绍
VFS
提供一个处理文件的通用API,而不关心文件的具体位置(无论文件位于磁盘上、归档文件中还是HTTP服务器上)。 追踪文件变化,并且在检测到文件内容发生更改时能提供新旧两个版本的文件。 建立文件在VFS和持久化存储之间的关联。
File ioFile = new File("./io.java")
VritualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(ioFile)
virtualFile.refresh(false, true)
WriteCommandAction.runWriteCommandAction(project, new Runnable() {
@Override
public void run() {
// virtualFile.getInputStream() / virtualFile.getOutputStream()
}
});
ApplicationManager.getApplication().invokeLater(new Runnable(){
...
})
PSI
//创建目录
PsiDirectory baseDir =PsiDirectoryFactory.getInstance(project).createDirectory(project.getBaseDir());
//创建Java文件
PsiJavaFile psiFile = (PsiJavaFile) PsiFileFactory.getInstance(project).createFileFromText("",StdFileTypes.JAVA, "");
//创建Xml文件
XmlFile psiFile = (XmlFile) PsiFileFactory.getInstance(project).createFileFromText("",StdFileTypes.XML, "");
PsiClass clazz =JavaDirectoryService.getInstance().createClass(subDir, className)
//还有通过freemarker模板建立class类。
PsiClass view = myFactory.createInterface("View");
psiClass.add(view);
PsiJavaFile javaFile = (PsiJavaFile) psiClass.getContainingFile();
PsiPackage psiPackage = myDirectoryService.getPackage(directory);
javaFile.setPackageName(psiPackage.getQualifiedName());
psiClass.getModifierList().setModifierProperty(PsiModifier.PUBLIC,true);
四.实例架构

五.准备工作

六.编码
BaseAnAction
public abstract class BaseAnAction extends AnAction {
private AnActionEvent anActionEvent;
private DataContext dataContext;
private Presentation presentation;
private Module module;
private IdeView view;
private ModuleType moduleType;
private Project project;
private PsiDirectory psiDirectory;
private DialogBuilder builder;
private PsiFile file;
private JavaDirectoryService javaDirectoryService;
private MysqlJdbc mysqlJdbc = MysqlJdbc.getMysqlJdbc();
private PropertiesUtil properties = PropertiesUtil.getConfigProperties();
public void init(AnActionEvent anActionEvent) {
this.javaDirectoryService = new JavaDirectoryServiceImpl();
this.anActionEvent = anActionEvent;
IdeView ideView = (IdeView)anActionEvent.getRequiredData(LangDataKeys.IDE_VIEW);
this.psiDirectory = ideView.getOrChooseDirectory();
this.project = this.psiDirectory.getProject();
}
public PropertiesUtil getProperties() {
return this.properties;
}
public MysqlJdbc getMysqlJdbc() {
return this.mysqlJdbc;
}
public PsiDirectory getPsiDirectory() {
return this.psiDirectory;
}
public JavaDirectoryService getJavaDirectoryService() {
return this.javaDirectoryService;
}
public AnActionEvent getAnActionEvent() {
return this.anActionEvent;
}
public void setAnActionEvent(AnActionEvent anActionEvent) {
this.anActionEvent = anActionEvent;
}
public DataContext getDataContext() {
return this.dataContext;
}
public void setDataContext(DataContext dataContext) {
this.dataContext = dataContext;
}
public Module getModule() {
return this.module;
}
public void setModule(Module module) {
this.module = module;
}
public IdeView getView() {
return this.view;
}
public void setView(IdeView view) {
this.view = view;
}
public ModuleType getModuleType() {
return this.moduleType;
}
public void setModuleType(ModuleType moduleType) {
this.moduleType = moduleType;
}
public Project getProject() {
return this.project;
}
public void setProject(Project project) {
this.project = project;
}
public DialogBuilder getBuilder() {
return this.builder;
}
public void setBuilder(DialogBuilder builder) {
this.builder = builder;
}
public PsiFile getFile() {
return this.file;
}
public void setFile(PsiFile file) {
this.file = file;
}
public Presentation getPresentation() {
return this.presentation;
}
public void setPresentation(Presentation presentation) {
this.presentation = presentation;
}
@Override
public void update(AnActionEvent e) {
try {
this.presentation = e.getPresentation();
this.onMenuUpade(e, (PsiFile)e.getData(DataKeys.PSI_FILE), ((IdeView)LangDataKeys.IDE_VIEW.getData(e.getDataContext())).getOrChooseDirectory());
}
catch (Exception exception) {
// empty catch block
}
}
public void show() {
this.presentation.setEnabled(true);
this.presentation.setVisible(true);
}
public void hide() {
this.presentation.setEnabled(false);
this.presentation.setVisible(false);
}
public void onMenuUpade(AnActionEvent e, PsiFile file, P
CodeComponent
public class CodeComponent implements ApplicationComponent {
@Override
public void initComponent() {
// TODO: insert component initialization logic here
}
@Override
public void disposeComponent() {
// TODO: insert component disposal logic here
}
@Override
@NotNull
public String getComponentName() {
if ("CreateMicroServiceProjectComponent" == null) {
CodeComponent.reportNull(0);
}
return "CreateMicroServiceProjectComponent";
}
private static void reportNull(int n) {
throw new IllegalStateException(String.format("@NotNull method %s.%s must not return null", "com/code/action/CodeComponent", "getComponentName"));
MMS_DO.java.ft
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
import lombok.Data;
import java.util.*;
import java.math.BigDecimal;
/**
* @author ${USER} E-mail:${E_MAIL}
* @version 创建时间:${DATE} ${TIME}
* ${doCalssName}DO对象
*/
@Data
public class ${doCalssName}DO {
}
CreateServiceAction
public class CreateServiceAction extends BaseAnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
this.init(anActionEvent);
String serviceName = Messages.showInputDialog((String)"Service name", (String)"Create service", (Icon)Messages.getInformationIcon());
Map param = new HashMap();
param.put("doCalssName", BaseUtils.firstLetterUpperCase(BaseUtils.markToHump(serviceName, "_", null)));
param.put("tableName", serviceName);
PsiDirectory implDir = this.getPsiDirectory().findSubdirectory("impl");
if (implDir == null) {
implDir = this.getPsiDirectory().createSubdirectory("impl");
}
PsiClass servicePsiClass = this.getJavaDirectoryService().createClass(this.getPsiDirectory(), "", "MMS_Service", false, param);
String packagePath = servicePsiClass.getQualifiedName();
String implT = "impl";
param.put(implT, packagePath + ";");
param.put("serviceName", serviceName);
this.getJavaDirectoryService().createClass(implDir, "", "MMS_ServiceImpl", false, param);
}
}
GitHub项目

七.部署
导入插件

效果展示




public class CreateDTOAction extends BaseAnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
this.init(anActionEvent);
String doName = Messages.showInputDialog((String)"DO name", (String)"Create DTO", (Icon)Messages.getInformationIcon());
final PsiElementFactory factory = JavaPsiFacade.getInstance((Project)this.getProject()).getElementFactory();
HashMap param = new HashMap();
String doClassName = BaseUtils.firstLetterUpperCase(BaseUtils.markToHump(doName.substring(0, doName.length() - 2), "_", null));
param.put("doCalssName", doClassName);
GlobalSearchScope searchScope = GlobalSearchScope.allScope((Project)this.getProject());
PsiPackage psiPackage = JavaPsiFacade.getInstance((Project)this.getProject()).findPackage("com.lm.model");
PsiClass[] doPsiClasss = psiPackage.findClassByShortName(doName, searchScope);
PsiClass doPsiClass = doPsiClasss[0];
PsiClass dtoPsiClass = JavaPsiFacade.getInstance((Project)this.getProject()).findClass(doPsiClass.getQualifiedName(), searchScope);
final PsiField[] psiFields = dtoPsiClass.getFields();
final PsiClass psiClass = this.getJavaDirectoryService().createClass(this.getPsiDirectory(), "", "MMS_DTO", false, param);
WriteCommandAction.runWriteCommandAction((Project)this.getProject(), (Runnable)new Runnable(){
@Override
public void run() {
for (PsiField psiField : psiFields) {
String comment = psiField.getDocComment().getText().replaceAll("\\*", "").replaceAll("/", "").replaceAll(" ", "").replaceAll("\n", "");
StringBuffer fieldStrBuf = new StringBuffer(psiField.getDocComment().getText()).append("\nprivate ").append(psiField.getType().getPresentableText()).append(" ").append(psiField.getName()).append(";");
psiClass.add((PsiElement)factory.createFieldFromText(fieldStrBuf.toString(), (PsiElement)psiClass));
}
}
});
}
}
八.总结
九.作者介绍
李孟,目前就职于知因智慧数据科技有限公司,负责数据中台数据引擎基础架构设计和中间件开发,专注云计算大数据方向。