java 对mybatis拦截Interceptor进行权限控制(条件修改),入参修改,返回修改
使用jsqlparser对sql解析拼装
基础准备
<dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>4.5</version><scope>compile</scope>
</dependency>
<dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.4</version>
</dependency>
import lombok.Data;
/*** 需要拦截或修改的值*/
@Data
public class InitiateInterceptField {public static final String deptId = "deptId";public static final String createBy = "createBy";public static final String updateBy = "updateBy";public static final String dept_id = "dept_id";public static final String create_by = "create_by";public static final String update_by = "update_by";public static final String deptName = "deptName";public static final String createName = "createName";public static final String updateName = "updateName";}
import cn.hutool.core.thread.ThreadFactoryBuilder;
import lombok.Data;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;import java.util.concurrent.*;@Data
@Component
public class DisclosureThreadPool {@Bean("mybatisInterceptorExecutorService")public ExecutorService entranceAlerterThread() {ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNamePrefix("thread-mybatisInterceptorExecutorService-runner-%d").build();return new ThreadPoolExecutor(4, 36, 200L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(204800), namedThreadFactory);}}
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;import java.lang.reflect.Proxy;
import java.util.Properties;/*** 插件工具类*/
public final class PluginUtils {public static final String DELEGATE_BOUNDSQL_SQL = "delegate.boundSql.sql";private PluginUtils() {// to do nothing}/*** 获得真正的处理对象,可能多层代理.*/@SuppressWarnings("unchecked")public static <T> T realTarget(Object target) {if (Proxy.isProxyClass(target.getClass())) {MetaObject metaObject = SystemMetaObject.forObject(target);return realTarget(metaObject.getValue("h.target"));}return (T) target;}/*** 根据 key 获取 Properties 的值*/public static String getProperty(Properties properties, String key) {String value = properties.getProperty(key);return StringUtils.isBlank(value) ? null : value;}
}
数据权限控制
比如根据部门权限动态修改sql
import cn.hutool.core.util.ObjectUtil;
import com.disclosure.common.core.domain.entity.SysRole;
import com.disclosure.common.core.domain.entity.SysUser;
import com.disclosure.common.core.domain.model.LoginUser;
import com.disclosure.common.utils.SecurityUtils;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;import java.sql.Connection;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;/*** @author <p>* mybatis 数据权限拦截器*/
@Slf4j
@AllArgsConstructor
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare" , args = {Connection.class, Integer.class})})
public class AuthorityScopeInterceptor implements Interceptor {private final ExecutorService executorService;@Override@SneakyThrowspublic Object intercept(Invocation invocation) {StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());MetaObject metaObject = SystemMetaObject.forObject(statementHandler);MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");//指定范围拦截if (!mappedStatement.getId().startsWith("com.disclosure.business")) return invocation.proceed();//只拦截 三种类型if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())&& !SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())&& !SqlCommandType.DELETE.equals(mappedStatement.getSqlCommandType())) return invocation.proceed();//获取用户属性LoginUser loginUser = SecurityUtils.getLoginUser();SysUser currentUser;if (ObjectUtil.isNull(loginUser) || ObjectUtil.isNull(currentUser = loginUser.getUser()))throw new RuntimeException("获取不到用户数据");// 如果是超级管理员,则不过滤数据if (currentUser.isAdmin()) return invocation.proceed();List<SysRole> roles = currentUser.getRoles();if (roles.isEmpty()) throw new RuntimeException("角色缺失");AuthorityCategory authorityCategory = new AuthorityCategory(executorService);//获取多角色的权限范围for (SysRole role : roles) {switch (role.getDataScope()) {case AuthorityCategory.DATA_SCOPE_ALL:return invocation.proceed();case AuthorityCategory.DATA_SCOPE_CUSTOM:authorityCategory.addDataScopeCustom(role.getRoleId());break;case AuthorityCategory.DATA_SCOPE_DEPT:authorityCategory.addDataScopeDept(currentUser.getDeptId());break;case AuthorityCategory.DATA_SCOPE_DEPT_AND_CHILD:authorityCategory.addDataScopeDeptAndChild(currentUser.getDeptId());break;default:authorityCategory.addDataScopeSelf(currentUser.getUserId());break;}}authorityCategory.removeRepeatDept();BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");String originalSql = boundSql.getSql();//生成新sqloriginalSql = authorityCategory.getNewSql(originalSql).toString();metaObject.setValue("delegate.boundSql.sql" , originalSql);return invocation.proceed();}/*** 生成拦截对象的代理** @param target 目标对象* @return 代理对象*/@Overridepublic Object plugin(Object target) {if (target instanceof StatementHandler) {return Plugin.wrap(target, this);}return target;}/*** mybatis配置的属性** @param properties mybatis配置的属性*/@Overridepublic void setProperties(Properties properties) {}
}
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.Data;
import lombok.SneakyThrows;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.update.Update;import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;import static com.disclosure.business.config.InitiateInterceptField.create_by;
import static com.disclosure.business.config.InitiateInterceptField.dept_id;@Data
public class AuthorityCategory {private final ExecutorService executorService;/*** 全部*/public static final String DATA_SCOPE_ALL = "1";/*** 自定数据权限*/public static final String DATA_SCOPE_CUSTOM = "2";/*** 部门数据权限*/public static final String DATA_SCOPE_DEPT = "3";/*** 部门及以下数据权限*/public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";/*** 仅本人数据权限*/public static final String DATA_SCOPE_SELF = "5";private ArrayList<Long> dataScopeCustomList;private ArrayList<Long> dataScopeDeptList;private ArrayList<Long> dataScopeDeptAndChildList;private ArrayList<Long> dataScopeSelfList;private String fromItemName;public AuthorityCategory(ExecutorService executorService) {this.executorService = executorService;this.dataScopeCustomList = new ArrayList<>();this.dataScopeDeptList = new ArrayList<>();this.dataScopeDeptAndChildList = new ArrayList<>();this.dataScopeSelfList = new ArrayList<>();this.fromItemName = "";}public void addDataScopeSelf(Long dataScopeSelf) {this.dataScopeSelfList.add(dataScopeSelf);}public void addDataScopeDeptAndChild(Long dataScopeDeptAndChild) {this.dataScopeDeptAndChildList.add(dataScopeDeptAndChild);}public void addDataScopeDept(Long dataScopeDept) {this.dataScopeDeptList.add(dataScopeDept);}public void addDataScopeCustom(Long dataScopeCustom) {this.dataScopeCustomList.add(dataScopeCustom);}public void removeRepeatDept() {dataScopeDeptList.removeAll(dataScopeDeptAndChildList);}@SneakyThrowspublic Statement getNewSql(String originalSql) {//解析只针对主表Statement statement = CCJSqlParserUtil.parse(new StringReader(originalSql));if (statement instanceof Select) {Select select = (Select) statement;SelectBody selectBody = select.getSelectBody();PlainSelect plainSelect = (PlainSelect) selectBody;plainSelect.setWhere(spliceSQl(plainSelect.getWhere(), plainSelect.getFromItem()));return select;} else if (statement instanceof Delete) {Delete delete = (Delete) statement;delete.setWhere(spliceSQl(delete.getWhere(), delete.getTable()));return delete;} else if (statement instanceof Update) {Update update = (Update) statement;update.setWhere(spliceSQl(update.getWhere(), update.getTable()));return update;}return statement;}private Expression spliceSQl(Expression expression, FromItem fromItem) {List<Expression> expressionList = this.newWhere(fromItem);if (ObjectUtil.isNull(expression)) {return getOr(expressionList);} else {AndExpression andExpression = new AndExpression();andExpression.setLeftExpression(expression);andExpression.setRightExpression(new Column("(" + getOr(expressionList) + ")"));return expression;}}/*** 处理 四种权限类型*/@SneakyThrowsprivate List<Expression> newWhere(FromItem fromItem) {handleFromItemName(fromItem);Future<Expression> handleDataScopeSelfLisSubmit = executorService.submit(this::handleDataScopeSelfList);Future<Expression> handleDataScopeDeptListSubmit = executorService.submit(this::handleDataScopeDeptList);Future<Expression> handleDataScopeCustomListSubmit = executorService.submit(this::handleDataScopeCustomList);Future<Expression> handleDataScopeDeptAndChildListSubmit = executorService.submit(this::handleDataScopeDeptAndChildList);return Stream.of(handleDataScopeSelfLisSubmit.get(),handleDataScopeDeptListSubmit.get(),handleDataScopeCustomListSubmit.get(),handleDataScopeDeptAndChildListSubmit.get()).filter(ObjectUtil::isNotNull).collect(Collectors.toList());}/*** 获取名字或别名*/private void handleFromItemName(FromItem fromItem) {if (ObjectUtil.isNull(fromItem.getAlias())) {this.fromItemName = fromItem.toString();} else {this.fromItemName = fromItem.getAlias().getName();}}/*** 拼接仅本人数据权限sql*/private Expression handleDataScopeSelfList() {if (!this.dataScopeSelfList.isEmpty()) {InExpression inExpression = new InExpression();inExpression.setLeftExpression(new Column(new Table(this.fromItemName), create_by));inExpression.setRightExpression(new Column("(" + CollUtil.join(this.dataScopeSelfList, ",") + ")"));return new Column("(" + inExpression + ")");}return null;}/*** 拼接仅部门数据权限sql*/private Expression handleDataScopeDeptList() {if (!this.dataScopeDeptList.isEmpty()) {InExpression inExpression = new InExpression();inExpression.setLeftExpression(new Column(new Table(this.fromItemName), dept_id));inExpression.setRightExpression(new Column("(" + CollUtil.join(this.dataScopeDeptList, ",") + ")"));return new Column("(" + inExpression + ")");}return null;}/*** 拼接自定义数据权限sql*/private Expression handleDataScopeCustomList() {if (!this.dataScopeCustomList.isEmpty()) {InExpression inExpression = new InExpression();inExpression.setLeftExpression(new Column(new Table(this.fromItemName), dept_id));inExpression.setRightExpression(new Column("(SELECT dept_id FROM sys_role_dept WHERE role_id in (" + CollUtil.join(this.dataScopeCustomList, ",") + "))"));return new Column("(" + inExpression + ")");}return null;}/*** 拼接部门及以下数据权限sql*/private Expression handleDataScopeDeptAndChildList() {if (!this.dataScopeDeptAndChildList.isEmpty()) {InExpression inExpression = new InExpression();inExpression.setLeftExpression(new Column(new Table(this.fromItemName), dept_id));inExpression.setRightExpression(new Column("(SELECT dept_id FROM sys_dept WHERE dept_id in (" + CollUtil.join(this.dataScopeDeptAndChildList, ",") + ") or " + this.dataScopeDeptAndChildList.parallelStream().map(n -> "find_in_set(" + n + ",ancestors)").collect(Collectors.joining(" or ")) + ")"));return new Column("(" + inExpression + ")");}return null;}private Expression getOr(List<Expression> expressions) {if (expressions.size() == 1) return expressions.get(0);String collect = expressions.parallelStream().map(Object::toString).collect(Collectors.joining(" or "));return new Column(collect);}}
入参修改
比如修改机构字段,删除机构字段,填充新建用户
import cn.hutool.core.util.ObjectUtil;
import com.disclosure.common.core.domain.entity.SysUser;
import com.disclosure.common.core.domain.model.LoginUser;
import com.disclosure.common.utils.SecurityUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.update.UpdateSet;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;import java.io.StringReader;
import java.sql.Connection;
import java.util.*;
import java.util.stream.Collectors;import static com.disclosure.business.config.InitiateInterceptField.*;@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare" , args = {Connection.class, Integer.class})})
public class AuthorityUserFillerInterceptor implements Interceptor {//清理指定映射字段private final String[] deleteParameter = {deptId, createBy, updateBy};private final String[] deleteColumn = {dept_id, create_by, update_by};@Override@SneakyThrowspublic Object intercept(Invocation invocation) {StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());MetaObject metaObject = SystemMetaObject.forObject(statementHandler);MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");if (!mappedStatement.getId().startsWith("com.disclosure.business")) return invocation.proceed();if (!SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())&& !SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())) {return invocation.proceed();}LoginUser loginUser = SecurityUtils.getLoginUser();SysUser currentUser;if (ObjectUtil.isNull(loginUser) || ObjectUtil.isNull(currentUser = loginUser.getUser()))throw new RuntimeException("获取不到用户数据");BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");String originalSql = boundSql.getSql();Statement statement = CCJSqlParserUtil.parse(new StringReader(originalSql));List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();//删除参数映射以免出现下列问题// Parameter index out of range (X > number of parameters, which is X)List<String> deleteParameterList = Arrays.asList(deleteParameter);List<ParameterMapping> deptIdParameterMappings = parameterMappings.stream().parallel().map(n -> {if (deleteParameterList.contains(n.getProperty())) {return n;}return null;}).filter(ObjectUtil::isNotNull).collect(Collectors.toList());if (!deptIdParameterMappings.isEmpty()) {parameterMappings.removeAll(deptIdParameterMappings);}if (statement instanceof Update) {Update update = (Update) CCJSqlParserUtil.parse(originalSql);ArrayList<UpdateSet> updateSets = update.getUpdateSets();//删除sql对应值updateCleanse(updateSets);//填充信息Map<String, Object> updateParameter = new HashMap<String, Object>() {{put(update_by, currentUser.getUserId());}};setUpdateParameter(updateSets, updateParameter);originalSql = update.toString();} else if (statement instanceof Insert) {Insert insert = (Insert) CCJSqlParserUtil.parse(originalSql);List<Column> columns = insert.getColumns();List<Expression> expressions = insert.getItemsList(ExpressionList.class).getExpressions();insertCleanse(columns, expressions);Map<String, Object> insertParameter = new HashMap<String, Object>() {{put(dept_id, currentUser.getDeptId());put(create_by, currentUser.getUserId());}};//填充信息setInsertParameter(columns, expressions, insertParameter);originalSql = insert.toString();} else {return invocation.proceed();}metaObject.setValue("delegate.boundSql.sql" , originalSql);return invocation.proceed();}/*** 填充Insert入参,只做string和long,可另行扩展*/private void setInsertParameter(List<Column> columns, List<Expression> expressions, Map<String, Object> insertParameter) {insertParameter.forEach((k, v) -> {columns.add(new Column(k));if (v instanceof Long) {expressions.add(new LongValue((long) v));} else {expressions.add(new StringValue((String) v));}});}/*** 填充update入参,只做string和long,可另行扩展*/public void setUpdateParameter(List<UpdateSet> updateSets, Map<String, Object> updateParameter) {updateParameter.forEach((k, v) -> {UpdateSet updateSet = new UpdateSet();if (v instanceof Long) {updateSet.add(new Column(k), new LongValue((long) v));} else {updateSet.add(new Column(k), new StringValue(String.valueOf(v)));}updateSets.add(updateSet);});}/*** 删除update数据*/private void updateCleanse(ArrayList<UpdateSet> updateSets) {List<String> deleteColumnList = Arrays.asList(deleteColumn);List<UpdateSet> deleteUpdateSets = updateSets.parallelStream().map(n -> {ArrayList<Column> columns = n.getColumns();for (Column column : columns) {if (deleteColumnList.contains(column.toString())) {return n;}}return null;}).filter(ObjectUtil::isNotNull).collect(Collectors.toList());if (!deleteUpdateSets.isEmpty()) {updateSets.removeAll(deleteUpdateSets);}}/*** 删除insert数据*/private void insertCleanse(List<Column> columns, List<Expression> expressions) {List<String> deleteColumnList = Arrays.asList(deleteColumn);//删除sql对应值List<Column> deleteColumn = columns.parallelStream().map(n -> {if (deleteColumnList.contains(n.toString())) {return n;}return null;}).filter(ObjectUtil::isNotNull).collect(Collectors.toList());if (!deleteColumn.isEmpty()) {deleteColumn.parallelStream().forEach(n -> expressions.remove(columns.indexOf(n)));columns.removeAll(deleteColumn);}}/*** 生成拦截对象的代理** @param target 目标对象* @return 代理对象*/@Overridepublic Object plugin(Object target) {if (target instanceof StatementHandler) {return Plugin.wrap(target, this);}return target;}/*** mybatis配置的属性** @param properties mybatis配置的属性*/@Overridepublic void setProperties(Properties properties) {}
}
返回数据修改
比如根据部门id查询部门返回部门名称
import cn.hutool.core.util.StrUtil;
import io.swagger.annotations.ApiModel;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.*;import static com.disclosure.business.config.InitiateInterceptField.*;@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets" , args = {Statement.class})})
public class FillUserInterceptor implements Interceptor {private final String[] initiateInterceptField = {deptId, createBy, updateBy};public Object intercept(Invocation invocation) throws Throwable {MetaObject metaStatementHandler = SystemMetaObject.forObject(invocation.getTarget());MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("mappedStatement");if (!mappedStatement.getId().startsWith("com.disclosure.business")) return invocation.proceed();if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) return invocation.proceed();List<ResultMap> resultMaps = mappedStatement.getResultMaps();Class<?> resultType = resultMaps.get(0).getType();//稍微判断一下只有实体类又这个注解才进入下面if (!resultType.isAnnotationPresent(ApiModel.class)) {return invocation.proceed();}//获取mybatis返回的实体类类型名int resultMapCount = resultMaps.size();if (resultMapCount > 0) {Statement statement = (Statement) invocation.getArgs()[0];ResultSet resultSet = statement.getResultSet();if (resultSet != null) {//获得对应列名ResultSetMetaData resultSetMetaData = resultSet.getMetaData();List<String> columnList = new ArrayList<>();List<String> initiateInterceptFieldList = Arrays.asList(initiateInterceptField);for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {//做下划线转驼峰操作columnList.add(StrUtil.toCamelCase(resultSetMetaData.getColumnName(i)));}initiateInterceptFieldList.retainAll(columnList);List<LinkedHashMap<String, Object>> resList = new ArrayList<>();while (resultSet.next()) {LinkedHashMap<String, Object> resultMap = new LinkedHashMap<>();for (String colName : columnList) {resultMap.put(colName, resultSet.getString(StrUtil.toUnderlineCase(colName)));}
// 将转换后的map转换为实体类中,适用于实体类中有需要填充的数据
// Object o = resultType.newInstance();
// BeanUtils.populate(o, resultMap);resultMap.put(deptName, "测试部门");resultMap.put(createName, "测试新增人员");resultMap.put(updateName, "测试修改人员");resList.add(resultMap);}return resList;}}return invocation.proceed();}public Object plugin(Object target) {
// System.out.println("将要包装的目标对象:" + target);return Plugin.wrap(target, this);}public void setProperties(Properties properties) {}
}
相关文章:
java 对mybatis拦截Interceptor进行权限控制(条件修改),入参修改,返回修改
使用jsqlparser对sql解析拼装 基础准备 <dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>4.5</version><scope>compile</scope> </dependency> <depende…...
鲲鹏+麒麟v10 sp1 打包tomcat
一、安装打包软件 yum install rpm-build yum install rpmdevtools 二、复制tar包 注意:如果要修改配置文件需要把tar包解压,修改tomcat配置文件 cp apache-tomcat-8.5.93.tar.gz /root/rpm-build/SOURCES 三、编辑/root/rpm-build/SOURCES/tomcat.…...

大屏大概是怎么个开发法(前端)
写在前面,博主是个在北京打拼的码农,从事前端工作5年了,做过十多个大大小小不同类型的项目,最近心血来潮在这儿写点东西,欢迎大家多多指教。 对于文章中出现的任何错误请大家批评指出,一定及时修改。有任何…...

什么是Selenium?使用Selenium进行自动化测试!
你知道什么是 Selenium 吗?你知道为什么要使用它吗?答案就在本文中,很高兴能够与你共飧。 自动化测试正席卷全球,Selenium 认证是业界最抢手的技能之一。 什么是 Selenium? Selenium 是一种开源工具,用于…...
第一章:最新版零基础学习 PYTHON 教程(第十七节 - Python 表达式语句–Python返回语句)
return 语句用于结束函数调用的执行,并将结果(return 关键字后面的表达式的值)“返回”给调用者。return 语句之后的语句不被执行。如果return语句没有任何表达式,则返回特殊值None。return 语句总体上用于调用函数,以便可以执行传递的语句。 注意: Return 语句不能在函数…...
前端碎知识点
1 统计字符串中每个字符出现的频率 const str fgasdfadfdasd;const result str.split().reduce((a, b) > {if (a[b]) {a[b];} else {a[b] 1;}return a; }, {});const resultNew str.split().reduce((a, b) > {// undefined,就是NaNa[b] || (a[b] 1);ret…...
【Leetcode Sheet】Weekly Practice 7
Leetcode Test 1462 课程表Ⅳ(9.12) 你总共需要上 numCourses 门课,课程编号依次为 0 到 numCourses-1 。你会得到一个数组 prerequisite ,其中 prerequisites[i] [ai, bi] 表示如果你想选 bi 课程,你 必须 先选 ai 课程。 有的课会有直接…...
leetcode Top100(23)回文链表
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 输入:head [1,2,2,1] 输出:true采用动态数组,判断数组对称就可以了(这解法空间…...

WebGL绘制圆形的点
目录 前言 如何实现圆形的点? 片元着色器内置变量(gl_FragCoord、gl_PointCoord) gl_PointCoord的含义 示例程序(RoundedPoint.js) 代码详解 前言 本文将讨论示例程序RoundedPoint,该程序绘制了圆…...

《The Rise and Potential of Large Language Model Based Agents: A Survey》全文翻译
The Rise and Potential of Large Language Model Based Agents: A Surve - 基于 LLMs 的代理的兴起和潜力:一项调查 论文信息摘要1. 介绍2. 背景2.1 AI 代理的起源2.2 代理研究的技术趋势2.3 为什么大语言模型适合作为代理大脑的主要组件 论文信息 题目࿱…...

在线地图获取城市路网数据
在线地图获取城市路网数据 近期科研项目中,需要获取城市路网数据,于是详细阅读各大在线地图api接口,总结出来这么一条可行的思路: 首先获取城市轮廓根据城市轮廓把城市分割成若干个小块在每个小块中根据在线地图的POI检索接口&a…...

8.2 Jmeter if控制器使用
前提:jmeter脚本需要用到if控制器,if判断如果查询不到,则去新增。 1、添加if控制器 线程组-->逻辑控制器-->如果(if)控制器 1)、Expression (must evaluate to true or false) :表达式(值必须是tru…...

科技云报道:青云科技打出“AI算力牌”,抢跑“云+AI”新增市场
科技云报道原创。 近三年,中国云计算市场在多个维度同时发生着剧烈变化——疫情极大加速了全社会对于数字化的认知和接受程度;一系列云原生技术依托着开源和蓬勃的市场而迅速发展演变,更多产品和技术名词同时涌向市场;国际关系复…...

学习路之PHP--lumen安装配置
一、下载lumen源码 composer create-project --prefer-dist laravel/lumen blog 安装lumen-generator composer require flipbox/lumen-generator 二、配置 bootstrap\app.php 97行 $app->register(Flipbox\LumenGenerator\LumenGeneratorServiceProvider::class);三、生成…...

【C++】构造函数和析构函数第一部分(构造函数和析构函数的作用)--- 2023.9.25
目录 前言初始化和清理的概念构造函数和析构函数的作用构造函数的作用析构函数的作用 使用构造函数和析构函数的注意事项默认的构造函数和析构函数结束语 前言 在使用c语言开发的项目场景中,我们往往会遇到申请空间的需求,同时也肯定遇到过程序运行一段…...
CocosCreator3.8研究笔记(二十一)CocosCreator Tween系统理解
在 Cocos Creator 3.x 版本中, Tween系统代替了原来的Action系统。很多朋友不明白Tween到底是什么,Tween原理是什么?怎么使用Tween? 今天就来详细了解一下,希望能帮助到大家加深对Tween的了解,并快速掌握Tw…...
大数据学习-目录
学习内容持续更新ing 1.大数据学习1.0-Centos8虚拟机安装 大数据学习1.0-Centos8虚拟机安装_汉卿HanQ的博客-CSDN博客 2.大数据学习1.1-Centos8网络配置 大数据学习1.1-Centos8网络配置_汉卿HanQ的博客-CSDN博客 3.大数据学习1.2-yum配置 大数据学习1.2-yum配置_汉卿HanQ的…...

《动手学深度学习 Pytorch版》 7.5 批量规范化
7.5.1 训练深层网络 训练神经网络的实际问题: 数据预处理的方式会对最终结果产生巨大影响。 训练时,多层感知机的中间层变量可能具有更广的变化范围。 更深层的网络很复杂容易过拟合。 批量规范化对小批量的大小有要求,只有批量大小足够…...

Toaster - Android 吐司框架,专治 Toast 各种疑难杂症
官网 https://github.com/getActivity/Toaster 这可能是性能优、使用简单,支持自定义,不需要通知栏权限的吐司 想了解实现原理的可以点击此链接查看:Toaster 源码 集成步骤 如果你的项目 Gradle 配置是在 7.0 以下,需要在 bui…...

2023年9月26日,历史上的今天大事件早读
1620年9月26日大明皇帝朱常洛驾崩 1815年9月26日俄、普、奥三国在巴黎发表缔结“神圣同盟” 1841年9月26日清代思想家、诗人龚自珍逝世 1849年9月26日“生理学之父”巴甫洛夫诞生 1909年9月26日云南陆军讲武堂创办 1953年9月26日画家徐悲鸿逝世 1980年9月26日国际宇航联合…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...