MyBatis 插件介绍及应用

MyBatis 插件介绍及应用

MyBatis 是一个持久层框架,它允许开发者自定义 SQL 语句并将其映射到 Java 对象中。MyBatis 提供了一种灵活的数据库操作方式,但随着项目的复杂度增加,一些通用功能如分页、缓存、事务管理等可能需要重复编写。为了解决这个问题,MyBatis 提供了插件机制,允许开发者扩展 MyBatis 的功能,实现自定义逻辑。

一、MyBatis 插件概述

MyBatis 插件是 MyBatis 框架的扩展点,它们可以拦截 MyBatis 的核心处理过程,包括执行器、参数处理器、结果处理器等。通过编写插件,开发者可以在不修改 MyBatis 核心代码的情况下,增加新的功能或改变 MyBatis 的行为。

1.1 插件的作用

  • 拦截器:在 MyBatis 执行 SQL 之前或之后执行自定义逻辑。
  • 扩展功能:实现 MyBatis 未提供的功能,如分页、性能监控等。
  • 自定义 SQL:通过插件机制,可以自定义 SQL 片段,提高代码的复用性。

1.2 插件的工作原理

MyBatis 插件通过使用 Java 的代理机制实现。开发者需要实现 MyBatis 提供的 Interceptor 接口,并重写 intercept 方法。在 intercept 方法中,可以定义拦截逻辑。

1.3 拦截四种核心组件

MyBatis所允许拦截的⽅法如下:

Executor:执⾏器 (update、query、commit、rollback等⽅法),负责SQL语句的执行和事务管理;
StatementHandler:SQL语法构建器(prepare、parameterize、batch、updates query等⽅ 法),处理具体的SQL语句,包括预编译和参数设置等;
ParameterHandler:参数处理器 (getParameterObject、setParameters⽅法),负责将用户传递的参数转换成JDBC可识别的参数;
ResultSetHandler:结果集处理器 (handleResultSets、handleOutputParameters等⽅法),负责将JDBC返回的结果集转换成用户所需的对象或集合;

二、MyBatis 插件开发

开发 MyBatis 插件需要对 MyBatis 的工作流程有深入的理解。下面是一个简单的插件开发示例。

会针对四种核心组件分别实现一个插件。

2.1Executor 拦截器实现

2.1.1 Query 拓展点
  • 用途:可以在查询操作执行前后添加逻辑,如记录查询时间、进行查询缓存等。
  • 拦截方法query

功能:打印查询sql耗时,以及结果集行数。

package com.company.oneday.plugin;import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;import java.util.*;@Component
@Intercepts({@Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class ExecutorQueryPlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {long start = System.currentTimeMillis();Object result = invocation.proceed(); // 执行原方法long end = System.currentTimeMillis();System.out.println("查询时间: " + (end - start) + " ms");// 如果查询结果是一个 List,可以打印查询到的行数if (result instanceof List<?>) {List<?> list = (List<?>) result;System.out.println("查询行数: " + list.size());}return result; // 返回原方法结果}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 可以为插件配置属性}
}

image-20240422185834914

2.1.2 Update 拓展点
  • 用途:可以在更新(插入、修改、删除)操作执行前后添加逻辑,如统计影响行数、记录变更日志等。
  • 拦截方法update
package com.company.oneday.plugin;import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.springframework.stereotype.Component;import java.util.Properties;@Component
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class ExecutorUpdatePlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 记录操作开始时间long start = System.currentTimeMillis();// 执行 update 操作Object result = invocation.proceed(); // 执行目标方法// 记录操作结束时间long end = System.currentTimeMillis();// 计算操作耗时long timeElapsed = end - start;// 获取影响的行数int affectedRows = (Integer) result;// 获取 MappedStatement 来获取相关的信息,如 SQL 语句和参数MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];Object parameter = invocation.getArgs()[1];// 记录变更日志recordChangeLog(mappedStatement, parameter, affectedRows);// 打印消耗时间和影响行数System.out.println(String.format("更新操作耗时: %d ms, 受影响的行数: %d", timeElapsed, affectedRows));return result; // 返回操作影响的行数}private void recordChangeLog(MappedStatement mappedStatement, Object parameter, int affectedRows) {// 模拟记录变更日志的逻辑// 这里可以根据实际需要,将变更信息写入日志系统或者存储起来String sql = mappedStatement.getBoundSql(parameter).getSql();sql = sql.replaceAll("\n", " ").replaceAll(" +", " ").toLowerCase();System.out.println("sql 日志打印: " + sql);// 可以在这里添加更多的日志记录逻辑,如记录操作的用户、时间戳等}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 可以为插件配置属性}
}

image-20240423182936627

2.1.3 Commit 拓展点
  • 用途:可以在事务提交时执行额外的操作,如记录事务提交日志、执行某些后置操作等。
  • 拦截方法commit
package com.company.oneday.plugin;import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;import java.util.Properties;@Component
@Intercepts({@Signature(type = Executor.class, method = "commit", args = {boolean.class})})
public class ExecutorCommitPlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 记录事务提交前的时间long start = System.currentTimeMillis();// 执行 commit 操作Object result = invocation.proceed(); // 执行目标方法// 记录事务提交后的时间long end = System.currentTimeMillis();// 计算事务提交耗时long timeElapsed = end - start;// 打印事务提交耗时System.out.println("事务提交时间: " + timeElapsed + " ms");// 记录事务提交日志recordTransactionCommitLog();// 执行后置操作performPostActions();return result; // 返回事务提交操作的结果}private void recordTransactionCommitLog() {// 模拟记录事务提交日志的逻辑// 这里可以根据实际需要,将事务提交信息写入日志系统或者存储起来System.out.println("事务已经提交成功.");}private void performPostActions() {// 模拟执行后置操作的逻辑// 后置操作可以是清理缓存、发送通知、统计信息等System.out.println("事务提交成功后置通知.");}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 可以为插件配置属性}
}

image-20240422193830476

2.1.4 Rollback 拓展点
  • 用途:在事务回滚时执行逻辑,如记录回滚原因、清理资源等。
  • 拦截方法rollback
package com.company.oneday.plugin;import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;import java.util.Properties;
@Component
@Intercepts({@Signature(type = Executor.class, method = "rollback", args = {boolean.class})})
public class ExecutorRollbackPlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {try {System.out.println(1111);// 执行回滚操作return invocation.proceed();} catch (Exception t) {// 记录回滚原因recordRollbackReason(t);throw t;}}private void recordRollbackReason(Throwable t) {// 实际应用中,这里应将异常信息记录到日志文件或数据库中System.err.println("事务回滚原因: " + t.getMessage());}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 可以为插件配置属性}
}

2.2 StatementHandler 拦截器实现

  1. prepare 方法
    • 作用:用于准备 Statement 对象。在这个方法中,会创建 PreparedStatement 对象,并且可能涉及动态 SQL 的解析和参数的预处理。
    • 拓展:可以修改 SQL 语句以添加日志、性能监控、防止 SQL 注入、实现分页逻辑等。
  2. parameterize 方法
    • 作用:用于处理参数对象,将参数与 SQL 语句中的占位符进行绑定。
    • 拓展:可以修改参数绑定逻辑,例如使用自定义的类型处理器(Type Handler)或者在参数绑定前后添加额外的处理。
  3. batch 方法
    • 作用:用于执行批量更新操作。在这个方法中,会将多个 SQL 语句打包在一起执行,以提高性能。
    • 拓展:可以监控批量操作的执行情况,或者对批量参数进行预处理。
  4. update 方法
    • 作用:用于执行插入、更新或删除操作。在这个方法中,会执行 Statement 对象以修改数据库中的数据。
    • 拓展:可以统计影响行数、记录操作日志、在执行前后添加事务控制逻辑等。
  5. query 方法
    • 作用:用于执行查询操作。在这个方法中,会执行 Statement 对象以获取查询结果,并将其映射到 Java 对象中。
    • 拓展:可以实现分页查询、缓存查询结果、修改结果集处理逻辑、添加查询性能监控等。
  6. getBoundSql 方法
    • 作用:用于获取 BoundSql 对象,该对象包含了 SQL 语句、参数信息和额外的上下文信息。
    • 拓展:可以修改 BoundSql 中的 SQL 语句或参数,或者添加额外的上下文信息。
  7. 结果集处理器(ResultSetHandler
    • 作用:用于处理 ResultSet 对象,将 SQL 查询的结果集映射到 Java 对象。
    • 拓展:可以自定义结果集的映射逻辑,实现复杂的对象映射关系,或者在结果集处理前后添加额外的处理。
2.2.1 prepare 拓展点
  • 用途:在事务回滚时执行逻辑,如记录回滚原因、清理资源等。
  • 拦截方法rollback
package com.company.oneday.plugin.statement;import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class StatementHandlerPreparePlugin implements Interceptor {// 默认的方言类型,可以根据需要进行扩展private static final String DIALECT_MYSQL = "mysql";@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler statementHandler = (StatementHandler) invocation.getTarget();MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);// 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次操作可以分离出最原始的的目标类)while (metaStatementHandler.hasGetter("h")) {Object object = metaStatementHandler.getValue("h");metaStatementHandler = SystemMetaObject.forObject(object);}// 获取到当前的映射语句对象(MappedStatement)MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");// 只对需要分页的查询进行拦截if (mappedStatement.getId().endsWith("ByPage")) {BoundSql boundSql = statementHandler.getBoundSql();String sql = boundSql.getSql();PaginationParam paginationParam = null;// 获取分页参数//兼容下面两种情况,其他的遇到了再补充if (boundSql.getParameterObject() instanceof PaginationParam) {paginationParam = (PaginationParam) boundSql.getParameterObject();} else if (boundSql.getParameterObject() instanceof MapperMethod.ParamMap) {MapperMethod.ParamMap map = (MapperMethod.ParamMap) boundSql.getParameterObject();List<PaginationParam> collect = (List<PaginationParam>) map.values().stream().filter(e ->  e instanceof PaginationParam).collect(Collectors.toList());if(!CollectionUtils.isEmpty(collect)) {paginationParam = collect.get(0);}} else {}String pageSql = buildPageSql(sql, paginationParam);pageSql = pageSql.replaceAll("\n", " ").replaceAll(" +", " ").toLowerCase();System.out.println("sql:" + pageSql);// 通过反射设置当前boundSql对应的sql为分页sqlField sqlField = boundSql.getClass().getDeclaredField("sql");sqlField.setAccessible(true);sqlField.set(boundSql, pageSql);// 采用物理分页后,就不需要mybatis的内存分页了,所以这里将这两个参数都置为null即可metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.DEFAULT.getOffset());metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.DEFAULT.getLimit());}// 继续执行原始方法return invocation.proceed();}private String buildPageSql(String sql, PaginationParam paginationParam) {if(paginationParam != null && paginationParam.getOffset() != null && paginationParam.getLimit() != null) {// 这里只提供了一个简单的MySQL分页示例,实际情况可能需要根据数据库类型动态构建SQLsql = sql + " LIMIT " + paginationParam.getOffset() + "," + paginationParam.getLimit();}return sql;}@Overridepublic Object plugin(Object target) {// 创建代理对象return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 处理插件属性(如果有的话)}// 分页参数类@Datapublic static class PaginationParam {@TableField(exist = false)private Integer offset; // 起始行数@TableField(exist = false)private Integer limit;  // 每页显示的数量}
}

2.3 ParameterHandler 拦截器实现

  1. getParameterObject 方法
    • 作用:获取传递给 StatementHandler 的参数对象。
    • 拓展:可以在这个方法中修改参数对象,例如添加额外的参数、修改参数值或替换参数对象。
  2. setParameters 方法
    • 作用:将参数对象的值设置到 Statement 对象的 SQL 占位符中。
    • 拓展:可以修改参数的设置逻辑,例如使用自定义的类型处理器(Type Handler)或在参数设置前后添加额外的处理。
2.3.1 setParameters 拓展点
package com.company.oneday.plugin.parameterHandler;import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.MybatisDefaultParameterHandler;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.Properties;@Component
@Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class})})
public class ParameterHandlerSetParametersPlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {PreparedStatement statement = (PreparedStatement) invocation.getArgs()[0];MybatisDefaultParameterHandler parameterHandler = (MybatisDefaultParameterHandler) invocation.getTarget();// 打印参数对象Object parameter = parameterHandler.getParameterObject();System.out.println("Parameter object before setting: " + JSON.toJSONString(parameter));// 修改参数对象// ........// 继续执行参数设置Object result = invocation.proceed();// 再次打印参数对象,可能已被修改System.out.println("Parameter object after setting: " + JSON.toJSONString(parameter));return result;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 可以为插件配置属性}
}

image-20240425141353042

2.4 ResultSetHandler 拦截器实现

  1. handleResultSets 方法
    • 作用:处理 Statement 对象执行后返回的结果集。
    • 拓展:可以修改结果集的处理逻辑,例如实现自定义的结果集映射、过滤特定列的数据、实现懒加载等。
  2. handleOutputParameters 方法
    • 作用:处理存储过程调用后的输出参数。
    • 拓展:可以对输出参数进行特殊处理,比如转换为特定的 Java 类型。
2.4.1 handleResultSets拓展点

实现对查询结果中的密码password进行MD5加密。

处理器实现:

package com.company.oneday.plugin.resultSetHandler;import com.company.oneday.entity.User;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.resultset.ResultSetHandler;import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;public class EncryptingResultSetHandler implements ResultSetHandler {private final ResultSetHandler resultSetHandler;public EncryptingResultSetHandler(ResultSetHandler resultSetHandler) {this.resultSetHandler = resultSetHandler;}@Overridepublic List<Object> handleResultSets(Statement stmt) throws SQLException {// 使用委托对象处理结果集List<Object> result = this.resultSetHandler.handleResultSets(stmt);// 假设我们有一个User对象,并且知道密码字段名为"password"// 对密码进行“加密”操作(这里只是示例,实际应该是解密)if (result instanceof List) {List<?> resultList = (List<?>) result;for (Object item : resultList) {if (item instanceof User) {User user = (User) item;String encryptedPassword = encryptPassword(user.getPassword());user.setPassword(encryptedPassword);}}}return result;}private String encryptPassword(String password) {// 这里应该是你的加密逻辑,为了演示,我们使用一个简单的替换逻辑return DigestUtils.md5Hex(password);}@Overridepublic <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {return null;}@Overridepublic void handleOutputParameters(CallableStatement cs) throws SQLException {}// 其他方法...
}

拦截器实现:

package com.company.oneday.plugin.resultSetHandler;import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;@Component
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
public class ResultSetHandlerHandleResultSetsPlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {Statement stmt = (Statement) invocation.getArgs()[0];// 创建自定义的 EncryptingResultSetHandlerEncryptingResultSetHandler customResultSetHandler = new EncryptingResultSetHandler((ResultSetHandler) invocation.getTarget());
//        Object result = invocation.proceed();// 使用自定义的 EncryptingResultSetHandler 重新处理结果集return customResultSetHandler.handleResultSets(stmt);}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 可以为插件配置属性}
}

image-20240429155148345

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

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

相关文章

MyBatis(注解方式操作)

文章目录 1.注解方式操作文件目录1.快速入门&#xff08;完整步骤&#xff09;1.pom.xml&#xff08;完整&#xff09;2.resources/jdbc.properties外部配置文件&#xff08;根据实际情况修改参数&#xff09;3.在resources/mybatis-config.xml&#xff08;完整&#xff09;中配…

Android Studio的笔记--布局文件

关于Layout布局文件的使用 LinearLayoutRelativeLayout之前文章的内容一些常见性质在android.graphics.Color中定义了12种常见的颜色常数线性布局LinearLayout 一些常见使用文本框TextView设置文本内容编辑框EditText获取文本内容按钮Button控件使用其他按钮修改图标及名称添加…

智慧旅游驱动行业革新:智能技术引领服务全面升级,匠心打造高品质、个性化旅游新体验

一、引言 随着科技的飞速发展和信息化程度的不断提高&#xff0c;智慧旅游正逐渐成为旅游业发展的新趋势。智慧旅游&#xff0c;顾名思义&#xff0c;是以智能化技术为支撑&#xff0c;通过大数据、云计算、物联网、人工智能等先进技术的应用&#xff0c;实现旅游服务的全面升…

React Router 路由配置数组配组持久化

在一些特定场景下,你可能需要将路由配置数组进行持久化,例如从后端动态加载路由配置或根据用户权限动态生成路由配置。这时,持久化路由配置数组就很有用,可以避免每次应用启动时重新获取或计算路由配置。 持久化路由配置数组的步骤如下: 定义路由配置数组 首先,你需要定义一…

ASR语音转录Prompt优化

ASR语音转录Prompt优化 一、前言 在ASR转录的时候&#xff0c;我们能很明显的感受到有时候语音识别不是很准确&#xff0c;这过程中常见的文本错误主要可以归纳为以下几类&#xff1a; 同音错误&#xff08;Homophone Errors&#xff09; 同音错误发生在不同词语发音相似或相…

使用Postman对@RequestPart和HttpServletRequest组合传参方式

使用Postman对RequestPart和HttpServletRequest组合传参方式 方法代码如下&#xff1a; /*** 发布*/ApiOperation("发布")ApiImplicitParams({ApiImplicitParam(name "req", value "json格式", dataType "Map", dataTypeClass Ma…

【在线名字作画HTML源码】

在线名字作画HTML源码 效果图部分源码领取源码下期更新预报 效果图 部分源码 index.htm <!DOCTYPE html> <html> <head> <title>在线名字作画|民间花鸟字|多彩花鸟虫鱼组合书法|藏字画|字谜语|飞帛板书|意匠文字</title> <meta http-equiv&…

HarmaonyOS鸿蒙应用科普课

一、什么是鸿蒙OS&#xff1f; 1.概念&#xff1a; 先给大家讲讲今天讲课的主题&#xff0c;鸿蒙OS是什么&#xff1f;鸿蒙系统大家都知道&#xff0c;就是一个操作系统&#xff0c;我们未来是为的成为鸿蒙程序员。所以我们不要将鸿蒙os完全等同于手机操作系统&#xff0c;太…

华为 huawei 交换机 配置 MUX VLAN 示例(汇聚层设备)

组网需求 在企业网络中&#xff0c;企业所有员工都可以访问企业的服务器。但对于企业来说&#xff0c;希望企业内部部分员工之间可以互相交流&#xff0c;而部分员工之间是隔离的&#xff0c;不能够互相访问。 如 图 6-4 所示&#xff0c; Switch1 位于网络的汇聚层&#xff0…

Nacos 安全零信任实践

作者&#xff1a;柳遵飞 Nacos 作为配置中心经常存储一些敏感信息&#xff0c;但是由于误用导致安全风险&#xff0c;最常见的主要是以下两个问题&#xff1a; 1&#xff09;Nacos 暴露公网可以吗&#xff1f;不可以&#xff0c;因为 Nacos 定位是注册配置中心&#xff0c;是…

Windows命令行基本命令

目录 什么是相对路径和绝对路径&#xff1f; 一、目录&#xff08;文件夹&#xff09;和文件操作 1.cd命令 用于切换目录 2.dir命令 用于显示目录和文件列表 3.md或mkdir命令 创建文件&#xff0c;也可以创建多级子目录 4.rd命令 用于删除目录 5.move命令 用于移动…

C++:拷贝构造函数和赋值运算符重载

目录 一、拷贝构造函数 1.1概念 1.2特征 二、赋值运算符重载 2.1运算符重载 2.2赋值运算符重载 2.2.1赋值运算符重载格式 2.2.2赋值运算符重载要求 2.2.3默认生成的赋值运算符重载 2.3前置和后置重载 一、拷贝构造函数 1.1概念 只有一个形参&#xff0c;这个形参是…

leetcode51.N皇后(困难)-回溯法

思路 都知道n皇后问题是回溯算法解决的经典问题&#xff0c;但是用回溯解决多了组合、切割、子集、排列问题之后&#xff0c;遇到这种二维矩阵还会有点不知所措。 首先来看一下皇后们的约束条件&#xff1a; 不能同行不能同列不能同斜线 确定完约束条件&#xff0c;来看看究…

Python爬虫(入门版)

1、爬虫是什么 简单的来说&#xff1a;就是用程序获取网络上数据。 2、爬虫的原理 如果要获取网络上数据&#xff0c;我们要给爬虫一个网址&#xff08;程序中通常叫URL&#xff09;&#xff0c;爬虫发送一个HTTP请求给目标网页的服务器&#xff0c;服务器返回数据给客户端&am…

异地组网、网络部署、无线覆盖,贝锐蒲公英一步到位

面对网络架构复杂的企业总部&#xff0c;分散在各地的分支机构&#xff0c;以及出差的远程办公人员&#xff0c;如何才能高效异地组网&#xff1f; 为了确保总部、分部网络实现远程稳定、高速互访&#xff0c;以及远程人员安全访问总部业务系统&#xff0c;基于自研SD-WAN的贝…

C++ 小游戏:战斗之旅

一、游戏名称&#xff1a;战斗之旅 游戏规则 角色选择&#xff1a;玩家可以选择不同的角色&#xff0c;每个角色都有不同的属性和技能。商城&#xff1a;玩家可以访问商城购买不同的装备&#xff0c;包括武器和回复物品。战斗&#xff1a;玩家可以与其他角色进行战斗。在战斗…

数据结构(01)——链表OJ

目录 移除链表元素 思路1 不创建虚拟头节点 思路2 创建虚拟头节点 反转链表 寻找链表中间节点 判断链表是否相交 回文链表 环形链表 环形链表|| 移除链表元素 . - 力扣&#xff08;LeetCode&#xff09; 要想移除链表的元素&#xff0c;那么只需要将目标节点的前一…

XMind轮播图banner测试点

banner测试点 显示1到5张banner图片 [1,5] 6 1 一张不轮播 5 3 0可选 自动轮播,3秒切换一张 鼠标悬停,不轮播 实心为当前图 点击可以跳转 点击左,切换一张图片 点击右, 切换一张图片…

【中级软件设计师】上午题12-软件工程(3):项目活动图、软件风险、软件评审、软件项目估算

【中级软件设计师】上午题12-软件工程&#xff08;3&#xff09; 1 软件项目估算1.1 COCOMO估算模型1.2 COCOMOⅡ模型 2 进度管理2.1 gantt甘特图2.2 pert图2.3 项目活动图2.3.1 画项目图 3 软件配置管理4 软件风险4.1 风险管理4.2 风险识别4.3 风险预测4.4 风险评估4.5 风险控…

Baidu comate智能编程助手评测

Baidu comate智能编程助手评测 作者&#xff1a;知孤云出岫 目录 一&#xff0e; 关于comate产品 二&#xff0e; 关于comate产品体验 三&#xff0e; 关于实际案例. 四&#xff0e; 关于baidu comate编程助手的实测体验感悟 五&#xff0e; …