Mybatis源码分析系列之第四篇:Mybatis中代理设计模型源码详解
一: 前言
我们尝试在前几篇文章的内容中串联起来,防止各位不知所云。
1:背景
我们基于Mybatis作为后台Orm框架进行编码的时候,有两种方式。
//编码方式1
UserDao userDao = sqlSession.getMapper(UserDao.class);
userDao.queryAllUser(Map map);
//有同学质疑为毛我从来没有sqlSesseion.getMapper(..)是因为我们使用Spring或者
//boot的时候右边给你封装了,导致你直接在Service里边把UserDao进行了注入,直接
//=右边的,你可能压根就没有机会写。//编码方式2
sqlSession.select("com.shit.user.UserDao.queryAllUser",Map map);
我们提到了,最为底层的是下边这种方式,第一种方式是基于动态代理的技术实现的,底层也是第二种方式,基于此我们开始探索了动态代理的这种方式,整套流程下来是怎么玩的。
UserDao userDao = SqlSession.getMapper(User.class)
userDao.queryuserById();
userDao.queryUsers();
2:代理设计模式(动态代理)的一些特点(简述)
这里的userDao是DAO接口的实现类的对象,那么我们就会有一个疑问,UserDao这个接口实现类在哪里呢?
这个Dao接口的实现类我们是见不到的,因为这里是一个典型的动态字节码技术,这个类是在虚拟机当中运行时创建的。虚拟机运行结束的时候,这个类就消失了
这和我们之前的这些类不一样,我们之前写的这些类就是实实在在的文件,在我们的硬盘当中,通过JVM的类加载的方式读到虚拟机的内存当中。这是我们传统意义上的一个类的创建的方式,通过复杂的类加载机制将这个类加载到JVM虚拟机当中之后,我们在这个类的Class对象创建出来。
动态字节码技术的特点是类的文件自始至终就没有,直接就在虚拟机当中的内存当中去创建,进而创建这个类的Class对象。是JVM在运行时的时候基于动态字节码技术将这个类创建出来的操作。
这就是为什么我们知道有这么一个类的存在,有这么一个类的对象的存在,但是我们就看不到的原因。
创建完成之后,就按照多态的特点,代理对象地址保存在这个接口的引用类型下边。按照多态的原则,所以,我们才可以有这个效果。
3:使用代理设计模式场景
1):为原始对象(目标)增加额外功能
ps:如果是和原始功能没有半毛钱关系的内容才叫做额外功能,与原始功能有关系的功能就不太算是额外功能了,这种情况下推进使用装饰器模式。
这个场景典型的Spring的AOP的功能。
2):远程代理(远程代理)
ps:远程代理:这时候额外功能就变成1:网络连接:2:数据传输嘛。这个最典型的就是Dubbo,远程代理代理的是远程服务。
3):无中生有(只有接口,除了接口毛都没有)
ps:这个最符合的就是Mybatis的Dao层的实现类的处理了。接口实现类这个东西,我们真的看不见,但是运行时确实可以体现出来。
在这三个场景下,我们需要考虑用到代理设计模式,第三种是典型的动态代理。而动态代理的典型的编码是:
Proxy.newProxyInstance(AssistantToHistoryPriceRule.class.getClassLoader(),new Class[]{BillSystemConfigGateWay.class}, new InvocationHandler(){@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("额外功能输出......");return null;}});
二:实现类该如何去实现预定的功能呢?
1:猜想Mybatis的代理流程
我们现在有一个Ineterface接口中有这样的一个方法,实现类实现之后应该大致张这个样子:
以下这个是我们的大致流程猜想,当然最终结果肯定是与Mybatis的具体实现大差不差的。
interface UserDao{List<User> queryAllUsers();save(User user)
}UserDaoImpl implements UserDAO {queryAllUsers(){sqlSession.select(....);}save(){sqlSession.insert("namespace.id",参数);}
}

类加载器,接口,额外功能处理Handler三个条件满足之后,运行时会把这个代理对象给创建出来。
2:模拟Mybatis代理对象创建
@Testpublic void testProxy() throws IOException {InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();Class[] interfaces = new Class[]{UserDAO.class};UserDAO userDAO = (UserDAO) Proxy.newProxyInstance(TestMybatis.class.getClassLoader(),interfaces,new MyMapperProxy(sqlSession,UserDAO.class));List<User> users = userDAO.queryAllUsersByPage();for (User user : users) {System.out.println("user = " + user);}}
public class MyMapperProxy implements InvocationHandler {private SqlSession sqlSession;private Class daoClass;public MyMapperProxy(SqlSession sqlSession, Class daoClass) {this.sqlSession = sqlSession;this.daoClass = daoClass;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(daoClass.getName() + "." + method.getName());return sqlSession.selectList(daoClass.getName() + "." + method.getName());}
}
这一步的创建代理的操作就等同于我们之前当中的SqlSession.getMapper(UserDao.class)的操作。

三:Mybatis代理对象创建的源码如下
Mybatis当中完成代理创建的核心类型是:MapperProxy和MapperProcyFactory
MapperProcyFactory:是个工厂,他的作用就是创建代理,工厂底层设计的过程中完成的就是Proxy.newProxyInstance(),为我们创建代理对象
MapperProxy:实现了InvocationHandler接口,他就是用调用具体method的。
/*** @author Lasse Voss*/
public class MapperProxyFactory<T> {private final Class<T> mapperInterface;private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();public MapperProxyFactory(Class<T> mapperInterface) {this.mapperInterface = mapperInterface;}public Class<T> getMapperInterface() {return mapperInterface;}public Map<Method, MapperMethod> getMethodCache() {return methodCache;}@SuppressWarnings("unchecked")protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}}
/*** @author Clinton Begin* @author Eduardo Macarron*/
public class MapperProxy<T> implements InvocationHandler, Serializable {private static final long serialVersionUID = -6424540398559729838L;private final SqlSession sqlSession;private final Class<T> mapperInterface;private final Map<Method, MapperMethod> methodCache;public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {this.sqlSession = sqlSession;this.mapperInterface = mapperInterface;this.methodCache = methodCache;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (isDefaultMethod(method)) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);}private MapperMethod cachedMapperMethod(Method method) {MapperMethod mapperMethod = methodCache.get(method);if (mapperMethod == null) {mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());methodCache.put(method, mapperMethod);}return mapperMethod;}@UsesJava7private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)throws Throwable {final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);if (!constructor.isAccessible()) {constructor.setAccessible(true);}final Class<?> declaringClass = method.getDeclaringClass();return constructor.newInstance(declaringClass,MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC).unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);}/*** Backport of java.lang.reflect.Method#isDefault()*/private boolean isDefaultMethod(Method method) {return (method.getModifiers()& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC&& method.getDeclaringClass().isInterface();}
}
public class MapperMethod {private final SqlCommand command;private final MethodSignature method;public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {this.command = new SqlCommand(config, mapperInterface, method);this.method = new MethodSignature(config, mapperInterface, method);}public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(command.getName(), param);}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");}return result;}private Object rowCountResult(int rowCount) {final Object result;if (method.returnsVoid()) {result = null;} else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {result = rowCount;} else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {result = (long)rowCount;} else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {result = rowCount > 0;} else {throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());}return result;}private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());if (!StatementType.CALLABLE.equals(ms.getStatementType())&& void.class.equals(ms.getResultMaps().get(0).getType())) {throw new BindingException("method " + command.getName()+ " needs either a @ResultMap annotation, a @ResultType annotation,"+ " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");}Object param = method.convertArgsToSqlCommandParam(args);if (method.hasRowBounds()) {RowBounds rowBounds = method.extractRowBounds(args);sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));} else {sqlSession.select(command.getName(), param, method.extractResultHandler(args));}}private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {List<E> result;Object param = method.convertArgsToSqlCommandParam(args);if (method.hasRowBounds()) {RowBounds rowBounds = method.extractRowBounds(args);result = sqlSession.<E>selectList(command.getName(), param, rowBounds);} else {result = sqlSession.<E>selectList(command.getName(), param);}// issue #510 Collections & arrays supportif (!method.getReturnType().isAssignableFrom(result.getClass())) {if (method.getReturnType().isArray()) {return convertToArray(result);} else {return convertToDeclaredCollection(sqlSession.getConfiguration(), result);}}return result;}private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {Cursor<T> result;Object param = method.convertArgsToSqlCommandParam(args);if (method.hasRowBounds()) {RowBounds rowBounds = method.extractRowBounds(args);result = sqlSession.<T>selectCursor(command.getName(), param, rowBounds);} else {result = sqlSession.<T>selectCursor(command.getName(), param);}return result;}private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {Object collection = config.getObjectFactory().create(method.getReturnType());MetaObject metaObject = config.newMetaObject(collection);metaObject.addAll(list);return collection;}@SuppressWarnings("unchecked")private <E> Object convertToArray(List<E> list) {Class<?> arrayComponentType = method.getReturnType().getComponentType();Object array = Array.newInstance(arrayComponentType, list.size());if (arrayComponentType.isPrimitive()) {for (int i = 0; i < list.size(); i++) {Array.set(array, i, list.get(i));}return array;} else {return list.toArray((E[])array);}}private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {Map<K, V> result;Object param = method.convertArgsToSqlCommandParam(args);if (method.hasRowBounds()) {RowBounds rowBounds = method.extractRowBounds(args);result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);} else {result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());}return result;}public static class ParamMap<V> extends HashMap<String, V> {private static final long serialVersionUID = -2212268410512043556L;@Overridepublic V get(Object key) {if (!super.containsKey(key)) {throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());}return super.get(key);}}public static class SqlCommand {private final String name;private final SqlCommandType type;public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {final String methodName = method.getName();final Class<?> declaringClass = method.getDeclaringClass();MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,configuration);if (ms == null) {if (method.getAnnotation(Flush.class) != null) {name = null;type = SqlCommandType.FLUSH;} else {throw new BindingException("Invalid bound statement (not found): "+ mapperInterface.getName() + "." + methodName);}} else {name = ms.getId();type = ms.getSqlCommandType();if (type == SqlCommandType.UNKNOWN) {throw new BindingException("Unknown execution method for: " + name);}}}public String getName() {return name;}public SqlCommandType getType() {return type;}private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,Class<?> declaringClass, Configuration configuration) {String statementId = mapperInterface.getName() + "." + methodName;if (configuration.hasStatement(statementId)) {return configuration.getMappedStatement(statementId);} else if (mapperInterface.equals(declaringClass)) {return null;}for (Class<?> superInterface : mapperInterface.getInterfaces()) {if (declaringClass.isAssignableFrom(superInterface)) {MappedStatement ms = resolveMappedStatement(superInterface, methodName,declaringClass, configuration);if (ms != null) {return ms;}}}return null;}}public static class MethodSignature {private final boolean returnsMany;private final boolean returnsMap;private final boolean returnsVoid;private final boolean returnsCursor;private final Class<?> returnType;private final String mapKey;private final Integer resultHandlerIndex;private final Integer rowBoundsIndex;private final ParamNameResolver paramNameResolver;public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);if (resolvedReturnType instanceof Class<?>) {this.returnType = (Class<?>) resolvedReturnType;} else if (resolvedReturnType instanceof ParameterizedType) {this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();} else {this.returnType = method.getReturnType();}this.returnsVoid = void.class.equals(this.returnType);this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();this.returnsCursor = Cursor.class.equals(this.returnType);this.mapKey = getMapKey(method);this.returnsMap = this.mapKey != null;this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);this.paramNameResolver = new ParamNameResolver(configuration, method);}public Object convertArgsToSqlCommandParam(Object[] args) {return paramNameResolver.getNamedParams(args);}public boolean hasRowBounds() {return rowBoundsIndex != null;}public RowBounds extractRowBounds(Object[] args) {return hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null;}public boolean hasResultHandler() {return resultHandlerIndex != null;}public ResultHandler extractResultHandler(Object[] args) {return hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null;}public String getMapKey() {return mapKey;}public Class<?> getReturnType() {return returnType;}public boolean returnsMany() {return returnsMany;}public boolean returnsMap() {return returnsMap;}public boolean returnsVoid() {return returnsVoid;}public boolean returnsCursor() {return returnsCursor;}private Integer getUniqueParamIndex(Method method, Class<?> paramType) {Integer index = null;final Class<?>[] argTypes = method.getParameterTypes();for (int i = 0; i < argTypes.length; i++) {if (paramType.isAssignableFrom(argTypes[i])) {if (index == null) {index = i;} else {throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");}}}return index;}private String getMapKey(Method method) {String mapKey = null;if (Map.class.isAssignableFrom(method.getReturnType())) {final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);if (mapKeyAnnotation != null) {mapKey = mapKeyAnnotation.value();}}return mapKey;}}}

2:核心对象的拆解
1:MapMethod对象
private final SqlCommand command;private final MethodSignature method;public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {this.command = new SqlCommand(config, mapperInterface, method);this.method = new MethodSignature(config, mapperInterface, method);}
}
MapMethod当中需要两个成员变量:SqlCommand+MethodSignature
把Method(Dao接口中的某一个方法的对象,再次包装了一层,包装成了MapMethod对象),增添了SqlCommand+MethodSignature两个成员。
2:SqlCommand对象
包含两个关键成员:
private final String name;
private final SqlCommandType type;private final String name;private final SqlCommandType type;//构造方法public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {final String methodName = method.getName();final Class<?> declaringClass = method.getDeclaringClass();MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,configuration);if (ms == null) {if (method.getAnnotation(Flush.class) != null) {name = null;type = SqlCommandType.FLUSH;} else {throw new BindingException("Invalid bound statement (not found): "+ mapperInterface.getName() + "." + methodName);}} else {name = ms.getId();type = ms.getSqlCommandType();if (type == SqlCommandType.UNKNOWN) {throw new BindingException("Unknown execution method for: " + name);}}}
name是nameSpace+id,type是insert,delete,update,select
3:MethodSignature
方法的入参类型和返回值类型。对SQL入参和SQL执行后的返回值的处理提供类型机制。
到这里我们是不是就有点清晰了,Mybatis中对代理设计模式的应用是这个样子的:
1:这个流程中几个核心的成员
MappedProxyFactory:执行Proxy.newProxyInstance(ClassLoader,UserDao.class,InvocationHandler);
来创建代理对象
MappedProxy:InvocationHandler的实习类,原始功能都没有的这种代理设计模式中的功能模块,
具体进行sqlSession.select,insert,update,delete的模块
MapMethod 是对方法对象的二次封装,我们知道Mybatis中UserDao中的method,可以提供nameSpace.id嘛,
这就是他的作用。
SqlCommand:对象nameSpace.id基于构造方法,放到了name属性里边,type是method增删改查的类型描述
MethodSignature:对方法入参和方法返参的描述

相关文章:
Mybatis源码分析系列之第四篇:Mybatis中代理设计模型源码详解
一: 前言 我们尝试在前几篇文章的内容中串联起来,防止各位不知所云。 1:背景 我们基于Mybatis作为后台Orm框架进行编码的时候,有两种方式。 //编码方式1 UserDao userDao sqlSession.getMapper(UserDao.class); userDao.quer…...
JDBC的API详解
🍎道阻且长,行则将至。🍓 目录 一、DriverManager 驱动管理类 1.注册驱动 2.获取数据库连接 二、Connection 数据库连接对象 1.获取执行对象 2.事务管理 三、Statement 1.执行DDL、DML语句 2.执行DQL语句 四、ResultSet 以JDBC快速…...
【深度强化学习】(4) Actor-Critic 模型解析,附Pytorch完整代码
大家好,今天和各位分享一下深度强化学习中的 Actor-Critic 演员评论家算法,Actor-Critic 算法是一种综合了策略迭代和价值迭代的集成算法。我将使用该模型结合 OpenAI 中的 Gym 环境完成一个小游戏,完整代码可以从我的 GitHub 中获得…...
SQL注入——文件上传
目录 一,mysql文件上传要点 二,文件上传指令 一句话木马 三,实例 1,判断注入方式 2,测试目标网站的闭合方式: 3,写入一句话木马 4,拿到控制权 一,mysql文件上传…...
【ESP32+freeRTOS学习笔记之“ESP32环境下使用freeRTOS的特性分析(新的开篇)”】
目录【ESP32freeRTOS学习笔记】系列新的开篇ESP-IDF对FreeRTOS的适配ESP-IDF环境中使用FreeRTOS的差异性简介关于FreeRTOS的配置关于ESP-IDF FreeRTOS Applications结语【ESP32freeRTOS学习笔记】系列新的开篇 ESP-IDF对FreeRTOS的适配 FreeRTOS是一个可以适用于多个不同MCU开…...
Uipath Excel 自动化系列18-RefreshPivotTable(刷新透视表)
活动描述 RefreshPivotTable(刷新透视表):如果透视表的数据源发生变化,需使用刷新透视表活动,该活动需与Use Excel File 活动选择的 Excel 文件一起使用。 使用如下图: RefreshPivotTable(刷新透视表)属性 属性 作用 Display…...
设计模式之不变模式
在并行软件开发过程中,同步操作是必不可少的。当多线程对同一个对象进行读写操作时,为了保证对象数据的一致性和正确性,有必要对对象进行同步操作,但同步操作对系统性能有损耗。不变模式可以去除这些同步操作,提高并行…...
C++11 map
C11中Map的使用Map是c的一个标准容器,她提供了很好一对一的关系,在一些程序中建立一个map可以起到事半功倍的效果,总结了一些map基本简单实用的操作!1. map最基本的构造函数;map<string , int >mapstring; map&l…...
docker基本命令 - 数据卷
作用 ● 做数据持久化。防止容器一旦停止运行,该容器中运行产生的数据就没了 ● 不同容器之间的数据共享(大鲸鱼背上各个小集装箱之间可以共享数据) 交互式命令使用 docker run -it -v / 宿主机的绝对路径目录:/容器内绝对路径目录 镜像名 docker run -it -v / 宿…...
SQL查漏补缺
有这么一道题,先看题目,表的内容如下 显示GDP比非洲任何国家都要高的国家名称(一些国家的GDP值可能为NULL)。 错误的查询: SELECT name FROM bbcWHERE gdp > ALL (SELECT gdp FROM bbc WHERE region Africa)正确的查询: SE…...
偏向锁撤销
偏向状态 一个对象创建时: 如果开启了偏向锁(默认开启),那么对象创建后,markword 值为 0x05 即最后 3 位为 101,这时它的thread、epoch、age 都为 0。偏向锁是默认是延迟的,不会在程序启动时立…...
Qt版海康MV多相机的采集显示程序
创建对话框工程MultiCamera工程文件MultiCamera.pro#------------------------------------------------- # # Project created by QtCreator 2023-03-11T16:52:53 # #-------------------------------------------------QT core guigreaterThan(QT_MAJOR_VERSION, 4): …...
2023年江苏省职业院校技能大赛中职网络安全赛项试卷-教师组任务书
2023年江苏省职业院校技能大赛中职网络安全赛项试卷-教师组任务书 一、竞赛时间 9:00-12:00,12:00-15:00,15:00-17:00共计8小时。 二、竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 第一阶段 基础设施设置与安全加固、网络安全事件响应、数…...
零基础小白如何自学网络安全成为顶尖黑客?
在成为黑客之前,你需要做两点准备: 1、学一门编程语言。学哪一门不重要,但你要参考一下下面的条例: C语言是Unix系统的基础。它(连同汇编语言)能让你学习对黑客非常重要的知识:内存的工作原理…...
外贸建站如何提高搜索引擎排名,吸引更多潜在客户?
在如今全球贸易日益繁荣的背景下,越来越多的企业开始重视外贸建站,并寻求提高搜索引擎排名以吸引更多潜在客户。 那么,如何才能有效地提高外贸网站的搜索引擎排名呢?本文将为您详细介绍几个有效的方法。 一、关键词优化 关键词…...
计算机网络考研-第一章学
计算机网学习总结第一章计算机系统概述1.1.1 导学1.1.2 操作系统的特征1.2 操作系统的发展与分类1.3 操作系统的运行环境1.3.1 操作系统的运行机制1.3.2 中断和异常1.3.3系统调用:1.3.4 操作系统的体系结构第一章总结第一章计算机系统概述 1.1.1 导学 1.1.2 操作系…...
【分布式版本控制系统Git】| Git概述、Git安装、Git常用命令
目录 一:概述 1.1. 何为版本控制 1.2. 为什么需要版本控制 1.3. 版本控制工具 1.4. Git 简史 1.5. Git 工作机制 1.6. Git和代码托管中心 二:安装 2.1. Git安装 三:常用命令 3.1 设置用户签名 3.2 初始化本地库 3.3 查看本地库…...
【人脸识别】ssd + opencv Eigenfaces 和 LBPH算法进行人脸监测和识别
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言ssd opencv Eigenfaces 和 LBPH算法进行人脸监测和识别1. ssd 目标监测2.opencv的三种人脸识别方法2.1 Eigenfaces2.2 LBPH前言 ssd opencv Eigenfaces 和 LB…...
PMP项目管理项目成本管理
目录1 项目成本管理概述2 规划成本管理3 估算成本4 制定预算5 控制成本1 项目成本管理概述 项目成本管理包括为使项目在批准的预算内完成而对成本进行规划、估算、预测、融资、筹资、管理和控制的各个过程,从而确保项目在批准的预算内完工。核心概念 项目成本管理旨…...
Vue3视频播放器组件Vue3-video-play入门教程
Vue3-video-play适用于 Vue3 的 hls.js 播放器组件 | 并且支持MP4/WebM/Ogg格式。 1、支持快捷键操作 2、支持倍速播放设置 3、支持镜像画面设置 4、支持关灯模式设置 5、支持画中画模式播放 6、支持全屏/网页全屏播放 7、支持从固定时间开始播放 8、支持移动端,移动…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
当下AI智能硬件方案浅谈
背景: 现在大模型出来以后,打破了常规的机械式的对话,人机对话变得更聪明一点。 对话用到的技术主要是实时音视频,简称为RTC。下游硬件厂商一般都不会去自己开发音视频技术,开发自己的大模型。商用方案多见为字节、百…...
简约商务通用宣传年终总结12套PPT模版分享
IOS风格企业宣传PPT模版,年终工作总结PPT模版,简约精致扁平化商务通用动画PPT模版,素雅商务PPT模版 简约商务通用宣传年终总结12套PPT模版分享:商务通用年终总结类PPT模版https://pan.quark.cn/s/ece1e252d7df...
自定义线程池1.2
自定义线程池 1.2 1. 简介 上次我们实现了 1.1 版本,将线程池中的线程数量交给使用者决定,并且将线程的创建延迟到任务提交的时候,在本文中我们将对这个版本进行如下的优化: 在新建线程时交给线程一个任务。让线程在某种情况下…...
