若依代码生成器原理velocity模板引擎(自用)

1.源码分析

代码生成器:导入表结构(预览、编辑、删除、同步)、生成前后端代码

代码生成器表结构说明:

若依提供了两张核心表来存储导入的业务表信息:

gen_table存储业务表的基本信息 ,它对应于配置代码基本信息和生成信息的页面

gen_table_column存储业务表的字段信息 它对应于配置代码字段信息的页面。

这两张表是一对多的关系,一张业务表可以有多个字段的信息,所以在字段信息表中有个外键table_id指向

代码生成器目录结构:

查询数据库列表

当管理员在界面上点击导入按钮时,会弹出一个对话框,此时,前端需要向后端发送请求,查询数据库并返回到前端,展示当前项目库中所有待导入的业务表。

前端:

此功能涉及前端相关的代码位于views/tool/index.vue这个视图组件中,负责实现导入业务表的用户界面和交互逻辑。

/** 打开导入表弹窗 */
function openImportTable() {proxy.$refs["importRef"].show();
}

后端:

后端处理逻辑则在代码生成模块的GenController中,负责接收前端的请求,处理业务逻辑,并返回查询结果。

/*** 查询数据库列表*/
@PreAuthorize("@ss.hasPermi('tool:gen:list')")
@GetMapping("/db/list")
public TableDataInfo dataList(GenTable genTable)
{startPage();List<GenTable> list = genTableService.selectDbTableList(genTable);return getDataTable(list);
}

具体的执行流程如下图:

导入表结构:

前端:此功能涉及前端相关的代码位于views/tool/importTable.vue这个视图组件中,负责实现导入业务表的用户界面和交互逻辑。

/** 导入按钮操作 */
function handleImportTable() {const tableNames = tables.value.join(",");if (tableNames == "") {proxy.$modal.msgError("请选择要导入的表");return;}importTable({ tables: tableNames }).then(res => {proxy.$modal.msgSuccess(res.msg);if (res.code === 200) {visible.value = false;emit("ok");}});
}

后端:后端处理逻辑则在代码生成模块的GenController中,负责接收前端的请求,处理业务逻辑,保存业务表的基本信息和字段信息

/*** 导入表结构(保存)*/
@PreAuthorize("@ss.hasPermi('tool:gen:import')")
@Log(title = "代码生成", businessType = BusinessType.IMPORT)
@PostMapping("/importTable")
public AjaxResult importTableSave(String tables)
{// 将表名字符串转换为数组 tb_task_collect,tb_vendout_runningString[] tableNames = Convert.toStrArray(tables);// 查询表信息List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);// 导入表结构(保存)genTableService.importGenTable(tableList, SecurityUtils.getUsername());return success();
}

具体的执行的流程如下图:

生成代码

首先管理员,选中需要下载的业务表,并点击生成按钮来触发代码生成并下载的过程。

前端随后向后端发送请求,这个请求会告知服务器需要生成代码的业务表。

前端:

负责实现这一功能的前端代码位于views/tool/index.vue这个视图组件中,负责实现生成业务表的用户界面和交互逻辑。

/** 生成代码操作 */
function handleGenTable(row) {const tbNames = row.tableName || tableNames.value;if (tbNames == "") {proxy.$modal.msgError("请选择要生成的数据");return;}if (row.genType === "1") {genCode(row.tableName).then(response => {proxy.$modal.msgSuccess("成功生成到自定义路径:" + row.genPath);});} else {proxy.$download.zip("/tool/gen/batchGenCode?tables=" + tbNames, "ruoyi.zip");}
}

后端:后端的逻辑处理则在代码生成模块的GenController中,这里是处理前端请求、执行代码生成逻辑,将生成的代码字节流通过HTTP响应返回给客户端。

/*** 批量生成代码*/
@PreAuthorize("@ss.hasPermi('tool:gen:code')")
@Log(title = "代码生成", businessType = BusinessType.GENCODE)
@GetMapping("/batchGenCode")
public void batchGenCode(HttpServletResponse response, String tables) throws IOException
{// 将表名字符串转换为数组 tb_task_collect,tb_vendout_runningString[] tableNames = Convert.toStrArray(tables);// 根据表名下载生成的代码字节数组byte[] data = genTableService.downloadCode(tableNames);// 将生成的代码字节流通过HTTP响应返回给客户端genCode(response, data);
}

具体的执行的流程如下图:

问题分析

我们已经对代码生成器的工作原理有了一定的了解,接下来我们解决一些项目中使用的问题,比如:

每次生成代码都需要修改作者,去除实体类前缀过于繁琐,现在我们可以修改generator.yml配置文件来调整为自己项目的

# 代码生成
gen:# 作者author: itheima# 默认生成包路径 system 需改成自己的模块名称 如 system monitor toolpackageName: com.dkd.manage# 自动去除表前缀,默认是falseautoRemovePre: true# 表前缀(生成类名不会包含表前缀,多个用逗号分隔)tablePrefix: sys_,tb_

velocity模板引擎

  • Velocity官网:https://velocity.apache.org
  • Velocitv是一个|ava模板引擎,它使用特定语法在模板中嵌入lava对象数据,实现界面与代码的分离

常见的应用场景:

  • Web内容生成 : 生成动态Web页面。

  • 代码生成 : 生成Java源代码、SQL脚本、XML配置文件等。

  • 网页静态化 : 生成静态网页。

入门

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>velocity快速入门</title>
</head>
<body><h3>心怀梦想,坚持不懈,成功即在前方。加油少年!!</h3></body>
</html>

准备模板

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>velocity快速入门</title>
</head>
<body><h3>心怀梦想,坚持不懈,成功即在前方。${message}</h3></body>
</html>

上述代码中的 加油少年 修改为了 ${message} 这是一个动态变量(占位符),方便动态填充数据

数据填充

编写java代码实现数据填充,并生成文件

package com.dkd.test;import com.dkd.generator.util.VelocityInitializer;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;import java.io.FileWriter;
import java.util.List;public class VelocityDemoTest {public static void main(String[] args) throws Exception {//1. 初始化模板引擎VelocityInitializer.initVelocity();//2. 准备数据模型VelocityContext velocityContext = new VelocityContext();velocityContext.put("message", "加油朋友!!");//3. 读取模板Template template = Velocity.getTemplate("vm/index.html.vm", "UTF-8");//4. 渲染模板FileWriter fileWriter = new FileWriter("D:\\workspace\\index.html");template.merge(velocityContext, fileWriter);fileWriter.close();}
}

基础语法-变量

Velocity中的变量有两类

  • 在模板中定义变量: #set开头,比如 #set($name = "velocity")

  • 获取变量的的值: $name 或者 ${name}

下面是案例,基于刚才的入门案例模板改进

##双#号 是vm的注释

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>velocity快速入门</title>
</head>
<body><h3>心怀梦想,坚持不懈,成功即在前方。${message}</h3>
##定义变量
#set ($name = "velocity")##输出变量
第一种情况:${name} <br>
第二种情况:$name## 第三种情况:orderService#set($column = "order")
字符串拼接:${column}Service <br>
</body>
</html>

对象的定义获取

在ruoyi-generator模块下新增一个区域的实体类

package com.dkd.test;import com.dkd.common.annotation.Excel;
import com.dkd.common.core.domain.BaseEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 区域管理对象 tb_region** @author itheima* @date 2024-06-05*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Region extends BaseEntity {private static final long serialVersionUID = 1L;/*** 主键ID*/private Long id;/*** 区域名称*/@Excel(name = "区域名称")private String regionName;}

准备模型数据

package com.dkd.test;import com.dkd.generator.util.VelocityInitializer;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;import java.io.FileWriter;
import java.util.List;public class VelocityDemoTest {public static void main(String[] args) throws Exception {//1. 初始化模板引擎VelocityInitializer.initVelocity();//2. 准备数据模型VelocityContext velocityContext = new VelocityContext();velocityContext.put("message", "加油朋友!!");// 创建区域对象Region region = new Region(1L, "北京北五环");velocityContext.put("region", region);//3. 读取模板Template template = Velocity.getTemplate("vm/index.html.vm", "UTF-8");//4. 渲染模板FileWriter fileWriter = new FileWriter("D:\\workspace\\index.html");template.merge(velocityContext, fileWriter);fileWriter.close();}
}

动态模板展示数据

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>velocity快速入门</title>
</head>
<body><h3>心怀梦想,坚持不懈,成功即在前方。${message}</h3>## 定义变量
#set($name = "velocity")## 输出变量
第一种情况:${name} <br>
第二种情况:$name <br>## 第三种情况:orderService
#set($column = "order")
字符串拼接:${column}Service <br>
<hr>## 获取区域对象中的数据
区域ID:$region.id <br>
区域名称:${region.regionName} <br>
<hr>
</body>
</html>

基础语法-循环

循环的语法:#foreach(...) ... #end

##定义一个集合
#set($list = ["春", "夏", "秋", "冬"])    ## 遍历循环
#foreach($item in $list)序号[$foreach.count] $item <br> ## count从1开始 index从0开始
#end

准备模型数据

// 创建区域对象
Region region1 = new Region(1L, "北京北五环");
Region region2 = new Region(2L, "北京北四环");
velocityContext.put("region", region1);
List<Region> regionList = List.of(region1, region2);
velocityContext.put("regionList", regionList);

动态模板展示数据

## 遍历区域
#foreach($item in $regionList)序号[$foreach.count],区域ID:$item.id,区域名称:$item.regionName <br>
#end
</br>

基础语法-if判断

判断的语法:#if(condition) ... #elseif(condition) ... #else ... #end

##定义变量
#set($score=80)## if判断
#if($score>=80)优秀
#elseif($score>=60)及格
#else不及格
#end
## 对象obj不为空才会执行里面的逻辑
#if($obj) ..... #end## 对象obj为空才会执行里面的逻辑
#if(!$obj) ..... #end

模板阅读

我们不需要使用velocity去开发新的模板,若依已经提供好了,在它基础上进行调整即可

下面这个是关于实体类的模板

package ${packageName}.domain;## 根据列类型获取导入包
#foreach ($import in $importList)
import ${import};
#end
## 导入Apache Commons Lang库,用于对象的toString方法
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
## 导入项目自定义的Excel注解,用于生成Excel文档
import com.dkd.common.annotation.Excel;
#if($table.crud || $table.sub)
## 如果表具有CRUD操作或子表,继承BaseEntity
import com.dkd.common.core.domain.BaseEntity;
#elseif($table.tree)
## 如果表是树形结构,继承TreeEntity
import com.dkd.common.core.domain.TreeEntity;
#end/*** ${functionName}对象 ${tableName}** @author ${author}* @date ${datetime}*/
#if($table.crud || $table.sub)#set($Entity="BaseEntity")
#elseif($table.tree)#set($Entity="TreeEntity")
#end
public class ${ClassName} extends ${Entity}{   ## 定义类的序列化版本ID
private static final long serialVersionUID = 1L;
## 根据表的列定义实体类的属性
#foreach ($column in $columns)## 如果不是父类的属性,则生成属性#if(!$table.isSuperColumn($column.javaField))/** $column.columnComment */## 如果字段需要在列表中展示,使用Excel注解标记#if($column.list)#set($parentheseIndex=$column.columnComment.indexOf("("))#if($parentheseIndex != -1)#set($comment=$column.columnComment.substring(0, $parentheseIndex))#else#set($comment=$column.columnComment)#end#if($parentheseIndex != -1)@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")#elseif($column.javaType == 'Date')@JsonFormat(pattern = "yyyy-MM-dd")@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")#else@Excel(name = "${comment}")#end#endprivate $column.javaType $column.javaField;#end
#end
## 如果表有子表,定义子表信息的集合
#if($table.sub)
/** $table.subTable.functionName信息 */
private List<${subClassName}> ${subclassName}List;#end
## 为每个属性字段生成getter和setter方法
#foreach ($column in $columns)#if(!$table.isSuperColumn($column.javaField))#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))#set($AttrName=$column.javaField)#else#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})#endpublic void set${AttrName}($column.javaType $column.javaField){this.$column.javaField = $column.javaField;}public $column.javaType get${AttrName}(){return $column.javaField;}#end
#end
## 如果表有子表,生成子表信息的getter和setter方法
#if($table.sub)
public List<${subClassName}> get${subClassName}List(){return ${subclassName}List;}public void set${subClassName}List(List<${subClassName}> ${subclassName}List){this.${subclassName}List = ${subclassName}List;}#end
## 重写toString方法,使用Apache Commons Lang的ToStringBuilder
@Override
public String toString() {return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)#foreach ($column in $columns)#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))#set($AttrName=$column.javaField)#else#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})#end.append("${column.javaField}", get${AttrName}())#end#if($table.sub).append("${subclassName}List", get${subClassName}List())#end.toString();}}

Lombok集成

目前,我们已经基本熟悉了velocity的作用和一些语法,那接下来,我们就通过这些知识来去改造若依框架的代码生成部分

common模块的pom.xml中添加lombok坐标

<!--  lombok工具-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>

修改模板

enerator模块的domain.java.vm模板中添加lombok注解

package ${packageName}.domain;## 根据列类型获取导入包
#foreach ($import in $importList)
import ${import};
#end
## 导入项目自定义的Excel注解,用于生成Excel文档
import com.dkd.common.annotation.Excel;
#if($table.crud || $table.sub)
## 如果表具有CRUD操作或子表,继承BaseEntity
import com.dkd.common.core.domain.BaseEntity;
#elseif($table.tree)
## 如果表是树形结构,继承TreeEntity
import com.dkd.common.core.domain.TreeEntity;
#end
## 注意lombok导包
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;/*** ${functionName}对象 ${tableName}** @author ${author}* @date ${datetime}*/
#if($table.crud || $table.sub)
#set($Entity="BaseEntity")
#elseif($table.tree)
#set($Entity="TreeEntity")
#end
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ${ClassName} extends ${Entity}{   ## 定义类的序列化版本ID
private static final long serialVersionUID=1L;
## 根据表的列定义实体类的属性
#foreach ($column in $columns)## 如果不是父类的属性,则生成属性#if(!$table.isSuperColumn($column.javaField))/** $column.columnComment */## 如果字段需要在列表中展示,使用Excel注解标记#if($column.list)#set($parentheseIndex=$column.columnComment.indexOf("("))#if($parentheseIndex != -1)#set($comment=$column.columnComment.substring(0, $parentheseIndex))#else#set($comment=$column.columnComment)#end#if($parentheseIndex != -1)@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")#elseif($column.javaType == 'Date')@JsonFormat(pattern = "yyyy-MM-dd")@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")#else@Excel(name = "${comment}")#end#endprivate $column.javaType $column.javaField;#end
#end
## 如果表有子表,定义子表信息的集合
#if($table.sub)
/** $table.subTable.functionName信息 */
private List<${subClassName}> ${subclassName}List;#end
}
  • 正常添加了关于lombok的注解

  • 删除了set 、 get 、toString 等方法

可以把生成后的代码,拷贝到项目中,如果订单管理能够正常访问和操作,就算修改成功了,后期再次生成的代码,全部都支持lombok

Swagger集成

generator模块的 controller.java.vm模板中添加Swagger注解

package ${packageName}.controller;import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.dkd.common.annotation.Log;
import com.dkd.common.core.controller.BaseController;
import com.dkd.common.core.domain.AjaxResult;
import com.dkd.common.enums.BusinessType;
import ${packageName}.domain.${ClassName};
import ${packageName}.service.I${ClassName}Service;
import com.dkd.common.utils.poi.ExcelUtil;
#if($table.crud || $table.sub)
import com.dkd.common.core.page.TableDataInfo;
#elseif($table.tree)
#end
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;/*** ${functionName}Controller** @author ${author}* @date ${datetime}*/
@Api(tags = "${functionName}Controller")
@RestController
@RequestMapping("/${moduleName}/${businessName}")
public class ${ClassName}Controller extends BaseController
{@Autowiredprivate I${ClassName}Service ${className}Service;/*** 查询${functionName}列表*/@ApiOperation("查询${functionName}列表")@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')")@GetMapping("/list")
#if($table.crud || $table.sub)public TableDataInfo list(${ClassName} ${className}){startPage();List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});return getDataTable(list);}
#elseif($table.tree)public AjaxResult list(${ClassName} ${className}){List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});return success(list);}
#end/*** 导出${functionName}列表*/@ApiOperation("导出${functionName}列表")@PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')")@Log(title = "${functionName}", businessType = BusinessType.EXPORT)@PostMapping("/export")public void export(HttpServletResponse response, ${ClassName} ${className}){List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);util.exportExcel(response, list, "${functionName}数据");}/*** 获取${functionName}详细信息*/@ApiOperation("获取${functionName}详细信息")@PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')")@GetMapping(value = "/{${pkColumn.javaField}}")public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}){return success(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}));}/*** 新增${functionName}*/@ApiOperation("新增${functionName}")@PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')")@Log(title = "${functionName}", businessType = BusinessType.INSERT)@PostMappingpublic AjaxResult add(@RequestBody ${ClassName} ${className}){return toAjax(${className}Service.insert${ClassName}(${className}));}/*** 修改${functionName}*/@ApiOperation("修改${functionName}")@PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')")@Log(title = "${functionName}", businessType = BusinessType.UPDATE)@PutMappingpublic AjaxResult edit(@RequestBody ${ClassName} ${className}){return toAjax(${className}Service.update${ClassName}(${className}));}/*** 删除${functionName}*/@ApiOperation("删除${functionName}")@PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')")@Log(title = "${functionName}", businessType = BusinessType.DELETE)@DeleteMapping("/{${pkColumn.javaField}s}")public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s){return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s));}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/76905.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何制定有效的风险应对计划

制定有效的风险应对计划的核心在于&#xff1a; 识别潜在风险、评估风险的影响与概率、选择合适的应对策略、建立动态监控和反馈机制。 其中&#xff0c;识别潜在风险是最为关键的第一步。只有准确识别出可能的风险&#xff0c;才能在后续的评估、应对、监控等环节中做到有的放…

A2A协议实现详解及示例

A2A协议概述 A2A (Agent2Agent) 是Google推出的一个开放协议&#xff0c;旨在使AI智能体能够安全地相互通信和协作。该协议打破了孤立智能体系统之间的壁垒&#xff0c;实现了复杂的跨应用自动化。[1] A2A协议的核心目标是让不同的AI代理能够相互通信、安全地交换信息以及在各…

【中级软件设计师】前趋图 (附软考真题)

【中级软件设计师】前趋图 (附软考真题) 目录 【中级软件设计师】前趋图 (附软考真题)一、历年真题三、真题的答案与解析答案解析 复习技巧&#xff1a; 若已掌握【前趋图】相关知识&#xff0c;可直接刷以下真题&#xff1b; 若对知识一知半解&#xff0c;建议略读题目&#x…

调节磁盘和CPU的矛盾——InnoDB的Buffer Pool

缓存的重要性 无论是用于存储用户数据的索引【聚簇索引、二级索引】还是各种系统数据&#xff0c;都是以页的形式存放在表空间中【对一个/几个实际文件的抽象&#xff0c;存储在磁盘上】如果需要访问某页的数据&#xff0c;就会把完整的页数据加载到内存中【即使只访问页中的一…

springboot和springcloud的区别

1. ‌目的与功能‌ ‌1)Spring Boot‌: 主要用于快速构建独立的、生产级的 Spring 应用程序。它通过自动配置和嵌入式服务器等特性,简化了微服务的开发、启动和部署,使开发者能够专注于业务逻辑而非繁琐的配置。‌Spring Boot是一个快速开发的框架,旨在简化Java应用程序的开…

耘想WinNAS:以聊天交互重构NAS生态,开启AI时代的存储革命

一、传统NAS的交互困境与范式瓶颈 在传统NAS&#xff08;网络附加存储&#xff09;领域&#xff0c;用户需通过复杂的图形界面或命令行工具完成文件管理、权限配置、数据检索等操作&#xff0c;学习成本高且效率低下。例如&#xff0c;用户若需搜索特定文件&#xff0c;需手动…

在断网的时候,websocket 一直在CLOSING 状态

现象 websocket 先连接成功&#xff0c;然后断网。 由于维护了一套心跳机制&#xff0c;前端发起心跳&#xff0c;如果一段时间内没有收到服务端返回的心跳。则表示连接断开。 用心跳的方式处理断网的兜底情况。 然而&#xff0c;此时网络是断开的&#xff0c;在代码中直接调…

基于AWS的大模型调用场景:10大成本优化实战方案

大模型训练与推理是AI领域的计算密集型场景&#xff0c;如何在AWS上实现高性能与低成本的双重目标&#xff1f;本文从实例选型、弹性伸缩、存储优化等角度&#xff0c;分享10个经过验证的AWS成本优化策略&#xff0c;帮助企业节省30%以上成本。 一、大模型场景的成本痛点分析 计…

【网络原理】TCP/IP协议五层模型

目录 一. 协议的分层 二. OSI七层网络协议 三. TCP/IP五层网络协议 四. 网络设备所在分层 五. 封装 六. 分用 七. 传输中的封装和分用 八. 数据单位术语 一. 协议的分层 常见的分层为两种OSI七层模型和TCP/IP五层模型 为什么要协议分层&#xff1f; 在网络通信中&…

科技快讯 | 阿里云百炼MCP服务上线;英伟达官宣:CUDA 工具链将全面原生支持 Python

李飞飞团队最新AI报告&#xff1a;中美模型性能差距近乎持平 4月8日&#xff0c;斯坦福大学以人为本人工智能研究所发布《2025年人工智能指数报告》。报告显示&#xff0c;2023年AI性能显著提升&#xff0c;AI应用加速&#xff0c;投资增长&#xff0c;中美AI模型差距缩小。报告…

猫咪如厕检测与分类识别系统系列【三】融合yolov11目标检测

✅ 前情提要 家里养了三只猫咪&#xff0c;其中一只布偶猫经常出入厕所。但因为平时忙于学业&#xff0c;没法时刻关注牠的行为。我知道猫咪的如厕频率和时长与健康状况密切相关&#xff0c;频繁如厕可能是泌尿问题&#xff0c;停留过久也可能是便秘或不适。为了更科学地了解牠…

2025年燃气证书:传承与发展的行业纽带

回溯历史长河&#xff0c;能源的利用与人类文明的发展息息相关。从远古时期的钻木取火&#xff0c;到如今广泛应用的燃气能源&#xff0c;每一次能源的变革都推动着社会的巨大进步。而在现代燃气行业蓬勃发展的背后&#xff0c;燃气从业人员资格证书正扮演着传承与发展的重要纽…

在Ubuntu下进行单片机开发是否需要关闭Secure Boot

1. Secure Boot的作用 功能&#xff1a;Secure Boot是UEFI的安全功能&#xff0c;旨在阻止未经验证的驱动或操作系统启动&#xff0c;防止恶意软件篡改引导过程。 影响范围&#xff1a;它主要限制的是操作系统启动阶段加载的内核级驱动&#xff08;如显卡驱动、虚拟化模块&…

国达陶瓷重磅推出陶瓷罗马柱外墙整装尖端新产品“冠岩臻石”

近日&#xff0c;记者在佛山国达建材有限公司&#xff08;以下简称国达陶瓷&#xff09;董事长杨建平处了解到&#xff0c;该公司重磅推出的“冠岩臻石”新产品&#xff0c;是属于陶瓷罗马柱外墙整装产品中的尖端产品。新产品自面市之后&#xff0c;深受高端用户的青睐与认可。…

【分享】Ftrans文件摆渡系统:既保障传输安全,又提供强集成支持

【分享】Ftrans文件摆渡系统&#xff1a;既保障传输安全&#xff0c;又提供强集成支持&#xff01; 在数字化浪潮中&#xff0c;企业对数据安全愈发重视&#xff0c;网络隔离成为保护核心数据的关键防线&#xff0c;比如隔离成研发网-办公网、生产网-测试网、内网-外网等。网络…

实验一 字符串匹配实验

一、实验目的 1&#xff0e;熟悉汇编语言编程环境和DEBUG调试程序的使用。 2&#xff0e;掌握键盘输入字符串的方法和分支程序的设计。 二、实验内容 编程实现&#xff1a;从键盘分别输入两个字符串&#xff0c;然后进行比较&#xff0c;若两个字符串的长度…

添加登录和注册功能

先写前端再写后端 前提&#xff1a;ideavue3mybatisspringBoot3前后端分离实现对一张表的增删改查&#xff08;完整代码版&#xff09;-CSDN博客 项目地址 1.添加一个Login.vue视图 <template><div class"login_container"><div class"login…

【Windows】系统安全移除移动存储设备指南:告别「设备被占用」弹窗

Windows系统安全移除移动存储设备指南&#xff1a;告别「设备被占用」弹窗 解决移动硬盘和U盘正在被占用无法弹出 一、问题背景 使用Windows系统时&#xff0c;经常遇到移动硬盘/U盘弹出失败提示「设备正在使用中」&#xff0c;即使已关闭所有可见程序。本文将系统梳理已验证…

Springboot下载文件, 文件名中文是乱码, 空格变加号

默认把文件名放上去, 中文会乱码, 文件名种有空格, 就会被截断 public void download(HttpServletResponse response){// 文件名先进行url编码, 避免乱码问题// 把用%20进行替换fileName URLEncoder.encode(fileName, "UTF-8").replace("", "%20&qu…

MySQL 超详细安装教程与常见问题解决方案

一、MySQL 安装教程 1. Windows 系统安装&#xff08;以 MySQL 8.0 为例&#xff09; 步骤 1&#xff1a;下载 MySQL Installer 访问 MySQL 官网下载页面。 选择 Windows (x86, 64-bit), MSI Installer&#xff08;推荐使用完整版 mysql-installer-web-community-8.0.xx.xx.…