Mybatis源码分析系列之第四篇:Mybatis中代理设计模型源码详解

news2024/11/26 16:22:42

一: 前言

我们尝试在前几篇文章的内容中串联起来,防止各位不知所云。

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(){
            @Override
            public 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代理对象创建

    @Test
    public 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;
    }

    @Override
    public 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;
  }

  @Override
  public 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;
  }

  @UsesJava7
  private 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 support
    if (!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;

    @Override
    public 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 是对方法对象的二次封装,我们知道MybatisUserDao中的method,可以提供nameSpace.id嘛,
这就是他的作用。
SqlCommand:对象nameSpace.id基于构造方法,放到了name属性里边,type是method增删改查的类型描述
MethodSignature:对方法入参和方法返参的描述

在这里插入图片描述

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

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

相关文章

[入门必看]数据结构1.1:数据结构的基本概念

[入门必看]数据结构1.1&#xff1a;数据结构的基本概念第一章 绪论1.1 数据结构的基本概念知识总览1.1.1 基本概念和术语数据类型、抽象数据类型&#xff1a;1.1.2 数据结构的三要素数据的逻辑结构数据的物理结构&#xff08;存储结构&#xff09;数据的运算知识回顾与重要考点…

【数据库概论】第十一章 数据库并发控制

第十一章 并发控制 在多处理机系统中&#xff0c;每个处理机可以运行一个事务&#xff0c;多个处理机可以同时运行多个事务&#xff0c;实现多个事务并行运行&#xff0c;这就是同时并发方式。当多个用户并发存取数据库时会产生多个事务同时存取同一事务的情况&#xff0c;如果…

ESP32设备驱动-红外寻迹传感器驱动

红外寻迹传感器驱动 1、红外寻迹传感器介绍 红外寻迹传感器具有一对红外线发射管与接收管,发射管发射出一定频率的红外线,当检测方向遇到障碍物(反射面)时,红外线反射回来被接收管接收,经过比较器电路处理之后,输出接口会输出一个数字信号(低电平或高电平,取决于电路…

Nginx配置实例-反向代理案例二

实现效果&#xff1a;使用nginx反向代理&#xff0c;根据访问的路径跳转到不同端口服务 nginx监听端口为9000&#xff0c; 访问 http://127.0.0.1:9000/edu/ 直接跳转到127.0.0.1:8080 访问 http://127.0.0.1:9000/vod/ 直接跳转到127.0.0.1:8081 一、准备工作 1. 准备两个tom…

TCP相关概念

目录 一.滑动窗口 1.1概念 1.2滑动窗口存在的意义 1.3 滑动窗口的大小变化 1.4丢包问题 二.拥塞控制 三.延迟应答 四.捎带应答 五.面向字节流 六.粘包问题 七.TIME_WAIT状态 八.listen第2个参数 九.TCP总结 一.滑动窗口 1.1概念 概念&#xff1a;双方在进行通信时&a…

2023年软考高级-系统分析师考试学习指导计划!

2023年软考高级-系统分析师考试学习指导计划&#xff01; 一、导学阶段 第一天 考试情况及备考攻略&#xff1a;https://www.educity.cn/xuanke/rk/rjsp/?sywzggw 考试介绍&#xff1a;https://www.educity.cn/wangxiao2/c410653/sp110450.html 考试经验分享&#xff1a;h…

如何在Linux中优雅的使用 head 命令,用来看日志简直溜的不行

当您在 Linux 的命令行上工作时&#xff0c;有时希望快速查看文件的第一行&#xff0c;例如&#xff0c;有个日志文件不断更新&#xff0c;希望每次都查看日志文件的前 10 行。很多朋友使用文本编辑的命令是vim&#xff0c;但还有个命令head也可以让轻松查看文件的第一行。 在…

记录使用chatgpt的复杂经历

背景 由于最近要写论文&#xff0c;c站的gpt也变样了&#xff0c;无奈之下和同学借了一个gpt账号&#xff0c;才想起没有npv&#xff0c;不好意思去要&#xff0c;也不想买&#xff0c;于是我找了很多临时免费的直到我看到有一家&#xff0c;邀请10人即可&#xff0c;而且只用…

江苏专转本 专科生的最好出入

专转本 专科生的最好出入(1) 减少每天刷某音&#xff0c;刷朋友圈的时间无所事事、捧着手机刷的不是某音就是朋友圈&#xff0c;三年下来没去成一次图书馆。碎片化信息很大程度只是在浪费时间&#xff0c;不如每天比同龄人至少多出1小时时间&#xff0c;用来看书、护肤、健身。…

磨金石教育摄影技能干货分享|高邮湖上观花海

江苏高邮&#xff0c;说到这里所有人能想到的&#xff0c;就是那烟波浩渺的高邮湖。高邮在旅游方面并不出名&#xff0c;但是这里的自然人文景观绝对不输于其他地方。高邮不止有浩瀚的湖泊&#xff0c;春天的油菜花海同样壮观。春日的午后&#xff0c;与家人相约游玩&#xff0…

C语言--字符串函数1

目录前言strlenstrlen的模拟实现strcpystrcatstrcat的模拟实现strcmpstrcmp的模拟实现strncpystrncatstrncmpstrstrstrchr和strrchrstrstr的模拟实现前言 本章我们将重点介绍处理字符和字符串的库函数的使用和注意事项。 strlen 我们先来看一个我们最熟悉的求字符串长度的库…

【Echart多场景示例应用】Echarts柱状图、折线图、饼图、雷达图等完整示例。 echarts主标题和副标题的位置、样式等设置(已解决附源码)

**【写在前面】**前端时间做一个echarts的页面调整&#xff0c;临时客户要求加一个参数&#xff08;总容量&#xff09;显示&#xff0c;当时我就想用个默认的副标题吧&#xff0c;哪知客户和我说得紧跟在主标题后面&#xff0c;于是乎我就根据设置做了一个调整&#xff0c;我也…

燕山大学-面向对象程序设计实验 - 实验1 C++基础

CSDN的各位uu们你们好,今天千泽燕山大学-面向对象程序设计实验 - 实验1 C基础 相关内容, 接下来让我们一起进入c的神奇小世界吧,相信看完你也能写出自己的实验报告!实验一 C基础 1.1 实验目的 1&#xff0e;了解并熟悉开发环境&#xff0c;学会调试程序&#xff1b; 2&#xf…

超过10000人学习的Fiddler抓包教程,只需一小时就可以精通!

如果还是有朋友不太明白的话&#xff0c;可以看看这套视频&#xff0c;有实战讲解 零基础玩转Fiddler抓包在测试领域应用实战&#xff01;一、Fiddler与其他抓包工具的区别 1、Firebug虽然可以抓包&#xff0c;但是对于分析http请求的详细信息&#xff0c;不够强大。模拟http请…

【Linux】信号的产生、保存、捕捉处理 (四种信号产生、核心存储、用户态与内核态、信号集及其操作函数)

文章目录1、什么是信号&#xff1f;2、信号的产生2.1 通过键盘产生信号2.2 通过系统调用产生信号2.3 硬件异常产生的信号2.4 由软件条件产生的信号2.5 进程的核心转储3、信号的保存4、信号的捕捉4.1 用户态和内核态4.2 用户态到内核态的切换4.3 信号捕捉过程5、信号集操作函数以…

Spring——Spring整合Mybatis(XML和注解两种方式)

框架整合spring的目的:把该框架常用的工具对象交给spring管理&#xff0c;要用时从IOC容器中取mybatis对象。 在spring中应该管理的对象是sqlsessionfactory对象&#xff0c;工厂只允许被创建一次&#xff0c;所以需要创建一个工具类&#xff0c;把创建工厂的代码放在里面&…

Qt不会操作?Qt原理不知道? | Qt详细讲解

文章目录Qt界面开发必备知识UI界面与控件类型介绍Qt设计器原理控件类型的介绍信号与槽机制处理常用控件创建与设置常见展示型控件创建与设置常见动作型控件创建与设置常见输入型控件创建与设置常见列表控件创建于设置Qt中对象树的介绍项目源码结构刨析.pro.hmain.cpp.cppQt界面…

JVM的几种GC

GC JVM在进行GC时&#xff0c;并不是对这三个区域统一回收。大部分时候&#xff0c;回收都是新生代~ 新生代GC&#xff08;minor GC&#xff09;&#xff1a; 指发生在新生代的垃圾回收动作&#xff0c;因为Java对象大多都具备朝生夕灭的特点&#xff0c;所以minor GC发生得非…

【问题排查】Linux虚拟机无法识别串口与ttyUSB

虚拟机串口连接失败问题 小哥的Linux系统是用虚拟机来装的&#xff0c;最近恰好需要用到串口和Linux进行通信&#xff0c;连接好硬件之后&#xff0c;发现虚拟机上找不到串口。 经查询才发现通过虚拟机启动的系统&#xff0c;正常情况下是无法使用串口进行通信的&#xff0c;需…

Ast2500增加用户自定义功能

备注&#xff1a;这里使用的AMI的开发环境MegaRAC进行AST2500软件开发&#xff0c;并非openlinux版本。1、添加上电后自动执行的任务在PDKAccess.c中列出了系统启动过程中的所有任务&#xff0c;若需要添加功能&#xff0c;在相应的任务中添加自定义线程。一般在两个任务里面添…