【设计模式】从Mybatis源码中学习到的10种设计模式

news2025/2/27 8:48:05

文章目录

  • 一、前言
  • 二、源码:学设计模式
  • 三、类型:创建型模式
    • 1. 工厂模式
    • 2. 单例模式
    • 3. 建造者模式
  • 四、类型:结构型模式
    • 1. 适配器模式
    • 2. 代理模式
    • 3. 组合模式
    • 4. 装饰器模式
  • 五、类型:行为型模式
    • 1. 模板模式
    • 2. 策略模式
    • 3. 迭代器模式
  • 六、总结

一、前言

“为什么学设计模式、看框架源码、补技术知识,就一个普通的业务项目,会造飞机不也是天天写CRUD吗?”

你说的没错,但你天天写CRUD,你觉得烦不? 慌不? 是不是既担心自己没有得到技术成长,也害怕将来没法用这些都是CRUD的项目去参加述职、晋升、答辩,甚至可能要被迫面试时,自己手里一点干货也没有的情况。

所以,当然要扩充自己的知识储备,否则架构,架构思维不懂、设计,设计模式不会、源码、源码学习不深,最后就用一堆CRUD写简历吗?

二、源码:学设计模式

在 Mybatis 两万多行的框架源码实现中,使用了大量的设计模式来解耦工程架构中面对复杂场景的设计,这些是设计模式的巧妙使用才是整个框架的精华,整理有如下10种设计模式的使用,如图所示
在这里插入图片描述

讲道理,如果只是把这10种设计模式背下来,等着下次面试的时候拿出来说一说,虽然能有点帮助,不过这种学习方式就真的算是把路走窄了。就像你每说一个设计模式,能联想到这个设计模式在Mybatis的框架中,体现到哪个流程中的源码实现上了吗?这个源码实现的思路能不能用到你的业务流程开发里?别总说你的流程简单,用不上设计模式!

三、类型:创建型模式

1. 工厂模式

源码详见:com.demo.mybatis.session.SqlSessionFactory

public interface SqlSessionFactory {

   SqlSession openSession();

}

源码详见:com.demo.mybatis.session.defaults.DefaultSqlSessionFactory

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
        Transaction tx = null;
        try {
            final Environment environment = configuration.getEnvironment();
            TransactionFactory transactionFactory = environment.getTransactionFactory();
            tx = transactionFactory.newTransaction(configuration.getEnvironment().getDataSource(), TransactionIsolationLevel.READ_COMMITTED, false);
            // 创建执行器
            final Executor executor = configuration.newExecutor(tx);
            // 创建DefaultSqlSession
            return new DefaultSqlSession(configuration, executor);
        } catch (Exception e) {
            try {
                assert tx != null;
                tx.close();
            } catch (SQLException ignore) {
            }
            throw new RuntimeException("Error opening session.  Cause: " + e);
        }
    }

}

在这里插入图片描述

Mybatis 工厂模式

  • 工厂模式:简单工厂,是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例对象的类型。
  • 场景介绍:SqlSessionFactory 是获取会话的工厂,每次我们使用 Mybatis 操作数据库的时候,都会开启一个新的会话。在会话工厂的实现中负责获取数据源环境配置信息、构建事务工厂、创建操作SQL的执行器,并最终返回会话实现类。
  • 同类设计:SqlSessionFactory、ObjectFactory、MapperProxyFactory、DataSourceFactory

2. 单例模式

源码详见:com.demo.mybatis.session.Configuration

public class Configuration {

    // 缓存机制,默认不配置的情况是 SESSION
    protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;

    // 映射注册机
    protected MapperRegistry mapperRegistry = new MapperRegistry(this);

    // 映射的语句,存在Map里
    protected final Map<String, MappedStatement> mappedStatements = new HashMap<>();
    // 缓存,存在Map里
    protected final Map<String, Cache> caches = new HashMap<>();
    // 结果映射,存在Map里
    protected final Map<String, ResultMap> resultMaps = new HashMap<>();
    protected final Map<String, KeyGenerator> keyGenerators = new HashMap<>();

    // 插件拦截器链
    protected final InterceptorChain interceptorChain = new InterceptorChain();

    // 类型别名注册机
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

    // 类型处理器注册机
    protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();

    // 对象工厂和对象包装器工厂
    protected ObjectFactory objectFactory = new DefaultObjectFactory();
    protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

    protected final Set<String> loadedResources = new HashSet<>();
 
    //...
}

在这里插入图片描述

Mybatis 单例模式

  • 单例模式:是一种创建型模式,让你能够保证一个类只有一个实例,并提供一个访问该实例的全局节点。
  • 场景介绍:Configuration 就像狗皮膏药一样大单例,贯穿整个会话的生命周期,所以的配置对象;映射、缓存、入参、出参、拦截器、注册机、对象工厂等,都在 Configuration 配置项中初始化。并随着 SqlSessionFactoryBuilder 构建阶段完成实例化操作。
  • 同类场景:ErrorContext、LogFactory、Configuration

3. 建造者模式

源码详见:com.demo.mybatis.mapping.ResultMap#Builder

public class ResultMap {

    private String id;
    private Class<?> type;
    private List<ResultMapping> resultMappings;
    private Set<String> mappedColumns;

    private ResultMap() {
    }

    public static class Builder {
        private ResultMap resultMap = new ResultMap();

        public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
            resultMap.id = id;
            resultMap.type = type;
            resultMap.resultMappings = resultMappings;
        }

        public ResultMap build() {
            resultMap.mappedColumns = new HashSet<>();
            // step-13 新增加,添加 mappedColumns 字段
            for (ResultMapping resultMapping : resultMap.resultMappings) {
                final String column = resultMapping.getColumn();
                if (column != null) {
                    resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
                }
            }
            return resultMap;
        }

    }
    
    // ... get
}

在这里插入图片描述

Mybatis 建造者模式

  • 建造者模式:使用多个简单的对象一步一步构建成一个复杂的对象,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
  • 场景介绍:关于建造者模式在 Mybatis 框架里的使用,那真是纱窗擦屁股,给你漏了一手。到处都是 XxxxBuilder,所有关于 XML 文件的解析到各类对象的封装,都使用建造者以及建造者助手来完成对象的封装。它的核心目的就是不希望把过多的关于对象的属性设置,写到其他业务流程中,而是用建造者的方式提供最佳的边界隔离。
  • 同类场景:SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder

四、类型:结构型模式

1. 适配器模式

源码详见:com.demo.mybatis.logging.Log

public interface Log {

  boolean isDebugEnabled();

  boolean isTraceEnabled();

  void error(String s, Throwable e);

  void error(String s);

  void debug(String s);

  void trace(String s);

  void warn(String s);

}

源码详见:com.demo.mybatis.logging.slf4j.Slf4jImpl

public class Slf4jImpl implements Log {

  private Log log;

  public Slf4jImpl(String clazz) {
    Logger logger = LoggerFactory.getLogger(clazz);

    if (logger instanceof LocationAwareLogger) {
      try {
        // check for slf4j >= 1.6 method signature
        logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class);
        log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger);
        return;
      } catch (SecurityException e) {
        // fail-back to Slf4jLoggerImpl
      } catch (NoSuchMethodException e) {
        // fail-back to Slf4jLoggerImpl
      }
    }

    // Logger is not LocationAwareLogger or slf4j version < 1.6
    log = new Slf4jLoggerImpl(logger);
  }

  @Override
  public boolean isDebugEnabled() {
    return log.isDebugEnabled();
  }

  @Override
  public boolean isTraceEnabled() {
    return log.isTraceEnabled();
  }

  @Override
  public void error(String s, Throwable e) {
    log.error(s, e);
  }

  @Override
  public void error(String s) {
    log.error(s);
  }

  @Override
  public void debug(String s) {
    log.debug(s);
  }

  @Override
  public void trace(String s) {
    log.trace(s);
  }

  @Override
  public void warn(String s) {
    log.warn(s);
  }

}
  • 适配器模式:是一种结构型设计模式,它能使接口不兼容的对象能够相互合作。
  • 场景介绍:正是因为有太多的日志框架,包括:Log4j、Log4j2、Slf4J等等,而这些日志框架的使用接口又都各有差异,为了统一这些日志工具的接口,Mybatis 定义了一套统一的日志接口,为所有的其他日志工具接口做相应的适配操作。
  • 同类场景:主要集中在对日志的适配上,Log 和 对应的实现类,以及在 LogFactory 工厂方法中进行使用。
    在这里插入图片描述
    Mybatis 适配器模式

2. 代理模式

源码详见:com.demo.mybatis.binding.MapperProxy

public class MapperProxy<T> implements InvocationHandler, Serializable {

    private static final long serialVersionUID = -6424540398559729838L;

    private 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 {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            final MapperMethod mapperMethod = cachedMapperMethod(method);
            return mapperMethod.execute(sqlSession, args);
        }
    }
    
    // ...

}

在这里插入图片描述

Mybatis 代理模式

  • 代理模式:是一种结构型模式,让你能够提供对象的替代品或其占位符。代理控制着对原对象的访问,并允许在将请求提交给对象前进行一些处理。
  • 场景介绍:不吹牛的讲,没有代理模式,就不会有各类的框架存在。就像 Mybatis 中的 MapperProxy 映射器代理实现类,它所实现的功能就是帮助我们完成 DAO 接口的具体实现类的方法操作,你的任何一个配置的 DAO 接口所调用的 CRUD 方法,都会被 MapperProxy 接管,调用到方法执行器等一系列操作,并返回最终的数据库执行结果。
  • 同类场景:DriverProxy、Plugin、Invoker、MapperProxy

3. 组合模式

源码详见:com.demo.mybatis.scripting.xmltags.SqlNode

public interface SqlNode {

    boolean apply(DynamicContext context);

}

源码详见:com.demo.mybatis.scripting.xmltags.IfSqlNode

public class IfSqlNode implements SqlNode{

    private ExpressionEvaluator evaluator;
    private String test;
    private SqlNode contents;

    public IfSqlNode(SqlNode contents, String test) {
        this.test = test;
        this.contents = contents;
        this.evaluator = new ExpressionEvaluator();
    }

    @Override
    public boolean apply(DynamicContext context) {
        // 如果满足条件,则apply,并返回true
        if (evaluator.evaluateBoolean(test, context.getBindings())) {
            contents.apply(context);
            return true;
        }
        return false;
    }

}

源码详见:com.demo.mybatis.scripting.xmltags.XMLScriptBuilder

public class XMLScriptBuilder extends BaseBuilder {

    private void initNodeHandlerMap() {
        // 9种,实现其中2种 trim/where/set/foreach/if/choose/when/otherwise/bind
        nodeHandlerMap.put("trim", new TrimHandler());
        nodeHandlerMap.put("if", new IfHandler());
    }
 
    List<SqlNode> parseDynamicTags(Element element) {
        List<SqlNode> contents = new ArrayList<>();
        List<Node> children = element.content();
        for (Node child : children) {
            if (child.getNodeType() == Node.TEXT_NODE || child.getNodeType() == Node.CDATA_SECTION_NODE) {

            } else if (child.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = child.getName();
                NodeHandler handler = nodeHandlerMap.get(nodeName);
                if (handler == null) {
                    throw new RuntimeException("Unknown element " + nodeName + " in SQL statement.");
                }
                handler.handleNode(element.element(child.getName()), contents);
                isDynamic = true;
            }
        }
        return contents;
    }
    
    // ...
}

配置详见:resources/mapper/Activity_Mapper.xml

<select id="queryActivityById" parameterType="com.demo.mybatis.test.po.Activity" resultMap="activityMap" flushCache="false" useCache="true">
    SELECT activity_id, activity_name, activity_desc, create_time, update_time
    FROM activity
    <trim prefix="where" prefixOverrides="AND | OR" suffixOverrides="and">
        <if test="null != activityId">
            activity_id = #{activityId}
        </if>
    </trim>
</select>

在这里插入图片描述

Mybatis 组合模式

  • 组合模式:是一种结构型设计模式,你可以使用它将对象组合成树状结构,并且能独立使用对象一样使用它们。
  • 场景介绍:在 Mybatis XML 动态的 SQL 配置中,共提供了9种(trim/where/set/foreach/if/choose/when/otherwise/bind)标签的使用,让使用者可以组合出各类场景的 SQL 语句。而 SqlNode 接口的实现就是每一个组合结构中的规则节点,通过规则节点的组装完成一颗规则树组合模式的使用。
  • 同类场景:主要体现在对各类SQL标签的解析上,以实现 SqlNode 接口的各个子类为主。

4. 装饰器模式

源码详见:com.demo.mybatis.session.Configuration

public Executor newExecutor(Transaction transaction) {
    Executor executor = new SimpleExecutor(this, transaction);
    // 配置开启缓存,创建 CachingExecutor(默认就是有缓存)装饰者模式
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);
    }
    return executor;
}

在这里插入图片描述

Mybatis 装饰器模式

  • 装饰器模式:是一种结构型设计模式,允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。
  • 场景介绍:Mybatis 的所有 SQL 操作,都是经过 SqlSession 会话调用 SimpleExecutor 简单实现的执行器完成的,而一级缓存的操作也是在简单执行器中处理。那么这里二级缓存因为是基于一级缓存刷新操作的,所以在实现上,通过创建一个缓存执行器,包装简单执行器的处理逻辑,实现二级缓存操作。那么这里用到的就是装饰器模式,也叫俄罗斯套娃模式。
  • 同类场景:主要提前在 Cache 缓存接口的实现和 CachingExecutor 执行器中。

五、类型:行为型模式

1. 模板模式

源码详见:com.demo.mybatis.executor.BaseExecutor

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    if (closed) {
        throw new RuntimeException("Executor was closed.");
    }
    // 清理局部缓存,查询堆栈为0则清理。queryStack 避免递归调用清理
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
        clearLocalCache();
    }
    List<E> list;
    try {
        queryStack++;
        // 根据cacheKey从localCache中查询数据
        list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
        if (list == null) {
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
        }
    } finally {
        queryStack--;
    }
    if (queryStack == 0) {
        if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            clearLocalCache();
        }
    }
    return list;
}

源码详见:com.demo.mybatis.executor.SimpleExecutor

protected int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        // 新建一个 StatementHandler
        StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
        // 准备语句
        stmt = prepareStatement(handler);
        // StatementHandler.update
        return handler.update(stmt);
    } finally {
        closeStatement(stmt);
    }
}

在这里插入图片描述

Mybatis 模板模式

  • 模板模式:是一种行为设计模式,它在超类中定义了一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。
  • 场景介绍:只要存在一系列可被标准定义的流程,在流程的步骤大部分是通用逻辑,只有一少部分是需要子类实现的,那么通常会采用模板模式来定义出这个标准的流程。就像 Mybatis 的 BaseExecutor 就是一个用于定义模板模式的抽象类,在这个类中把查询、修改的操作都定义出了一套标准的流程。
  • 同类场景:BaseExecutor、SimpleExecutor、BaseTypeHandler

2. 策略模式

源码详见:com.demo.mybatis.type.TypeHandler

public interface TypeHandler<T> {

    /**
     * 设置参数
     */
    void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

    /**
     * 获取结果
     */
    T getResult(ResultSet rs, String columnName) throws SQLException;

    /**
     * 取得结果
     */
    T getResult(ResultSet rs, int columnIndex) throws SQLException;

}

源码详见:com.demo.mybatis.type.LongTypeHandler

public class LongTypeHandler extends BaseTypeHandler<Long> {

    @Override
    protected void setNonNullParameter(PreparedStatement ps, int i, Long parameter, JdbcType jdbcType) throws SQLException {
        ps.setLong(i, parameter);
    }

    @Override
    protected Long getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getLong(columnName);
    }

    @Override
    public Long getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getLong(columnIndex);
    }

}

在这里插入图片描述

Mybatis 策略模式

  • 策略模式:是一种行为设计模式,它能定义一系列算法,并将每种算法分别放入独立的类中,以使算法的对象能够互相替换。
  • 场景介绍:在 Mybatis 处理 JDBC 执行后返回的结果时,需要按照不同的类型获取对应的值,这样就可以避免大量的 if 判断。所以这里基于 TypeHandler 接口对每个参数类型分别做了自己的策略实现。
  • 同类场景:PooledDataSource\UnpooledDataSource、BatchExecutor\ResuseExecutor\SimpleExector\CachingExecutor、LongTypeHandler\StringTypeHandler\DateTypeHandler

3. 迭代器模式

源码详见:com.demo.mybatis.reflection.property.PropertyTokenizer

public class PropertyTokenizer implements Iterable<PropertyTokenizer>, Iterator<PropertyTokenizer> {

    public PropertyTokenizer(String fullname) {
        // 班级[0].学生.成绩
        // 找这个点 .
        int delim = fullname.indexOf('.');
        if (delim > -1) {
            name = fullname.substring(0, delim);
            children = fullname.substring(delim + 1);
        } else {
            // 找不到.的话,取全部部分
            name = fullname;
            children = null;
        }
        indexedName = name;
        // 把中括号里的数字给解析出来
        delim = name.indexOf('[');
        if (delim > -1) {
            index = name.substring(delim + 1, name.length() - 1);
            name = name.substring(0, delim);
        }
    }

		// ...

}

在这里插入图片描述

Mybatis 迭代器模式

  • 迭代器模式:是一种行为设计模式,让你能在不暴露集合底层表现形式的情况下遍历集合中所有的元素。
  • 场景介绍:PropertyTokenizer 是用于 Mybatis 框架 MetaObject 反射工具包下,用于解析对象关系的迭代操作。这个类在 Mybatis 框架中使用的非常频繁,包括解析数据源配置信息并填充到数据源类上,以及参数的解析、对象的设置都会使用到这个类。
  • 同类场景:PropertyTokenizer

六、总结

一份源码的成体系拆解渐进式学习,可能需要1~2个月的时间,相比于爽文和疲于应试要花费更多的经历。但你总会在一个大块时间学习完后,会在自己的头脑中构建出一套完整体系关于此类知识的技术架构,无论从哪里入口你都能清楚各个分支流程的走向,这也是你成为技术专家路上的深度学习。

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

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

相关文章

长江流域9省2市可视化(不展示业务信息水质及真实断面)

一、处理9省2市地理信息为geojson集成到项目 shp转geojson关键Java代码 /*** shp转换为Geojson* param shpPath* return*/ public static Map shape2Geojson(String shpPath,String filePath){Map map new HashMap();FeatureJSON fjson new FeatureJSON();try{StringBuffer …

阶段二33_面向对象高级_IO[转换流,对象流]

知识点&#xff1a; 1.转换流&#xff1a;InputStreamReader&#xff0c;OutputStreamWriter2.对象流&#xff1a;ObjectInputStream&#xff0c;ObjectOutputStream一.转换流 1.转换流原理图 2.转换流概述 转换流就是来进行字节流和字符流之间转换的 InputStreamReader是从…

p75 应急响应-数据库漏洞口令检索应急取证箱

数据来源 必须知识点&#xff1a; 第三方应用由于是选择性安装&#xff0c;如何做好信息收集和漏洞探针也是获取攻击者思路的重要操作&#xff0c; 除去本身漏洞外&#xff0c;提前预知或口令相关攻击也要进行筛选。排除三方应用攻击行为&#xff0c;自查漏洞分析攻击者思路&a…

表白墙(服务器版)

文章目录一、准备工作二、前后端交互后端前端三、数据库版本一、准备工作 我们之前实现过这样一个表白墙&#xff0c;具体前端代码参考 表白墙 这篇文章 但是我们之前写的这个表白墙有一些问题&#xff1a; 1.如果我们刷新页面/重新开启&#xff0c;之前的数据就不见了 2.我们…

python pyc文件

参考自 What are pyc files in Python 和Python什么情况下会生成pyc文件&#xff1f; - 知乎 加上了我自己的理解 官方文档有这么解释 A program doesnt run any faster when it is read from a ‘.pyc’ or ‘.pyo’ file than when it is read from a ‘.py’ file; the o…

C生万物 | 一探指针函数与函数指针的奥秘

文章目录一、指针函数1、定义2、示例二、函数指针1、概念理清2、如何调用函数指针&#xff1f;3、两道“有趣”的代码题O(∩_∩)O< 第一题 >< 第二题 >4、函数指针数组概念明细具体应用&#xff1a;转移表✔5、指向函数指针数组的指针三、实战训练 —— 回调函数1、…

Pix4D软件简易使用方法

一、实验目的 学习无人机处理软件 Pix4D 的各项基本功能模块&#xff0c;掌握处理无人机影像的一般处理流程及质量评价。学习新建项目&#xff0c;对图像进行初始化操作以便后处理。学会制作正射影像图&#xff0c;生成质量报告&#xff0c;并对其进行分析。 二、实验内容 &…

抽象轻松MySqL

第一步安装下载MySQL 手把手教你下载安装 第一步打开官方网站 这里提供两种——第一种懒人版&#xff1a;MySQL点击蓝色字会有链接 第二种手动版本&#xff1a;百度搜索Mysql&#xff08;注意不要点.cn的因为有点翻译问题&#xff09; 点开后的图如下 接下来开始装备下载 点…

Disentangled Graph Collaborative Filtering

代码地址&#xff1a;https://github.com/ xiangwang1223/disentangled_graph_collaborative_filtering Background&#xff1a; 现有模型在很大程度上以统一的方式对用户-物品关系进行建模(将模型看做黑盒&#xff0c;历史交互作为输入&#xff0c;Embedding作为输出。)&…

【C++进阶之路】初始C++

文章目录一.C的发展历史时代背景产生原因发型版本二.C的应用场景三.C 的学习成本C的难度C的学习阶段21天精通C的梗一.C的发展历史 时代背景 20世纪60年代——软件危机。部分原因:C语言等计算机语言是面向过程语言&#xff0c;在编写大型程序需要高度抽象与建模&#xff0c;此…

HTML中表格标签<table><tr><tb><th>中单元格的合并问题

前情知晓 层级关系如下&#xff1a; <table><tr><td> </td><th> </th></tr></table> <table>...</table> 用于定义一个表格开始和结束 <tr>...</tr> 定义一行标签&#xff0c;一组行标签内可以建立…

【前端】从零开始读懂Web3

序言 用心生活&#xff0c;用力向上&#xff0c;微笑前行&#xff0c;就是对生活最好的回馈。 本专栏说明&#xff1a; 主要是记录在分享知识的同时&#xff0c;不定时给大家送书的活动。 参与方式&#xff1a; 赠书数量&#xff1a;本次送书 3 本&#xff0c;评论区抽3位小伙伴…

Python进阶特性(类型标注)

1.4 Python进阶特性(类型标注) 1.4.1 类型标注介绍 Python属于动态类型语言&#xff0c;只有在运行代码的时候才能够知道变量类型&#xff0c;那么这样容易产生传入参数类型不一致的问题&#xff0c;导致出现意想不到的bug。这也是动态类型语言天生的一个问题。 所以在Python…

【Spring】— Spring中Bean的装配方式

Spring中Bean的装配方式Bean的装配方式1.基于XML的装配2.基于Annotation的装配3.自动装配Bean的装配方式 Bean的装配可以理解为依赖关系注入&#xff0c;Bean的装配方式即Bean依赖注入的方式。Spring容器支持多种形式的Bean装配方式&#xff0c;如基于XML的装配、基于Annotatio…

电力系统中针对状态估计的虚假数据注入攻击建模与对策(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…

免费部署属于自己的chatGPT网站,欢迎大家试玩

最近我发现了一个非常nice的部署网站的工具&#xff0c; railway&#xff0c;这个网站是国外的&#xff0c;所以部署出来的项目域名是国外的&#xff0c;并不需要担心封号&#xff0c;也不需要进行域名注册&#xff0c;部署成功之后会自动生成域名&#xff0c;在国内就能够正常…

[NSSRound#11] 密码学个人赛

这个比赛没有参加,跟别人要了些数据跑一下,其实交互这东西基本上一样,跑通就行. ez_enc 这题有点骗人,给了一堆AB串,一开始以为是培根密码,结果出来很乱.再看长度:192 应该就是01替换 a ABAABBBAABABAABBABABAABBABAAAABBABABABAAABAAABBAABBBBABBABBABBABABABAABBAABBABAA…

“心机boy”马斯克:明面上呼吁暂停先进AI研发,背地里悄悄买1万块GPU推进大模型项目

来源: AI前线 微信号&#xff1a;ai-front 整理 | 冬梅、核子可乐 为了研发自家 AIGC&#xff0c; 马斯克狂买 GPU 并四处挖人 当地时间 4 月 11 日&#xff0c;据多家外媒报道&#xff0c;尽管高调建议在整个行业范围内停止 AI 训练&#xff0c;但伊隆马斯克本人倒是在 T…

VMware:安装centos7

环境&#xff1a; 准备好VMware软件 准备好centos镜像 如有需要 寻找镜像&&真机安装&&真机安装时候找不到硬盘 可以查看 &#xff1a; linux&#xff1a;真机安装centos linux&#xff08;突发事件&#xff1a;解决卡在安装界面&#xff09;{寻找镜像--u…

springboot+vue简历系统(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的简历系统简历系统。项目源码请联系风歌&#xff0c;文末附上联系信息 。 目前有各类成品java毕设&#xff0c;需要请看文末联系方式 …