当前位置: 首页 > 科技观察

将选中的对象批量编织到“x.set(y.get)”代码中,自动生成vo2dto

时间:2023-03-16 15:10:19 科技观察

,转载请联系bugstack公众号。1.前言给了你机会,你却没用!从事编程和开发这些年,我似乎发现,在研发中,大部分不想做的事,都成就了别人。就像部署服务的麻烦一样,用Docker,简单的CRUD不想开发,代码少,方法代码加监控麻烦,无侵入的全链路监控。而这些事情你也在做,因为你没有想法,没有创新,没有思考,也许还没有能力,所以你一直在搬砖,堆砖,砌砖,周而复始,来来回回。键盘打字越来越快,代码越来越烂。工资一直没有提高,头发也越来越少。对于想走技术路线的码农,不要只停留在业务功能的逻辑开发上。只有当你有了通用简洁的逻辑思维,你才会逐渐思考如何把一个重复的东西做成一个通用的服务或者组件,而这些东西的实现不仅需要你会写代码,还需要能够思考和索引一些你需要的技术,并通过自学来补充这部分技能。2.需求目的对象之间要写get和set吗?烦,烦,尤其是DDD四层架构下,加上多层抗污染处理,一层vo2dto,一层vo2do,一层Ado2po,虽然有很多工具可以操作,还是得写。我应该怎么办?不要惊慌,这是一个机会。我们做一个插件来修复它,让它自动为我生成get和set代码。在IDEAPlugin的处理下,选择需要生成目标代码的锚点,将其复制Convertobjects,自动编织成代码,1秒搞定!效果视频:3.案例开发1.项目结构guide-idea-plugin-vo2dto├──.gradle└──src├──main│└──java│└──cn.bugstack.guide.idea.plugin│├──action│└──Vo2DtoGenerateAction.java│├──application││└──IGenerateVo2Dto.java│├──domain│├──model││...─AbstractGenerateVo2Dto.java│└──基础设施│└──Utils.java├──resources│└──META-INF│└──plugin.xml├──build.gradle└──gradle.properties在这个IDEA插件项目中,主要分为4个areas:Action:提供一个菜单栏表单。在插件中,我们把这个菜单栏配置在Generate下,这里就是你平时生成get、set、constructor方法的地方。application:应用层定义接口,定义了生成代码并织入锚点的方法接口。domian:领域层专门负责代码生成和编织动作。该层获取代码中的锚点位置,复制剪贴板信息,应用上下文,解析类中的get和set,最后将生成的代码编织成到达锚点后的操作。infrastructure:提供底层的工具类,用于获取剪贴板信息、锚点位置判断等操作。2.织入代码界面cn.bugstack.guide.idea.plugin.application.IGenerateVo2DtopublicinterfaceIGenerateVo2Dto{voiddoGenerate(Projectproject,DataContextdataContext);}其实是定义界面非常重要的一步,因为这一步定义了生成的标准,所有必须从此界面启动生成操作。学习源代码也是如此。您需要找到一个核心切入点才能更好地开始学习。3.定义模板方法因为生成代码并织入锚点位置的操作实际上是一个整体的一组流程操作,因为在这个流程中需要;获取context信息(即project对象),提取当前锚点位置的类的set方法集,然后读取ctrl+C剪贴板上的信息提取get方法集,第四步是toset,get组合并将代码编织到锚点位置。整体流程如下:使用模板方法后,很容易将写在一个类中的代码块按照职责进行拆分。同时,因为模板的定义,定义了一整套标准的流程,在流程规范下执行代码会更容易,后期添加逻辑迭代功能。4.代码织入锚点在代码织入锚点之前,我们在模板类中定义的方法需要实现接口进行处理。关键点包括:通过CommonDataKeys.EDITOR.getData(dataContext),CommonDataKeys.PSI_ELEMENT.getData(dataContext)封装了GenerateContext对象的上下文信息,即一些类的对象,锚点位置,文档编辑等。通过PsiClass获取光标所在位置对应的Class信息,通过psiClass.getMethods()读取对象方法,过滤掉set方法,封装成集合。通过Toolkit.getDefaultToolkit().getSystemClipboard()获取剪贴板信息,即当你为锚点处的对象生成x.set(y.get)时,复制Yy对象,开始提取get方法,它也被封装到集合中。那么最后就是代码的组装和编织了。这部分我们的代码如下;getApplication();//获取空格位置长度intdistance=Utils.getWordStartOffset(generateContext.getEditorText(),generateContext.getOffset())-generateContext.getStartOffset();application.runWriteAction(()->{StringBuilderblankSpace=newStringBuilder();for(inti=0;isetMtdList=setObjConfigDO。getParamList();for(Stringparam:setMtdList){intlineStartOffset=generateContext.getDocument().getLineStartOffset(lineNumberCurrent++);newWriteCommandAction(generateContext.getProject()){@Overrideprotectedvoidrun(@NotNullResultresult)throwsThrowable{ggenerateContext.getDocument().insertString(lineStartOffset,blankSpace+setObjConfigDO.getClazzParamName()+"."+setObjConfigDO.getParamMtdMap().get(param)+"("+(null==getObjConfigDO.getParamMtdMap().get(param)?"":getObjConfigDO.getClazzParam()+"."+getObjConfigDO.getParamMtdMap().get(param)+"()")+");\n");generateContext.getEditor().getCaretModel()。moveToOffset(lineStartOffset+2);generateContext.getEditor().getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);}}.execute();}});}织入代码的流程动作主要是设置方法set遍历,将对应的x.set(y.get)通过document.insertString编织到具体的位置和代码最后所有生成代码的方法,也就是完成x.set(y.get)的整个过程.5.配置菜单入口plugin.xml这次我们在生成x.set(y.get)代码的操作中加入了一个快捷键,可以让我们操作起来更加方便。4.测试验证点击Plugin启动IDEA插件,然后有2步操作;复制你需要转换的对象,因为复制之后,插件可以获取剪贴板信息,也可以提取get方法集。将鼠标定义到需要转换设置值的对象上,然后鼠标右键选择Generate->Vo2Dto-付兄1.复制对象2.生成对象3.最终效果最后可以看到你所有的对象都转换好了,代码自动生成,是不是很香。如果直接使用快捷键Ctrl+Shift+K,也可以自动生成。5.扩展获取当前编辑文件的接口。可以通过PsiFilepsiFile=e.getData(LangDataKeys.PSI_FILE);获取PsiClass、PsiField等。获取当前项目对象Projectproject=e.getProject();获取数据上下文DataContextdataContext=e.getDataContext();获取到数据上下文后,可以通过CommonDataKeys对象获取File的所有信息
虚拟文件virtualFile=CommonDataKeys.VIRTUAL_FILE.getData(dataContext);GlobalSearchScope有Project域、Moudule域、File域等PsiFile[]psiFiles=FilenameIndex.getFilesByName(project,name,GlobalSearchScope);类似于IDEFindUsages操作Querysearch=ReferencesSearch.search(PsiElem耳鼻喉科);重命名重构newName=RefactoringFactory.getInstance(Project).createRename(PsiElement,"newName");搜索一个类的所有子类,重载方法较多,不一一列举Querysearch=ClassInheritorsSearch.search(PsiClass);根据类的全限定名查询PsiClass。下面的方法是查询Project域PsiClasspsiClass=JavaPsiFacade.getInstance(project).findClass(classQualifiedName,GlobalSearchScope.projectScope(project));获取Java类PackagePsiPackage的位置psiPackage=JavaPsiFacade.getInstance(Project).findPackage(classQualifiedName);查找被特定方法覆盖的方法Querysearch=OverridingMethodsSearch.search(PsiMethod);工程对象的类和方法用于处理操作。这些内容的实践也非常适合你在其他场景中使用,比如为项目的接口生成一些自动化的API操作。在为对象生成x.set(y.get)时,我也在思考如何更合理地将转换对象代入到插件的代码逻辑中。我可能会想到通过弹窗配置或者扫码到上一行。但是这种方法终究是不舒服的。考虑到我们自己编码的习惯性操作,其实我们在做这一步的时候,复制是第一步。为了更好的体验,这里我们选择使用复制来处理这个连接问题。本系列IDEAPlugin的开发是按照DDD工程结构的思想设计实现的。虽然整体内容看起来并不复杂,但希望通过这些框架的积累,能够为DDD的落地做铺垫,让更多的工程研发人员适应DDD结构。