无影的博客 - Java
type: channel
link: http://www.darktool.cn/index.php/category/Java/
items: jasypt-配置文件加密使用说明
items: 自动计算从A向量变换到B向量的旋转矩阵和缩放因子
items: 下载JDK
items: springboot项目通过Jsoup过滤XSS攻击
items: java批量操作sqlite数据库
items: Java通过定时执行线程保持连接，防止超时
items: 通过aspose将word使用表格模板动态创建表格并导出
items: Java通过标准OpenAPI接口调用deepseek
items: 通过aspose将Excel使用模板导出
items: 通过aspose将word使用模板导出

jasypt-配置文件加密使用说明
type: item
date: 2025-12-05T14:00:49+08:00
description: 第一步引入依赖 <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>3.0.5</version> </dependency>第二步application.yml中增加jasypt配置# 如果密文加盐，需要配置盐值 jasypt: encryptor: password: # 密钥（该值不应该写在配置文件中，使用时建议通过【-Djasypt.encryptor.password=盐值】在启动命令传入） algorithm: PBEWITHHMACSHA512ANDAES_256 # 算法 iv-generator-classname: org.jasypt.iv.RandomIvGenerator # 盐生成器第三步加密数据通过Jasypt Jar包加密数据获取Jar包：首先需要拥有 jasypt-x.x.x.jar 文件。如果你使用Maven，可以在本地仓库（通常是 ~/.m2/repository/org/jasypt/jasypt/）找到；也可以在Maven中央仓库下载。执行加密命令：在命令行中运行以下命令（参数根据实际情况替换）：java -cp jasypt-1.9.3.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="明文" password="密钥" algorithm="算法"java -cp jasypt-1.9.3.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="yourpassword" password="yourMasterKey" algorithm=PBEWITHHMACSHA512ANDAES_256 ivGeneratorClassName=org.jasypt.iv.RandomIvGenerator解密：java -cp jasypt-1.9.3.jar org.jasypt.intf.cli.JasyptPBEStringDecryptionCLI input="密文" password="密钥" algorithm="算法"PBEWITHHMACSHA512ANDAES_256算法加密时需要指定盐生成器否则会报错，如：ivGeneratorClassName=org.jasypt.iv.RandomIvGenerator获取结果：命令执行后，在 OUTPUT 部分输出的字符串即为加密后的密文通过Spring Boot 编程方式加密数据@Autowired private StringEncryptor encryptor; public void testEncrypt() { String plainText = "yourpassword"; String encryptedText = encryptor.encrypt(plainText); System.out.println("加密后的密文: " + encryptedText); // 例如输出：qzVuB4VpQYoW0CkIUPNV1A== }第四步在配置文件中使用加密值格式：将密文用 ENC() 包裹起来# application.properties 示例 my.secret.property=ENC(qzVuB4VpQYoW0CkIUPNV1A==)# application.yml 示例 spring: datasource: password: ENC(qzVuB4VpQYoW0CkIUPNV1A==)关键配置：为了让Jasypt在应用启动时能自动解密，必须为其提供用于解密的主密码（Master Password）。出于安全考虑，主密码不应直接写在配置文件中。推荐的做法是通过系统属性、环境变量或启动命令传入java -jar your-application.jar -Djasypt.encryptor.password=yourMasterKey

自动计算从A向量变换到B向量的旋转矩阵和缩放因子
type: item
date: 2025-11-10T09:11:04+08:00
description: 1. 基础数学原理对于两个向量 v₁ 和 v₂，我们要找到旋转矩阵 R 和缩放因子 s，使得：v₂ = s × R × v₁2. Java实现public class VectorTransform { private double[][] rotationMatrix; private double scaleFactor; public VectorTransform(double[] sourceVector, double[] targetVector) { if (sourceVector.length != targetVector.length) { throw new IllegalArgumentException("Vectors must have same dimension"); } calculateTransform(sourceVector, targetVector); } private void calculateTransform(double[] v1, double[] v2) { int n = v1.length; // 计算缩放因子 (向量模长的比值) double normV1 = norm(v1); double normV2 = norm(v2); if (normV1 == 0) { throw new IllegalArgumentException("Source vector cannot be zero vector"); } this.scaleFactor = normV2 / normV1; // 计算旋转矩阵 if (n == 2) { calculateRotation2D(v1, v2); } else if (n == 3) { calculateRotation3D(v1, v2); } else { calculateRotationND(v1, v2); } } // 计算二维旋转 private void calculateRotation2D(double[] v1, double[] v2) { double[] u1 = normalize(v1); double[] u2 = normalize(v2); double cosTheta = dot(u1, u2); double sinTheta = u1[0] * u2[1] - u1[1] * u2[0]; // 2D叉积 rotationMatrix = new double[][]{ {cosTheta, -sinTheta}, {sinTheta, cosTheta} }; } // 计算三维旋转 (使用罗德里格斯旋转公式) private void calculateRotation3D(double[] v1, double[] v2) { double[] u1 = normalize(v1); double[] u2 = normalize(v2); double[] axis = cross(u1, u2); double axisNorm = norm(axis); if (axisNorm < 1e-10) { // 向量平行或反平行 if (dot(u1, u2) > 0) { // 平行 - 单位矩阵 rotationMatrix = new double[][]{ {1, 0, 0}, {0, 1, 0}, {0, 0, 1} }; } else { // 反平行 - 180度旋转 (任意轴) rotationMatrix = new double[][]{ {-1, 0, 0}, {0, -1, 0}, {0, 0, -1} }; } } else { double[] unitAxis = normalize(axis); double cosTheta = dot(u1, u2); double sinTheta = axisNorm; // 罗德里格斯旋转公式 double x = unitAxis[0], y = unitAxis[1], z = unitAxis[2]; double c = cosTheta; double s = sinTheta; double t = 1 - c; rotationMatrix = new double[][]{ {t*x*x + c, t*x*y - s*z, t*x*z + s*y}, {t*x*y + s*z, t*y*y + c, t*y*z - s*x}, {t*x*z - s*y, t*y*z + s*x, t*z*z + c} }; } } // 计算N维旋转 (使用Householde…

下载JDK
type: item
date: 2025-07-03T14:56:27+08:00
description: https://blog.csdn.net/Lwehne/article/details/135867512如果提示需要登录Oracle账号，这里提供一个：# 提醒：为了大伙的方便，请不要随便用该邮箱重新注册Oracle账号！ 账号：yawoniu@163.com 密码：Oracle.123

springboot项目通过Jsoup过滤XSS攻击
type: item
date: 2025-06-18T10:49:12+08:00
description: Jsoup概述org.jsoup.Jsoup 是一款强大的Java HTML解析库，它能够：🚀 从URL/文件/字符串加载HTML文档🔍 使用DOM遍历提取数据（类似jQuery语法）✂️ 清理用户输入防止XSS攻击🛠️ 操作HTML元素实现动态修改📦 零依赖轻量级设计（仅280KB）添加依赖（Maven）<dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.16.2</version> </dependency>创建清理工具类import org.jsoup.Jsoup; import org.jsoup.safety.Safelist; public class XssCleaner { // 基本HTML标签白名单 private static final Safelist BASIC_WHITELIST = Safelist.basic() .addTags("div", "span", "img") .addAttributes("img", "src", "alt", "title") .addProtocols("img", "src", "http", "https"); public static String clean(String dirty) { return dirty == null ? null : Jsoup.clean(dirty, BASIC_WHITELIST); } }创建可重复读取的请求包装器import org.springframework.util.StreamUtils; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper { private final byte[] cachedBody; public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException { super(request); InputStream inputStream = request.getInputStream(); this.cachedBody = StreamUtils.copyToByteArray(inputStream); } @Override public ServletInputStream getInputStream() { return new CachedBodyServletInputStream(cachedBody); } @Override public BufferedReader getReader() { return new BufferedReader(new InputStreamReader(this.getInputStream())); } public static class CachedBodyServletInputStream extends ServletInputStream { private final ByteArrayInputStream buffer; public CachedBodyServletInputStream(byte[] contents) { this.buffer = new ByteArrayInputStream(contents); } @Override public synchronized void reset() { buffer.reset(); } @Override public int read() { return buffer.read(); } @Override public boolean isFinished() { return buffer.available() == 0; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListene…

java批量操作sqlite数据库
type: item
date: 2025-05-09T18:09:25+08:00
description: 必须关闭自动提交，否则频繁提交事务会影响速度// 使用预处理语句+批处理 String baseSQL = "INSERT INTO my_table (col1, col2) VALUES (?, ?)"; try (Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db"); PreparedStatement pstmt = conn.prepareStatement(baseSQL)) { conn.setAutoCommit(false); // 关闭自动提交 // 添加批处理参数 for (int i = 0; i < 10000; i++) { pstmt.setInt(1, i); pstmt.setString(2, "value_" + i); pstmt.addBatch(); if (i % 1000 == 0) { // 每1000条执行一次 pstmt.executeBatch(); } } pstmt.executeBatch(); // 执行剩余批处理 conn.commit(); // 提交事务 } catch (SQLException e) { e.printStackTrace(); }

Java通过定时执行线程保持连接，防止超时
type: item
date: 2025-05-09T09:52:26+08:00
description: boolean result = false;ScheduledExecutorService scheduler = null;try {// 创建定时任务 scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(() -> { // 执行查询保持sql连接活跃 documentObjectService.findOne(modelObjectId).orElse(null); }, 0, 10, TimeUnit.MINUTES); // 每10分钟执行一次 result = createPropDb(new File(this.uploadBasePath + loadPath));} finally {// 释放定时任务 if (scheduler != null) { scheduler.shutdown(); try { if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { scheduler.shutdownNow(); } }}

通过aspose将word使用表格模板动态创建表格并导出
type: item
date: 2025-03-27T17:33:00+08:00
description: // 模板路径 ClassPathResource classPathResource = new ClassPathResource("/templates/DailyTemplate.docx"); try (InputStream inputStream = classPathResource.getInputStream()) { Document document = new Document(inputStream); // 表格模板路径 ClassPathResource tablePathResource = new ClassPathResource("/templates/TableDailyTemplate.docx"); try (InputStream tableInputStream = tablePathResource.getInputStream()) { Document table = new Document(tableInputStream); // 获取源文档中的表格 Table tableToCopy = (Table) table.getChild(NodeType.TABLE, 0, true); // 因为表格从第四行开始是需要动态增长的，所以取第四行作为模板 Row baseRow = tableToCopy.getRows().get(4); // 插入数据 for (int i = 0; i < dtoList.size(); i++) { DataDTO dataDTO = dtoList.get(i); Row newRow; if (i == 0) { // 第一行数据直接录入模板行 newRow = baseRow; } else { // 深度克隆模板行，含样式 newRow = (Row) baseRow.deepClone(true); } // 设置第一列值，后续所有列都可参照该方式设置 Cell newCell = newRow.getCells().get(0); newCell.removeAllChildren(); newCell.appendChild(new Paragraph(table)); newCell.getFirstParagraph().appendChild(new Run(table, dataDTO.getCode())); // 内容居中 newCell.getFirstParagraph().getParagraphFormat().setAlignment(ParagraphAlignment.CENTER); if (i > 0) { // 非模板行插入行 tableToCopy.getRows().insert(4 + i, newRow); } } // 克隆表格到目标文档 Node importedNode = document.importNode(tableToCopy, true); document.getLastSection().getBody().appendChild(importedNode); // 以下内容为需要动态往word文档中插入多个表格模板时分隔表格的方式 // 创建分页符 Paragraph blankParagraph = new Paragraph(document); Run run = new Run(document); run.setText(ControlChar.PAGE_BREAK); // 插入分页控制字符 blankParagraph.appendChild(run); // 插入分页符 document.getLastSection().getBody().appendChild(blankParagraph); // // 创建空白段落 // Paragraph blankParagraph = new Paragraph(document); // blankParagraph.appendChild(new Run(document, "\n")); // blankParagraph.getParagraphFormat().setSpaceBefore(10); // 设置段前间距 // blankParagraph.getParagraphFormat().setSpaceAfter(10); // 设置段后间距 // // 插入空白行 // document.getLastSection().getBody().appendChild(blankParagraph); } // 检测最后的内容是否是分页符，如果是则移除，避免空白页 Bod…

Java通过标准OpenAPI接口调用deepseek
type: item
date: 2025-03-11T16:44:30+08:00
description: 引入依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.42</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.6.0</version> </dependency>代码import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import okhttp3.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; /** * deepseek工具类 */ @Component public class DeepSeekUtil { private final Logger log = LoggerFactory.getLogger(DeepSeekUtil.class); private static OkHttpClient client; private final static String CHAT_URL = "/v1/chat/completions"; private static String deepseekHost; @Value("${deepseekHost}") public void setDeepseekHost(String deepseekHost) { DeepSeekUtil.deepseekHost = deepseekHost; } private static String deepseekChatUrl; @Value("${deepseekChatUrl}") public void setDeepseekChatUrl(String deepseekChatUrl) { DeepSeekUtil.deepseekChatUrl = deepseekChatUrl; } private static String deepseekApiKey; @Value("${deepseekApiKey}") public void setDeepseekApiKey(String deepseekApiKey) { DeepSeekUtil.deepseekApiKey = deepseekApiKey; } private static String deepseekModel; @Value("${deepseekModel}") public void setDeepseekModel(String deepseekModel) { DeepSeekUtil.deepseekModel = deepseekModel; } DeepSeekUtil(){ client = new OkHttpClient.Builder() .connectTimeout(60, TimeUnit.SECONDS) // 连接超时 60s .readTimeout(120, TimeUnit.SECONDS) // 读取超时 120s .writeTimeout(60, TimeUnit.SECONDS) // 写入超时 60s .build(); } /** * deepseek会话回复接口 */ @FunctionalInterface public interface ReplyFunction { void apply(String reasoningContent, String content); } /** * deepseek会话 * @param question 问题 * @param maxTokens 最大回复量 * @param preMessage 之前的消息，用于多轮会话 * @return * …

通过aspose将Excel使用模板导出
type: item
date: 2024-12-20T14:02:00+08:00
description: 打开Excel模板编写Java代码实现数据导出 public void getAllMonitorDataAndExport(MonitorDataQueryVM monitorDataQueryVM, HttpServletResponse response) throws IOException { monitorDataQueryVM.setPageNumber(0); monitorDataQueryVM.setPageSize(Integer.MAX_VALUE); Page<MonitorDataDTO> monitorDataDTOPage = getAllMonitorData(monitorDataQueryVM); File file = File.createTempFile("监测数据预警", ".xlsx"); try { // 数据映射 List<Map<String, Object>> mapList = new ArrayList<>(); if (!monitorDataDTOPage.getContent().isEmpty()) { // 查询工点 ProjectDTO site = projectService.findOne(monitorDataQueryVM.getProjectId()).orElse(null); // 查询工区 ProjectDTO area = projectService.findOne(site.getParentId()).orElse(null); for (MonitorDataDTO monitorDataDTO : monitorDataDTOPage.getContent()) { Map<String, Object> row = new HashMap<>(); row.put("area", area.getProjectName()); row.put("site", site.getProjectName()); if (!ObjectUtils.isEmpty(monitorDataDTO.getCollectTime())) { row.put("collectTime", InstantUtil.instantToStr(monitorDataDTO.getCollectTime(), "yyyy/MM/dd")); } else { row.put("collectTime", ""); } if (!StringUtils.isEmpty(monitorDataDTO.getPointType())) { row.put("pointType", MonitorPointType.getByCode(monitorDataDTO.getPointType()).getName()); } else { row.put("pointType", ""); } row.put("pointCode", monitorDataDTO.getPointCode()); if (!StringUtils.isEmpty(monitorDataDTO.getAlarmLevel())) { row.put("alarmLevel", MonitorAlarmLevelEnum.getByCode(monitorDataDTO.getAlarmLevel()).getName()); } else { row.put("alarmLevel", ""); } row.put("currentValue", monitorDataDTO.getCurrentValue()); if ("1".equals(monitorDataDTO.getIsEliminate())) { row.put("isEliminate", "已消警"); } else { row.put("isEliminate", "未消警"); } mapList.add(row); } // 设置表头 Map<String, Object> dataMap = new HashMap<>(); dataMap.put("headers", new String[]{ "工区", "工点", "预警发生日期", "测点类型", "测点名称", "预警级别", "累计值", "是否消警" }); // 模板路径 ClassPathResource classPathResource = new ClassPathResource("/templates/MonitorDataTemplate.xlsx"); try (InputStream inputStream = …

通过aspose将word使用模板导出
type: item
date: 2024-09-19T17:04:36+08:00
description: 打开word模板，通过设置标签的方式占位编写Java代码实现替换word模板中的占位 public void exportChangeDesignRecords(HttpServletResponse response, HttpServletRequest request, String id) { ChangeDesignRecord changeDesignRecord = changeDesignRecordRepository.findById(id).get(); response.setHeader("Cache-Control", "public, max-age=604800"); String fileName= "审批（审核）表.docx"; try(InputStream inputStream = FileUtil.getAsposeTempFileStream(request,fileName,"")) { AsposeUtil.judgeLicense("word"); Document nodes = new Document(inputStream); // nodes.getRange().getBookmarks().get("changeCode").setText(changeDesignRecord.getChangeCode()); nodes.getRange().getBookmarks().get("changeType").setText(changeDesignRecord.getChangeType()); nodes.getRange().getBookmarks().get("contractAmount").setText(changeDesignRecord.getContractAmount().toString()); nodes.getRange().getBookmarks().get("changeAmount").setText(changeDesignRecord.getChangeAmount().toString()); nodes.getRange().getBookmarks().get("changeDescription").setText(changeDesignRecord.getChangeDescription()); nodes.save(response.getOutputStream(), SaveFormat.DOCX); response.getOutputStream().flush(); } catch (Exception e) { log.error("导出{}失败：{}", changeDesignRecord, e); } }FileUtil /** * 获取aspose模板文件流 * @param fileName 文件名 * @param directory 模板目录 为空则默认模板路径 * @return * @throws UnsupportedEncodingException */ public static InputStream getAsposeTempFileStream(HttpServletRequest request, String fileName, String directory) throws IOException { /** 获取模板文件位置**/ String fileDirectory = directory; if (StringUtils.isEmpty(fileDirectory)) { fileDirectory = "templates/import/";//读取classes目录下文件 } fileDirectory = fileDirectory.replaceAll("\\\\","/"); fileDirectory = URLDecoder.decode(fileDirectory, "utf-8"); if(fileDirectory.lastIndexOf("/") != fileDirectory.length() - 1){ fileDirectory += "/"; } InputStream inputStream = FileUtil.class.getClassLoader().getResourceAsStream(fileDirectory + fileName); return inputStream; }