1. 引入 MyBatis Plus
1.1 添加依赖
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Plus Starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version> <!-- 请使用最新版本 -->
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version> <!-- 请使用最新版本 -->
</dependency>
<!-- Lombok (可选,用于简化代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version> <!-- 请使用最新版本 -->
<scope>provided</scope>
</dependency>
</dependencies>
1.2 配置数据库连接
在 application.yml 或 application.properties 文件中配置数据库连接信息
spring:
datasource:
url: jdbc:mysql://localhost:3306/tool?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: username
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
1.3 新建实体类
这边直接修改原来的Menu实体类,添加@TableName(“menu”),对应到表么menu
package com.tool.tooladmin.admin.menu.domain;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
@TableName("menu")
public class Menu {
private Long menuId;
private Long parentId;
private String menuName;
private String path;
private String icon;
private Long sort;
private Long isFrame;
private String query;
@TableField(exist = false)
private List<Menu> children;
}
1.4 创建 Mapper 接口
创建一个 MenuMapper接口,并继承 BaseMapper。MyBatis Plus 提供了丰富的 CRUD 方法,无需手动编写 SQL 语句。
package com.tool.tooladmin.admin.menu.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tool.tooladmin.admin.menu.domain.Menu;
public interface MenuMapper extends BaseMapper<Menu> {
}
1.5 配置 MyBatis Plus
在 Spring Boot 的主配置类中,添加 @MapperScan 注解,扫描 Mapper 接口所在的包。
package com.tool.tooladmin;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.tool.tooladmin.admin.menu.mapper") // 替换为你的 Mapper 接口所在包,可以只加载到个别,也可以设置父级,这样可以扫描到下面所有的mapper
public class ToolAdminApplication {
public static void main(String[] args) {
SpringApplication.run(ToolAdminApplication.class, args);
}
}
1.6 修改前端menu对应的值
前面改了menu实体类,需要修改前端对应的参数
<template>
<div>
<el-menu-item v-if="!item.children || item.children.length === 0" :index="item.path || item.menuId">
<span>{{ item.menuName }}</span>
</el-menu-item>
<el-sub-menu v-else :index="item.path || item.menuId">
<template #title>
<span>{{ item.menuName }}</span>
</template>
<menu-item v-for="child in item.children" :key="child.menuId" :item="child" />
</el-sub-menu>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
item: {
type: Object,
required: true
}
});
</script>
2. 添加获取反编译代码
可以通过测试demo,直接把class反编译成代码,虽然会有一定差异,但是可以节省一部分代码
采用的是cfr方式
2.1 添加依赖
<!-- https://mvnrepository.com/artifact/org.benf/cfr -->
<dependency>
<groupId>org.benf</groupId>
<artifactId>cfr</artifactId>
<version>0.151</version>
</dependency>
2.2 java代码
通过编译后的资源文件地址将class文件反编译成java文件,存在outputdir里,然后再进行读取
package com.tool.tooladmin.tool;
import org.apache.commons.io.FileUtils;
import org.benf.cfr.reader.api.CfrDriver;
import org.benf.cfr.reader.util.getopt.OptionsImpl;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@Component
public class DecompileTool {
/**
* 根据资源路径获取类反编译后的代码
* @param resourcePath 类路径
* @return
*/
public static String getClassDecompileCodeByCFR(String resourcePath) {
try {
// 读取资源文件
ClassPathResource resource = new ClassPathResource(resourcePath + ".class");
File file = resource.getFile();
// 反编译并获取结果字符串
decompile(file.getAbsolutePath());
// 读取反编译后的代码
StringBuffer sb = new StringBuffer();
File decompiledFile = new ClassPathResource("output/"+ resourcePath + ".java").getFile();
List<String> lines = FileUtils.readLines(decompiledFile, "UTF-8");
for (String line : lines) {
sb.append(line).append("\n");
}
String decompiledCode = sb.toString();
return decompiledCode;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static void decompile(String filePath) throws IOException {
// 配置选项
HashMap<String, String> outputMap = new HashMap<>();
// 反编译文件输出目录,在classpath下创建一个文件夹output,用于存放反编译后的代码
String outputDir = new ClassPathResource("/").getFile().getAbsolutePath() + "/output";
outputMap.put("outputdir", outputDir);
OptionsImpl options = new OptionsImpl(outputMap);
// 构建 CfrDriver
CfrDriver cfrDriver = new CfrDriver.Builder()
.withBuiltOptions(options)
.withOutputSink(null)
.build();
// 反编译
List<String> files = new ArrayList<>();
files.add(filePath);
cfrDriver.analyse(files);
}
}
2.3 前端将代码传给MD组件里,进行展示
<template>
<div>
<el-collapse v-model="activeNames">
<el-collapse-item name="1">
<template #title>
<div class="title-container">
<span>{{ title }}</span>
<el-tooltip content="复制" placement="top">
<el-icon class="copy-icon" @click.stop="copyText">
<CopyDocument/>
</el-icon>
</el-tooltip>
</div>
</template>
<div class="content-container">
<div ref="contentToCopy" class="content-to-copy">
<vue3-markdown-it :source="displayContent"/>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script>
import {CopyDocument} from '@element-plus/icons-vue';
export default {
components: {
CopyDocument
},
props: {
title: {
type: String,
required: true
},
contentToCopy: {
type: String,
required: true
},
contentType: {
type: String,
required: true
}
},
data() {
return {
activeNames: []
};
},
computed: {
displayContent() {
//中文乱码转换
let decodedContent = this.contentToCopy.replace(/\\u(\w{4})/g, (a, b) => String.fromCharCode(parseInt(b, 16)));
// 在最前面添加一行代码
return `\`\`\`${this.contentType}\n${decodedContent}`;
}
},
methods: {
async copyText() {
try {
await navigator.clipboard.writeText(this.contentToCopy);
this.$message({
message: '复制成功',
type: 'success'
});
} catch (err) {
this.$message({
message: '复制失败,请手动复制',
type: 'error'
});
}
},
}
};
</script>
<style scoped>
.content-container {
position: relative;
}
.content-to-copy {
background-color: #f0f0f0; /* 设置背景颜色为灰色 */
padding: 10px;
border-radius: 4px;
margin-bottom: 10px;
}
.title-container {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.copy-icon {
cursor: pointer;
font-size: 15px;
color: #409eff;
margin-right: 8px;
}
.copy-icon:hover {
color: #66b1ff;
}
</style>