MyBatis-Plus - 論 1 個實體類被 N 個DAO 類綁定,導致 MP 特性(邏輯刪)失效的解決方案

問題描述

最近遇到一個奇奇怪怪的問題,發現 Mybatis-Plus『邏輯刪』特性失效,而且是偶現,有時候可以,有時候又不行。于是開啟了 Debug Mybatis-Plus 源碼之旅

原因分析

  • 我們接下來重點關注 TableInfoHelper 類
/** Copyright (c) 2011-2020, baomidou (jobob@qq.com).* <p>* Licensed under the Apache License, Version 2.0 (the "License"); you may not* use this file except in compliance with the License. You may obtain a copy of* the License at* <p>* https://www.apache.org/licenses/LICENSE-2.0* <p>* Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the* License for the specific language governing permissions and limitations under* the License.*/
package com.baomidou.mybatisplus.core.metadata;import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
import com.baomidou.mybatisplus.core.toolkit.*;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.Reflector;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.session.Configuration;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static java.util.stream.Collectors.toList;/*** <p>* 實體類反射表輔助類* </p>** @author hubin sjy* @since 2016-09-09*/
public class TableInfoHelper {private static final Log logger = LogFactory.getLog(TableInfoHelper.class);/*** 儲存反射類表信息*/private static final Map<Class<?>, TableInfo> TABLE_INFO_CACHE = new ConcurrentHashMap<>();/*** 默認表主鍵名稱*/private static final String DEFAULT_ID_NAME = "id";/*** <p>* 獲取實體映射表信息* </p>** @param clazz 反射實體類* @return 數據庫表反射信息*/public static TableInfo getTableInfo(Class<?> clazz) {if (clazz == null|| ReflectionKit.isPrimitiveOrWrapper(clazz)|| clazz == String.class) {return null;}// https://github.com/baomidou/mybatis-plus/issues/299TableInfo tableInfo = TABLE_INFO_CACHE.get(ClassUtils.getUserClass(clazz));if (null != tableInfo) {return tableInfo;}//嘗試獲取父類緩存Class<?> currentClass = clazz;while (null == tableInfo && Object.class != currentClass) {currentClass = currentClass.getSuperclass();tableInfo = TABLE_INFO_CACHE.get(ClassUtils.getUserClass(currentClass));}if (tableInfo != null) {TABLE_INFO_CACHE.put(ClassUtils.getUserClass(clazz), tableInfo);}return tableInfo;}/*** <p>* 獲取所有實體映射表信息* </p>** @return 數據庫表反射信息集合*/@SuppressWarnings("unused")public static List<TableInfo> getTableInfos() {return new ArrayList<>(TABLE_INFO_CACHE.values());}/*** <p>* 實體類反射獲取表信息【初始化】* </p>** @param clazz 反射實體類* @return 數據庫表反射信息*/public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {TableInfo tableInfo = TABLE_INFO_CACHE.get(clazz);if (tableInfo != null) {if (builderAssistant != null) {tableInfo.setConfiguration(builderAssistant.getConfiguration());}return tableInfo;}/* 沒有獲取到緩存信息,則初始化 */tableInfo = new TableInfo(clazz);GlobalConfig globalConfig;if (null != builderAssistant) {tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace());tableInfo.setConfiguration(builderAssistant.getConfiguration());globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration());} else {// 兼容測試場景globalConfig = GlobalConfigUtils.defaults();}/* 初始化表名相關 */final String[] excludeProperty = initTableName(clazz, globalConfig, tableInfo);List<String> excludePropertyList = excludeProperty != null && excludeProperty.length > 0 ? Arrays.asList(excludeProperty) : Collections.emptyList();/* 初始化字段相關 */initTableFields(clazz, globalConfig, tableInfo, excludePropertyList);/* 放入緩存 */TABLE_INFO_CACHE.put(clazz, tableInfo);/* 緩存 lambda */LambdaUtils.installCache(tableInfo);/* 自動構建 resultMap */tableInfo.initResultMapIfNeed();return tableInfo;}/*** <p>* 初始化 表數據庫類型,表名,resultMap* </p>** @param clazz        實體類* @param globalConfig 全局配置* @param tableInfo    數據庫表反射信息* @return 需要排除的字段名*/private static String[] initTableName(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo) {/* 數據庫全局配置 */GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();TableName table = clazz.getAnnotation(TableName.class);String tableName = clazz.getSimpleName();String tablePrefix = dbConfig.getTablePrefix();String schema = dbConfig.getSchema();boolean tablePrefixEffect = true;String[] excludeProperty = null;if (table != null) {if (StringUtils.isNotBlank(table.value())) {tableName = table.value();if (StringUtils.isNotBlank(tablePrefix) && !table.keepGlobalPrefix()) {tablePrefixEffect = false;}} else {tableName = initTableNameWithDbConfig(tableName, dbConfig);}if (StringUtils.isNotBlank(table.schema())) {schema = table.schema();}/* 表結果集映射 */if (StringUtils.isNotBlank(table.resultMap())) {tableInfo.setResultMap(table.resultMap());}tableInfo.setAutoInitResultMap(table.autoResultMap());excludeProperty = table.excludeProperty();} else {tableName = initTableNameWithDbConfig(tableName, dbConfig);}String targetTableName = tableName;if (StringUtils.isNotBlank(tablePrefix) && tablePrefixEffect) {targetTableName = tablePrefix + targetTableName;}if (StringUtils.isNotBlank(schema)) {targetTableName = schema + StringPool.DOT + targetTableName;}tableInfo.setTableName(targetTableName);/* 開啟了自定義 KEY 生成器 */if (null != dbConfig.getKeyGenerator()) {tableInfo.setKeySequence(clazz.getAnnotation(KeySequence.class));}return excludeProperty;}/*** 根據 DbConfig 初始化 表名** @param className 類名* @param dbConfig  DbConfig* @return 表名*/private static String initTableNameWithDbConfig(String className, GlobalConfig.DbConfig dbConfig) {String tableName = className;// 開啟表名下劃線申明if (dbConfig.isTableUnderline()) {tableName = StringUtils.camelToUnderline(tableName);}// 大寫命名判斷if (dbConfig.isCapitalMode()) {tableName = tableName.toUpperCase();} else {// 首字母小寫tableName = StringUtils.firstToLowerCase(tableName);}return tableName;}/*** <p>* 初始化 表主鍵,表字段* </p>** @param clazz        實體類* @param globalConfig 全局配置* @param tableInfo    數據庫表反射信息*/public static void initTableFields(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo, List<String> excludeProperty) {/* 數據庫全局配置 */GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();ReflectorFactory reflectorFactory = tableInfo.getConfiguration().getReflectorFactory();//TODO @咩咩 有空一起來擼完這反射模塊.Reflector reflector = reflectorFactory.findForClass(clazz);List<Field> list = getAllFields(clazz);// 標記是否讀取到主鍵boolean isReadPK = false;// 是否存在 @TableId 注解boolean existTableId = isExistTableId(list);List<TableFieldInfo> fieldList = new ArrayList<>(list.size());for (Field field : list) {if (excludeProperty.contains(field.getName())) {continue;}/* 主鍵ID 初始化 */if (existTableId) {TableId tableId = field.getAnnotation(TableId.class);if (tableId != null) {if (isReadPK) {throw ExceptionUtils.mpe("@TableId can't more than one in Class: \"%s\".", clazz.getName());} else {isReadPK = initTableIdWithAnnotation(dbConfig, tableInfo, field, tableId, reflector);continue;}}} else if (!isReadPK) {isReadPK = initTableIdWithoutAnnotation(dbConfig, tableInfo, field, reflector);if (isReadPK) {continue;}}/* 有 @TableField 注解的字段初始化 */if (initTableFieldWithAnnotation(dbConfig, tableInfo, fieldList, field)) {continue;}/* 無 @TableField 注解的字段初始化 */fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field));}/* 檢查邏輯刪除字段只能有最多一個 */Assert.isTrue(fieldList.parallelStream().filter(TableFieldInfo::isLogicDelete).count() < 2L,String.format("@TableLogic can't more than one in Class: \"%s\".", clazz.getName()));/* 字段列表,不可變集合 */tableInfo.setFieldList(Collections.unmodifiableList(fieldList));/* 未發現主鍵注解,提示警告信息 */if (!isReadPK) {logger.warn(String.format("Can not find table primary key in Class: \"%s\".", clazz.getName()));}}/*** <p>* 判斷主鍵注解是否存在* </p>** @param list 字段列表* @return true 為存在 @TableId 注解;*/public static boolean isExistTableId(List<Field> list) {return list.stream().anyMatch(field -> field.isAnnotationPresent(TableId.class));}/*** <p>* 主鍵屬性初始化* </p>** @param dbConfig  全局配置信息* @param tableInfo 表信息* @param field     字段* @param tableId   注解* @param reflector Reflector*/private static boolean initTableIdWithAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo,Field field, TableId tableId, Reflector reflector) {boolean underCamel = tableInfo.isUnderCamel();final String property = field.getName();if (field.getAnnotation(TableField.class) != null) {logger.warn(String.format("This \"%s\" is the table primary key by @TableId annotation in Class: \"%s\",So @TableField annotation will not work!",property, tableInfo.getEntityType().getName()));}/* 主鍵策略( 注解 > 全局 ) */// 設置 Sequence 其他策略無效if (IdType.NONE == tableId.type()) {tableInfo.setIdType(dbConfig.getIdType());} else {tableInfo.setIdType(tableId.type());}/* 字段 */String column = property;if (StringUtils.isNotBlank(tableId.value())) {column = tableId.value();} else {// 開啟字段下劃線申明if (underCamel) {column = StringUtils.camelToUnderline(column);}// 全局大寫命名if (dbConfig.isCapitalMode()) {column = column.toUpperCase();}}tableInfo.setKeyRelated(checkRelated(underCamel, property, column)).setKeyColumn(column).setKeyProperty(property).setKeyType(reflector.getGetterType(property));return true;}/*** <p>* 主鍵屬性初始化* </p>** @param tableInfo 表信息* @param field     字段* @param reflector Reflector* @return true 繼續下一個屬性判斷,返回 continue;*/private static boolean initTableIdWithoutAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo,Field field, Reflector reflector) {final String property = field.getName();if (DEFAULT_ID_NAME.equalsIgnoreCase(property)) {if (field.getAnnotation(TableField.class) != null) {logger.warn(String.format("This \"%s\" is the table primary key by default name for `id` in Class: \"%s\",So @TableField will not work!",property, tableInfo.getEntityType().getName()));}String column = property;if (dbConfig.isCapitalMode()) {column = column.toUpperCase();}tableInfo.setKeyRelated(checkRelated(tableInfo.isUnderCamel(), property, column)).setIdType(dbConfig.getIdType()).setKeyColumn(column).setKeyProperty(property).setKeyType(reflector.getGetterType(property));return true;}return false;}/*** <p>* 字段屬性初始化* </p>** @param dbConfig  數據庫全局配置* @param tableInfo 表信息* @param fieldList 字段列表* @return true 繼續下一個屬性判斷,返回 continue;*/private static boolean initTableFieldWithAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo,List<TableFieldInfo> fieldList, Field field) {/* 獲取注解屬性,自定義字段 */TableField tableField = field.getAnnotation(TableField.class);if (null == tableField) {return false;}fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, tableField));return true;}/*** <p>* 判定 related 的值* </p>** @param underCamel 駝峰命名* @param property   屬性名* @param column     字段名* @return related*/public static boolean checkRelated(boolean underCamel, String property, String column) {if (StringUtils.isNotColumnName(column)) {// 首尾有轉義符,手動在注解里設置了轉義符,去除掉轉義符column = column.substring(1, column.length() - 1);}String propertyUpper = property.toUpperCase(Locale.ENGLISH);String columnUpper = column.toUpperCase(Locale.ENGLISH);if (underCamel) {// 開啟了駝峰并且 column 包含下劃線return !(propertyUpper.equals(columnUpper) ||propertyUpper.equals(columnUpper.replace(StringPool.UNDERSCORE, StringPool.EMPTY)));} else {// 未開啟駝峰,直接判斷 property 是否與 column 相同(全大寫)return !propertyUpper.equals(columnUpper);}}/*** <p>* 獲取該類的所有屬性列表* </p>** @param clazz 反射類* @return 屬性集合*/public static List<Field> getAllFields(Class<?> clazz) {List<Field> fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz));return fieldList.stream().filter(field -> {/* 過濾注解非表字段屬性 */TableField tableField = field.getAnnotation(TableField.class);return (tableField == null || tableField.exist());}).collect(toList());}public static KeyGenerator genKeyGenerator(String baseStatementId, TableInfo tableInfo, MapperBuilderAssistant builderAssistant) {IKeyGenerator keyGenerator = GlobalConfigUtils.getKeyGenerator(builderAssistant.getConfiguration());if (null == keyGenerator) {throw new IllegalArgumentException("not configure IKeyGenerator implementation class.");}Configuration configuration = builderAssistant.getConfiguration();//TODO 這里不加上builderAssistant.getCurrentNamespace()的會導致com.baomidou.mybatisplus.core.parser.SqlParserHelper.getSqlParserInfo越(chu)界(gui)String id = builderAssistant.getCurrentNamespace() + StringPool.DOT + baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;ResultMap resultMap = new ResultMap.Builder(builderAssistant.getConfiguration(), id, tableInfo.getKeyType(), new ArrayList<>()).build();MappedStatement mappedStatement = new MappedStatement.Builder(builderAssistant.getConfiguration(), id,new StaticSqlSource(configuration, keyGenerator.executeSql(tableInfo.getKeySequence().value())), SqlCommandType.SELECT).keyProperty(tableInfo.getKeyProperty()).resultMaps(Collections.singletonList(resultMap)).build();configuration.addMappedStatement(mappedStatement);return new SelectKeyGenerator(mappedStatement, true);}
}
  • 注意這里的?initTableInfo 方法,這里面是所有 MapperScan 掃碼 DAO 類時候會初始化的必經之路,不過出問題的根本也就是在這個方法里
/*** <p>* 實體類反射獲取表信息【初始化】* </p>** @param clazz 反射實體類* @return 數據庫表反射信息*/
public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {TableInfo tableInfo = TABLE_INFO_CACHE.get(clazz);if (tableInfo != null) {if (builderAssistant != null) {tableInfo.setConfiguration(builderAssistant.getConfiguration());}return tableInfo;}/* 沒有獲取到緩存信息,則初始化 */tableInfo = new TableInfo(clazz);GlobalConfig globalConfig;if (null != builderAssistant) {tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace());tableInfo.setConfiguration(builderAssistant.getConfiguration());globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration());} else {// 兼容測試場景globalConfig = GlobalConfigUtils.defaults();}/* 初始化表名相關 */final String[] excludeProperty = initTableName(clazz, globalConfig, tableInfo);List<String> excludePropertyList = excludeProperty != null && excludeProperty.length > 0 ? Arrays.asList(excludeProperty) : Collections.emptyList();/* 初始化字段相關 */initTableFields(clazz, globalConfig, tableInfo, excludePropertyList);/* 放入緩存 */TABLE_INFO_CACHE.put(clazz, tableInfo);/* 緩存 lambda */LambdaUtils.installCache(tableInfo);/* 自動構建 resultMap */tableInfo.initResultMapIfNeed();return tableInfo;
}
  • 我把關鍵核心代碼顯示出來,其他忽略掉先,單單看下面代碼意味著什么呢?
  • 簡單理解:TABLE_INFO_CACHE 集合存放實體類和DAO類的映射關系,KV結構(K - 實體類全限定名,V - DAO 類綁定數據源的相關信息)
  • 如果按照這個理解,那顯而易見,如果說有 1 個實體類,被多個 DAO 綁定的話,那一定會被掃到初始化時,后來者會覆蓋前者,這樣就導致了只有后者的 DAO 擁有 MP 特性,前者就會失去一些 MP 特性,但是經測試基本的 MyBatis 特性有些具備,反正就會功能不完整
private static final Map<Class<?>, TableInfo> TABLE_INFO_CACHE = new ConcurrentHashMap<>();public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {TableInfo tableInfo = TABLE_INFO_CACHE.get(clazz);if (tableInfo != null) {if (builderAssistant != null) {tableInfo.setConfiguration(builderAssistant.getConfiguration());}return tableInfo;}/* 沒有獲取到緩存信息,則初始化 */// .../* 放入緩存 */TABLE_INFO_CACHE.put(clazz, tableInfo);// ...return tableInfo;
}

解決方案

  1. 方法一:把實體類復制出來換個名字,重新給另一個DAO綁定上即可
  2. 方法二:抽出公共實體類和DAO,這樣多處使用的時候可以共享

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/213998.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/213998.shtml
英文地址,請注明出處:http://en.pswp.cn/news/213998.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

算法:兩數之和(暴力解法和優化算法)

暴力解法&#xff1a;使用快慢指針解決&#xff0c;時間復雜度 O(n^2)&#xff0c;空間復雜度 O(n) /*** param {number[]} nums* param {number} target* return {number[]}*/ var twoSum function(nums, target) {let slow 0let fast 1// 如果慢指針沒有超過nums邊界就繼…

代理IP怎么使用?Mac蘋果系統設置http代理IP教程

代理IP是一種通過將請求轉發到另一個服務器&#xff0c;以隱藏自己的真實IP地址的服務器。使用代理IP可以保護您的隱私和安全&#xff0c;防止被跟蹤或被攻擊。在本文中&#xff0c;我們將介紹如何在Mac蘋果系統上設置http代理IP教程。 一、了解代理IP 代理IP地址是一種可以用來…

基于Java二手房交易管理系統

基于Java二手房交易管理系統 功能需求 1、房源信息管理&#xff1a;系統需要能夠記錄和管理所有房源的詳細信息&#xff0c;包括房屋地址、房屋面積、售價、房屋類型等。管理員和房東可以添加、編輯和刪除房源信息。 2、買家信息管理&#xff1a;系統需要能夠記錄和管理所有…

2023.12.9 關于 Spring Boot 事務傳播機制詳解

目錄 事務傳播機制 七大事務傳播機制 支持當前調用鏈上的事務 Propagation.REQUIRED Propagation.SUPPORTS Propagation.MANDATORY 不支持當前調用鏈上的事務 Propagation.REQUIRES_NEW Propagation.NOT_SUPPORTED Propagation.NEVER 嵌套事務 Propagation.NESTED…

蜂窩、無線設備應用 HXG-242+、PVGA-123+、PMA-5452+、PSA-39+、PSA-14+射頻放大器(IC器件)

1、HXG-242 射頻放大器 IC 無線 LAN&#xff0c;LTE 700MHz 至 2.4GHz&#xff0c;6-SMD 模塊 HXG-242&#xff08;符合RoHS規范&#xff09;是一款先進的放大器模塊&#xff0c;結合了高動態范圍MMIC技術和優化電路&#xff0c;可在聚焦頻率范圍內提供業界領先的線性度。它采…

創建并測試第一個django項目并解決過程中遇到的問題

Django 是一個高級 Python Web 框架&#xff0c;它鼓勵快速開發和簡潔、實用的設計。它由經驗豐富的開發人員構建&#xff0c;解決了 Web 開發的大部分麻煩&#xff0c;因此您可以專注于編寫應用程序&#xff0c;而無需重新發明輪子。它是免費和開源的。 目錄 一、django項目 …

Nginx 簡單入門操作

前言:之前的文章有些過就不羅嗦了。 Nginx 基礎內容 是什么? Nginx 是一個輕量級的 HTTP 服務器,采用事件驅動、異步非阻塞處理方式的服務器,它具有極好的 IO 性能,常用于 HTTP服務器(包含動靜分離)、正向代理、反向代理、負載均衡 等等. Nginx 和 Node.js 在很多方…

pdb 調試 python 代碼

pdb python的官方調試工具; 默認下載的模塊 參考文檔 pdbpdb有官方文檔, 也有源碼, 可能閱讀python源碼更容易理解; 和gdb非常相似&#xff0c;也支持擴展; 基于bdb,cmd拓展; 代碼中設置調試點(一次性調試) 好處是可以源碼級別的調試, 對于剛了解pdb又想調試子進程的比較…

大語言模型有什么意義?亞馬遜訓練自己的大語言模型有什么用?

近年來&#xff0c;大語言模型的嶄露頭角引起了廣泛的關注&#xff0c;成為科技領域的一項重要突破。而在這個領域的巔峰之上&#xff0c;亞馬遜云科技一直致力于推動人工智能的發展。那么&#xff0c;作為一家全球科技巨頭&#xff0c;亞馬遜為何會如此注重大語言模型的研發與…

解讀 | GPT-4突然“變賴“ 是莫名其妙還是另有玄機

大家好&#xff0c;我是極智視界&#xff0c;歡迎關注我的公眾號&#xff0c;獲取我的更多前沿科技分享 邀您加入我的知識星球「極智視界」&#xff0c;星球內有超多好玩的項目實戰源碼和資源下載&#xff0c;鏈接&#xff1a;https://t.zsxq.com/0aiNxERDq 事情是這樣的&#…

初學websocket有感-待研究

https://www.bilibili.com/video/BV1KN411n7WD/ 看到一半的時候就會想到以下的問題&#xff1a; 初學websocket有感-待研究 既然每一個endPoint都是對應著一個服務器和客戶端瀏覽器的連接對象&#xff0c;那么就是說要創建很多個endPoint對象咯。 一、是否回將創建的這么多…

項目經理和產品經理哪個更有發展前景?

如果是單看“錢途”的話&#xff0c;如果是在傳統行業&#xff0c;可能差不多&#xff1b;如果是在IT行業的話&#xff0c;可能更需要項目經理&#xff1b;互聯網行業的話&#xff0c;可能更需要產品經理。 項目經理跟產品經理兩個證都挺受市場歡迎的&#xff0c;兩個崗位職責…

關東升老師Python著作推薦(由電子工業出版社出版)

前言&#xff1a;關東升老師簡單介紹 一個在IT領域摸爬滾打20多年的老程序員、軟件架構師、高級培訓講師、IT作家。熟悉Java、Kotlin、Python、iOS、Android、游戲開發、數據庫開發與設計、軟件架構設計等多種IT技術。參與設計和開發北京市公交一卡通百億級大型項目&#xff0c…

釣魚網站域名識別工具dnstwist算法研究

先上一個AI的回答&#xff1a; dnstwist是一種釣魚網站域名識別工具&#xff0c;可幫助用戶識別和檢測可能被惡意使用的域名。它通過生成類似的域名變體來模擬攻擊者可能使用的釣魚域名&#xff0c;并提供了一系列有用的功能和信息。 dnstwist能夠生成一組類似的域名變體&…

HTML常見的列表標簽

目錄 &#x1f367;無序列表&#x1f367;有序列表&#x1f367; 定義列表&#x1f367; 菜單列表 &#x1f367;無序列表 ulli的組合,ul標簽與li標簽之間盡量不要寫標簽或內容 列表可以嵌套多層 type屬性&#xff0c;可以指定序號的類型 可選值&#xff1a;默認值&#xff0c;…

15:00面試,15:06就出來了,問的問題太變態了。。

剛從小廠出來&#xff0c;沒想到在另一家公司我又寄了。 在這家公司上班&#xff0c;每天都要加班&#xff0c;但看在錢給的比較多的份上&#xff0c;也就不太計較了。但萬萬沒想到5月一紙通知&#xff0c;所有人不準加班了&#xff0c;不僅加班費沒有了&#xff0c;薪資還要降…

有病但合理的 ChatGPT 提示語

ChatGPT 面世一年多了&#xff0c;如何讓大模型輸出高質量內容&#xff0c;讓提示詞工程成了一門重要的學科。以下是一些有病但合理的提示詞技巧&#xff0c;大部分經過論文證明&#xff0c;有效提高 ChatGPT 輸出質量&#xff1a; ?1?? Take a deep breath. 深呼吸 ? 作用…

ChatGPT勝過我們人類嗎?

引言 人工智能&#xff08;AI&#xff09;一直是眾多技術進步背后的驅動力&#xff0c;推動我們走向曾經是科幻小說領域的未來。這些進步的核心引出這樣一個深刻的問題&#xff1a;機器能思考嗎&#xff1f;這一問題由英國數學家和計算機科學家艾倫圖靈&#xff08;Alan Turin…

SSL安全證書怎么查看證書類型?

SSL安全證書是一種用于確保互聯網通信安全的協議。它通過加密數據傳輸以保護敏感信息不被竊取或篡改。在瀏覽器中&#xff0c;我們可以輕松查看SSL安全證書的類型。本文將詳細介紹如何查看證書類型&#xff0c;并探討不同類型的SSL證書的用途和特點。 要查看SSL安全證書的類型&…

關于粒子群算法的一些簡單嘗試

粒子群算法核心思想&#xff1a;&#xff08;鳥 粒子&#xff09; &#xff08;1&#xff09;許多的鳥站在不同的地方&#xff1b; &#xff08;2&#xff09;每一只鳥都有自己尋找食物的初始飛行方向、飛行速度&#xff1b; &#xff08;3&#xff09;這些鳥兒每隔一段時間…