欲善其事,必先利其器!
就在不久之前,Java领域的开发神器IntelliJ IDEA终于迎来2021年的一个重要的大版本更新:IntelliJ IDEA 2021.1。
现如今大量的Java开发者深度依赖着这款开发软件,正如网上的段子所言:“可以毫不夸张地说,多少Java程序员离开了IDEA就不会写代码了(狗头)”,由此可见其使用的广泛程度。
新版本一出来,我也迫不及待地想尝试一番。当然,主力开发机我是不敢乱升级的,所以这两天,我在一台平时用来做测试的老开发本子上更新了全新的IDEA。
软件启动界面打开的那一瞬间,我就知道事情并不简单。
更新后,全新的启动页面更加花里胡哨了。
软件启动速度也是非常之快,就我这多年苦练的火箭般手速,都差点没截来下面这张启动页面图。
JetBrains提供的Space这个功能不知道大家有没有听说过,讲白了就是一套集成的团队协作环境,可以提供包括构建交付、聊天协作、团队管理以及项目管理等在内的一整套协作一体化解决方案。
最新的IDEA 2021.1把Space环境给无缝地集成进来了,现在属于开箱即用的状态,软件的右上角就有快捷入口:
这个功能相信对于很多依赖Windows系统以及WSL功能的用户来说,简直是喜大普奔!
以前WSL就算再好用,但是你的IDE并没有和它打通,多少总是一个遗憾。
这下好了,二者直接打通了,IDEA支持WSL 2。你可以直接在新版IDEA 2021.1中运行并开发WSL 2中的Java项目,包括Gradle类型项目和Maven类型等项目均支持。
Run Targets这个功能的意思有点类似于上面刚聊过的WSL 2。它允许开发者直接在远程主机甚至在Docker容器上运行和调试项目。
所以到目前为止,新版IDEA 2021允许开发者可以在本地、WSL 2、SSH远程主机、Docker等目标上运行项目,可以说贼香了!
这也算是一个比较重磅的更新。
近两年来,Java版本的发布速度也是快如老狗,我还在用Java 8,它都淦到Java 16了。
关于Java 16的新特性,我还准备写篇文章来详细聊一聊呢,包括比如:
这次IDEA 2021版的一个很重要的更新就是加入了对Java 16的基本支持,注意是基本支持。
除此之外IDEA还新增了几项检查机制,典型比如更加智能的数据流分析检查。
链式构造方式的优化格式设置等等。
目的都是为了帮助提升可读性,进一步提升用户体验。
Code With Me是一项用于协作开发与结对编程的服务,可以实Host-Guest模式的“手摸手”(滑稽)结对编程和群体编程。
目前,新版IDEA开箱即用地支持了Code With Me功能,同时它还具有音频通话和视频通话功能,可以满足随时随地的沟通需求,这操作简直骚到爆。
版本控制
版本控制这一块目前做了不少的更新,包括可以更快地完成PR的创建提交,支持PR模板。
变更提交至代码库前的自定义代码检查配置。
以及支持自定义Git提交模板等等。
以前在IDEA中预览网页得跳到外部浏览器,而现如今IDE的编辑器内部就支持Built-in级别的网页预览,只需要在右上角点那个IDEA小图标即可激活,而且可以编辑网页源码时做到同步更新和预览。
在Windows平台的新版IDEA上,可直接在任务栏(或开始菜单)上右键快捷呼出最近使用的项目。
讲白了,就是使搜索范围更易于自定义,我们可以直接在设置中进行Scope定义,自行选择External Dependencies的范围是否包含。
当你对编辑器里的多个文件进行垂直窗口拆分时,双击某个Tab就可以将当前文件窗口最大化,再次双击Tab则会还原。
当然除了这些之外,新版IDEA还新增了很多更新和增强,比如:对Kotlin、Scala、JavaScript等语言的开发优化、对常见框架与技术的优化和支持、对Kubernetes和Docker的更新支持和改进、数据库工具的更新支持等等,由于时间有限,在此就不一一赘述了,有需要的可以按需细究。
最后,让我们一起大喊一句:“IDEA,yyds!”
Java代码的时候,经常会涉及到重复性的操作,这个时候就会想要是有这样一个插件就好了,如果是大家都会遇到的场景,IDE或许已经提供了,再不然也有可能有人编写了相关的插件。
要是这个操作是你们的编码环境特有的,那就只能自己写工具了。所以这里来学学如何编写IDEA插件,让自己的编程环境更加强大,更好的进行装x。
开发IDEA插件有以下这些依赖:
你可能已经安装了Ultimate版本,但是你还是需要安装[IDEA][]的社区版本。因为商业版是闭源的,所以在调试时无法调试核心代码。
社区版的安装包里是不包含源码的,所以我们需要手动从github上clone一份:
git clone --depth 1 git://git.jetbrains.org/idea/community.git idea
关于从源码运行IDEA的方法参考 Check Out And Build Community Edition:
http://www.jetbrains.org/intellij/sdk/docs/basics/checkout_and_build_community.html
虽然不知道原因,但是根据Check Out And Build Community Edition:
http://www.jetbrains.org/intellij/sdk/docs/basics/checkout_and_build_community.html
我们需要建立一个 IDEA jdk来运行插件:
除非你在Mac上使用官方JDK,否则你需要手动添加 /lib/tools.jar到classpath中。关注微信公众号:匠心零度,在后台回复:idea,可以获取我整最新 IDEA 教程,都是干货。
打开 File|ProjectStructure新建一个 IntelliJPlatformSDK:
Java SDK选择我们刚刚建立的 IDEA jdk:
然后我们可以把下载的IDEA社区版源码添加到源码路径中,这样在调试时,就可以调试IDEA自身的代码了:
我们来编写一个最简单的插件来学习编写一个插件的完整步骤。
选择 IntellJPlatformPlugin,然后Project SDK指定刚刚新建的plugin sdk:
新建的插件项目:
插件根目录下有两个目录 src和 resources。src是插件代码目录, resource是插件资源目录,其中 META-INF/plugin.xml是插件的描述文件,就像Java web项目的 web.xml一样。
plugin.xml默认的内容如下:
<idea-plugin> <id>com.your.company.unique.plugin.id</id> <name>Plugin display name here</name> <version>1.0</version> <vendor email="support@yourcompany.com" url="http://www.yourcompany.com">YourCompany</vendor> <description><!\[CDATA\[ Enter short description for your plugin here.<br> <em>most HTML tags may be used</em> \]\]></description> <change-notes><!\[CDATA\[ Add change notes here.<br> <em>most HTML tags may be used</em> \]\]> </change-notes> <!\-\- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting\_started/build\_number_ranges.html for description --> <idea-version since-build="145.0"/> <!\-\- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting\_started/plugin\_compatibility.html on how to target different products --> <!\-\- uncomment to enable plugin in all products <depends>com.intellij.modules.lang</depends> --> <extensions defaultExtensionNs="com.intellij"> <!\-\- Add your extensions here --> </extensions> <actions> <!\-\- Add your actions here --> </actions> </idea-plugin>
插件扩展IDEA最常见的方式就是在菜单栏或者工具栏中添加菜单项,用户通过点击菜单项来触发插件功能。IDEA提供了 AnAction类,这个类有一个虚方法 actionPerformed,这个方法会在每次菜单被点击时调用。
新建一个自定义的Action有两个步骤:
1、 继承 AnAction类,在 actionPerformed方法中实现插件逻辑 2、 注册action,有两种方式,通过代码注册和通过 plugin.xml注册
我们先写一个简单的Action类:
publicclass TextBoxes extends AnAction { // 如果通过Java代码来注册,这个构造函数会被调用,传给父类的字符串会被作为菜单项的名称 // 如果你通过plugin.xml来注册,可以忽略这个构造函数 public TextBoxes() { // 设置菜单项名称 super("Text _Boxes"); // 还可以设置菜单项名称,描述,图标 // super("Text _Boxes","Item description",IconLoader.getIcon("/Mypackage/icon.png")); } public void actionPerformed(AnActionEvent event) { Project project = event.getData(PlatformDataKeys.PROJECT); String txt= Messages.showInputDialog(project, "What is your name?", "Input your name", Messages.getQuestionIcon()); Messages.showMessageDialog(project, "Hello, " \+ txt + "!\n I am glad to see you.", "Information", Messages.getInformationIcon()); } }
然后我们在 plugin.xml中注册这个Action:
<actions> <group id="MyPlugin.SampleMenu" text="_Sample Menu" description="Sample menu"> <add-to-group group-id="MainMenu" anchor="last" /> <action id="Myplugin.Textboxes"class="Mypackage.TextBoxes" text="Text _Boxes" description="A test menu item" /> </group> </actions>
这里我们新建了一个菜单组,其中text字符串的下划线表示这个字母作为快捷键。这个菜单显示的效果如下:
除了手动新建Action,IDEA还提供了快速新建的方法,在代码目录上点击新建,可以看到Action:
可以在这个面板中填写你要新建的Action信息,IDEA会帮你新建类,还有在plugin.xml中帮你注册:
运行插件特别简单,和运行普通Java代码一样,点击运行或者调试的按钮,就会启动一个新的IDEA实例,这个实例中插件是生效的。
点击Text Boxes就可以看到插件的效果了。
EasyCode是一款专为IntelliJ IDEA设计的代码生成工具插件,旨在通过自动化和模板化的方式,极大地提高Java开发者的编码效率和代码质量。这款插件深度融合于IDEA的开发环境中,为开发者带来全新的开发体验,让编程变得更加轻松愉快。
优点:
缺点:
综上所述,EasyCode作为一款高效的IDEA插件,通过自动化和模板化的方式,为Java开发者带来了极大的便利和效率提升。尽管存在一些缺点,但在大多数情况下,它仍然是提高开发效率、保持代码一致性的有力工具。
❝
其实这么古老的插件大家应该都知道,我就简单讲述一下。并且逆向生成也有很多的方式,这篇文章主要是记录下生成的模板配置信息,方便后续使用。
image.png
1.通过IDEA自带的数据库工具连接上数据库
image.png
image.png
2.配置EasyCode生成的模板
*注:点击上面的+是创建分组,输入分组名创建后点击里面的+是创建该分组的模板,后续生成的时候选择其分组就行
3.选择数据表,鼠标右键选择EasyCode来生成模板
image.png
image.png
*注:Package和Path选到要生成的模块级就行了(模块包名会根据模板自动创建,不要自己去创),比如:xxx/src/main/java/com/xx/包名
##定义初始变量
#set($tableName = $tool.append($tableInfo.name, "Controller"))
##设置回调
$!callback.setFileName($tool.append($tableName, ".java"))
$!callback.setSavePath($tool.append($tableInfo.savePath, "/controller"))
##拿到主键
#if(!$tableInfo.pkColumn.isEmpty())
#set($pk = $tableInfo.pkColumn.get(0))
#end
#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.#{end}controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import $!{tableInfo.savePackageName}.domain.$!{tableInfo.name};
import $!{tableInfo.savePackageName}.service.$!{tableInfo.name}Service;
import $!{tableInfo.savePackageName}.common.R;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import java.io.Serializable;
import java.util.List;
/**
* $!{tableInfo.comment}($!{tableInfo.name})控制层
*
* @author ycc
* @since $!time.currTime()
*/
@RestController
@RequestMapping("$!tool.firstLowerCase($tableInfo.name)")
public class $!{tableName} {
/**
* 服务对象
*/
@Resource
private $!{tableInfo.name}Service $!tool.firstLowerCase($tableInfo.name)Service;
/**
* 分页查询所有数据
*/
@GetMapping
public R page(@RequestParam int current,@RequestParam int size){
Page<$!{tableInfo.name}> page=new Page<>(current,size);
return R.ok($!{tool.firstLowerCase($tableInfo.name)}Service.page(page));}
/**
* 通过主键查询单条数据
*/
@GetMapping("{id}")
public R selectOne(@PathVariable Serializable id){
return R.ok($!{tool.firstLowerCase($tableInfo.name)}Service.getById(id));}
/**
* 新增数据
*/
@PostMapping
public R save(@RequestBody $!{tableInfo.name} $!tool.firstLowerCase($tableInfo.name)){
return R.ok($!{tool.firstLowerCase($tableInfo.name)}Service.save($!tool.firstLowerCase($tableInfo.name)));}
/**
* 修改数据
*/
@PutMapping
public R updateById(@RequestBody $!{tableInfo.name} $!tool.firstLowerCase($tableInfo.name)){
return R.ok($!{tool.firstLowerCase($tableInfo.name)}Service.updateById($!tool.firstLowerCase($tableInfo.name)));}
/**
* 单条/批量删除数据
*/
@DeleteMapping
public R delete(@RequestParam List<Long> id){
return R.ok($!{tool.firstLowerCase($tableInfo.name)}Service.removeByIds(id));
}
}
##定义初始变量
#set($tableName = $tool.append($tableInfo.name, "Service"))
##设置回调
$!callback.setFileName($tool.append($tableName, ".java"))
$!callback.setSavePath($tool.append($tableInfo.savePath, "/service"))
##拿到主键
#if(!$tableInfo.pkColumn.isEmpty())
#set($pk = $tableInfo.pkColumn.get(0))
#end
#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.#{end}service;
import $!{tableInfo.savePackageName}.domain.$!{tableInfo.name};
import com.baomidou.mybatisplus.extension.service.IService;
/**
* $!{tableInfo.comment}($!{tableInfo.name})服务接口
*
* @author ycc
* @since $!time.currTime()
*/
public interface $!{tableName} extends IService<$!tableInfo.name> {}
##定义初始变量
#set($tableName = $tool.append($tableInfo.name, "ServiceImpl"))
##设置回调
$!callback.setFileName($tool.append($tableName, ".java"))
$!callback.setSavePath($tool.append($tableInfo.savePath, "/service/impl"))
##拿到主键
#if(!$tableInfo.pkColumn.isEmpty())
#set($pk = $tableInfo.pkColumn.get(0))
#end
#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.#{end}service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import $!{tableInfo.savePackageName}.domain.$!{tableInfo.name};
import $!{tableInfo.savePackageName}.mapper.$!{tableInfo.name}Mapper;
import $!{tableInfo.savePackageName}.service.$!{tableInfo.name}Service;
import org.springframework.stereotype.Service;
/**
* $!{tableInfo.comment}($!{tableInfo.name})服务实现类
*
* @author ycc
* @since $!time.currTime()
*/
@Service
public class $!{tableName} extends ServiceImpl<$!{tableInfo.name}Mapper, $!{tableInfo.name}> implements $!{tableInfo.name}Service {}
##定义初始变量
#set($tableName = $tool.append($tableInfo.name, "Mapper"))
##设置回调
$!callback.setFileName($tool.append($tableName, ".java"))
$!callback.setSavePath($tool.append($tableInfo.savePath, "/mapper"))
##拿到主键
#if(!$tableInfo.pkColumn.isEmpty())
#set($pk = $tableInfo.pkColumn.get(0))
#end
#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.#{end}mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import $!{tableInfo.savePackageName}.domain.$!tableInfo.name;
/**
* $!{tableInfo.comment}($!{tableInfo.name})数据库访问层
*
* @author ycc
* @since $!time.currTime()
*/
@Mapper
public interface $!{tableName} extends BaseMapper<$!tableInfo.name> {}
##引入mybatis支持
$!{mybatisSupport.vm}
##设置保存名称与保存位置
$!callback.setFileName($tool.append($!{tableInfo.name}, "Mapper.xml"))
$!callback.setSavePath($tool.append($modulePath, "/src/main/resources/mapper"))
##拿到主键
#if(!$tableInfo.pkColumn.isEmpty())
#set($pk = $tableInfo.pkColumn.get(0))
#end
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="$!{tableInfo.savePackageName}.mapper.$!{tableInfo.name}Mapper">
<resultMap type="$!{tableInfo.savePackageName}.domain.vo.$!{tableInfo.name}VO" id="$!{tableInfo.name}VOMap">
#foreach($column in $tableInfo.fullColumn)
<result property="$!column.name" column="$!column.obj.name" jdbcType="$!column.ext.jdbcType"/>
#end
</resultMap>
</mapper>
##引入宏定义
$!{define.vm}
##使用宏定义设置回调(保存位置与文件后缀)
#save("/domain", ".java")
##使用宏定义设置包后缀
#setPackageSuffix("domain")
##使用全局变量实现默认包导入
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* $!{tableInfo.comment}($!{tableInfo.comment})表
*
* @author ycc
* @since $!time.currTime()
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class $!{tableInfo.name}{
private static final long serialVersionUID= 1L;
#foreach($column in $tableInfo.pkColumn)
#if(${column.comment})/**
* ${column.comment}
*/#end
@TableId(value = "$!{column.obj.name}" , type = IdType.NONE)
private $!{tool.getClsNameByFullName($column.type)} $!{column.name};
#end
#foreach($column in $tableInfo.otherColumn)
#if(${column.comment})
/**
* ${column.comment}
*/
#end
@TableField(value = "$!{column.obj.name}")
private $!{tool.getClsNameByFullName($column.type)} $!{column.name};
#end
}
##引入宏定义
$!{define.vm}
##使用宏定义设置回调(保存位置与文件后缀)
#save("/domain/dto", "DTO.java")
##使用宏定义设置包后缀
#setPackageSuffix("domain.dto")
import $!{tableInfo.savePackageName}.domain.$!{tableInfo.name};
##使用全局变量实现默认包导入
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* $!{tableInfo.comment}($!{tableInfo.name})DTO
*
* @author ycc
* @since $!time.currTime()
*/
##使用宏定义实现类注释信息
@Data
@NoArgsConstructor
public class $!{tableInfo.name}DTO extends $!{tableInfo.name}{
}
##引入宏定义
$!{define.vm}
##使用宏定义设置回调(保存位置与文件后缀)
#save("/domain/vo", "VO.java")
##使用宏定义设置包后缀
#setPackageSuffix("domain.vo")
import $!{tableInfo.savePackageName}.domain.$!{tableInfo.name};
##使用全局变量实现默认包导入
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* $!{tableInfo.comment}($!{tableInfo.name})VO
*
* @author ycc
* @since $!time.currTime()
*/
##使用宏定义实现类注释信息
@Data
@NoArgsConstructor
public class $!{tableInfo.name}VO extends $!{tableInfo.name}{
}
VO(View Object)、DTO(Data Transfer Object)和DOMAIN(领域模型)是三种常见的用于数据传递和处理的类型,它们各自在不同的场景下发挥着重要作用。下面分别解释它们的区别和使用场景。
定义:VO通常用于表示视图层(如JSP、HTML页面等)与控制器(Controller)之间的数据传输对象。它封装了需要展示给用户的数据,但不包含业务逻辑。
使用场景:
定义:DTO是一种设计模式,用于在系统不同层(如表现层与业务层之间,或微服务之间的远程调用)之间传输数据。DTO主要用于封装跨层或跨系统边界传输的数据。
使用场景:
定义:DOMAIN是领域驱动设计(Domain-Driven Design, DDD)中的一个核心概念,表示业务领域内的一组具有业务含义和规则的类及它们之间的关系。Domain类通常包含业务逻辑和状态信息。
使用场景:
在实际项目中,应根据具体需求和场景来选择合适的类型,以达到最佳的开发效率和系统性能。
❝
"非常感谢您一直观看到现在,这些看似基础的内容,实则构建了我们知识的基石。我惊喜地发现,将这些基础知识系统地记录下来,不仅是对自我学习的一种巩固,也是一件非常有意义且值得分享的事情。您也完全可以这样做,让我们一起在基础中深耕,发现更多可能。"
原文:https://juejin.cn/post/7392513123860365339
作者:世界哪有真情
#记录我的2024#
*请认真填写需求信息,我们会在24小时内与您取得联系。