1 业务背景
在日常开发中,我们经常需要为不同模块,开发增删改查接口。而这些不同模块的接口及其相似,在不同的模块controller中造成代码大量重复。
2 解决方案
不同模块controller中增删改查接口,具备通用性,可以放置在baseController基础抽象父类中进行复用。
3 具体细节
3.1 自动生成代码
添加依赖
xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!--自动生成增删改查代码-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
<!-- Swagger注解 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0</version>
</dependency>
编写代码生成逻辑,并配置数据源、包名称
typescript
public class CodeGenerator {
public static void main(String[] args) {
// 数据库URL、用户名和密码
String url = "jdbc:mysql://192.168.56.200:3306/db_data_stats?serverTimezone=Asia/Shanghai";
String username = "root";
String password = "123";
// 项目路径
String projectPath = System.getProperty("user.dir") + "/data-stats-app";
// 使用快速生成器
FastAutoGenerator.create(url, username, password)
// 全局配置
.globalConfig(builder -> {
builder.author("your_name") // 设置作者
.enableSwagger() // 开启Swagger文档
//.fileOverride() // 覆盖已有文件
.outputDir(projectPath + "/src/main/java") // 输出目录
.commentDate("yyyy-MM-dd") // 注释日期格式
.dateType(DateType.TIME_PACK); // 使用java8日期类型
})
// 包配置
.packageConfig(builder -> {
builder.parent("data.stats.app") // 父包名
//.moduleName("system") // 模块名
.entity("po") // Entity包名
.service("service") // Service包名
.serviceImpl("service.impl") // ServiceImpl包名
.mapper("mapper") // Mapper包名
.controller("controller") // Controller包名
.pathInfo(Collections.singletonMap(
OutputFile.xml,
projectPath + "/src/main/resources/mapper")); // XML文件输出路径
})
// 策略配置
.strategyConfig(builder -> {
// 设置需要生成的表名
builder.addTablePrefix("tb_") // 根据你的实际表前缀进行配置
.addInclude("tb_test")
// Entity策略配置
.entityBuilder()
.enableLombok() // 开启Lombok
.enableTableFieldAnnotation() // 开启字段注解
.logicDeleteColumnName("deleted") // 逻辑删除字段
.naming(NamingStrategy.underline_to_camel) // 表名映射规则
.columnNaming(NamingStrategy.underline_to_camel) // 列名映射规则
// Controller策略配置
.controllerBuilder()
.enableRestStyle() // 开启RestController
.enableHyphenStyle() // 开启连字符风格
.formatFileName("%sController")
.build() // 这里加上build()
// Service策略配置
.serviceBuilder()
.formatServiceFileName("%sService") // Service接口命名规则
.formatServiceImplFileName("%sServiceImpl") // Service实现类命名规则
// Mapper策略配置
.mapperBuilder()
.enableMapperAnnotation() // 开启@Mapper注解
.formatMapperFileName("%sMapper") // Mapper接口命名规则
.formatXmlFileName("%sMapper"); // XML文件命名规则
})
// 使用Freemarker模板引擎
.templateEngine(new FreemarkerTemplateEngine())
// 执行代码生成
.execute();
}
}
运行上面的代码生成逻辑即可生成service、mapper、model等代码。但是controller类中不会自动生成接口代码。
3.2 通用接口复用
于是我们手动去编写这种增、删、改、查接口,类似如下:
less
@Slf4j
@RestController
@RequestMapping("/llm")
public class Llmv2Controller {
@Resource
private LlmService llmService;
/**
* 模型列表接口
* @return
*/
@GetMapping("/list")
public List<LlmVO> list() {
// 查询模型列表
List<Llm> llmList = llmService.list();
// 模型实体List->转VO类型的List
return BeanConvertUtils.convertList(llmList, LlmVO.class);
}
}
封装一个实体转VO的工具类,支持各种实体转换,避免每次都要重复编写转换代码。
php
/**
* Bean转换工具类
*/
public class BeanConvertUtils {
/**
* 将源对象列表转换为目标对象列表
* @param sourceList 源对象列表
* @param targetClass 目标对象类型
* @return 目标对象列表
*/
public static <S, T> List<T> convertList(List<S> sourceList, Class<T> targetClass) {
return sourceList.stream().map(source -> convert(source, targetClass))
.collect(Collectors.toList());
}
/**
* 将源对象转换为目标对象
* @param source 源对象
* @param targetClass 目标对象类型
* @return 目标对象
*/
public static <S, T> T convert(S source, Class<T> targetClass) {
if (source == null) {
return null;
}
try {
T target = targetClass.getDeclaredConstructor().newInstance();
BeanUtils.copyProperties(source, target);
return target;
} catch (Exception e) {
throw new RuntimeException("Bean conversion failed", e);
}
}
}
我们分析下这个list列表接口的逻辑:
- 定义接口url
- 查询llmService获取列表
- 实体转VO
这个list接口基本流程,除了service对象不一样以外,其他的逻辑都是通用的,可以被复用的。

复用list接口
因此, 基础的通用的接口,放在抽象父类BaseController中进行复用。

继承list接口
scala
/**
* 通用的接口,放在抽象Base中进行复用
* @param <T>
* @param <V>
*/
public abstract class BaseController<T, V, S extends IService<T>> {
protected final S baseService;
private Class<V> voClass;
public BaseController(S baseService, Class<V> voClass) {
this.baseService = baseService;
this.voClass = voClass;
}
/**
* 获取列表
* @return
*/
@GetMapping("/list")
public List<V> list() {
List<T> entityList = baseService.list();
return BeanConvertUtils.convertList(entityList, voClass);
}
}
具体业务控制器类继承抽象父类BaseController中进行复用。
less
/**
* 模型控制器,复用基础的增、删、改、查
*/
@Slf4j
@RestController
@RequestMapping("/llm")
public class Llmv2Controller extends BaseController<Llm, LlmVO, LlmService> {
@Autowired
public Llmv2Controller(LlmService llmService) {
super(llmService, LlmVO.class);
}
}
这样就不必重复写基础的增、删、改、查接口了,直接复用基础父类BaseController中的接口。