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

从 Mybatis 源码中,学习到的 10 种设计模式 总有不少研发伙伴问小傅哥:“为什么学设计模式、看框架源码、补技术知识,就一个普通的业务项目,会造飞机不也是天天写 CRUD 吗?你说的没错,但你天天写 CRUD,你觉得烦不?慌不?是不是既担心自己没有得到技术成长,也害怕将来没法用这些都是 CRUD 的项目去参加;述职、晋升、答辩,甚至可能要被迫面试时,自己手里一点干货也没有的情况。所以你/我作为一个小镇卷码家,当然要扩充自己的知识储备,否则架构,架构思维不懂设计,设计模式不会源码、源码学习不深,最后就用一堆 CRUD 写简历吗? 阅读详情

一、前言:小镇卷码家

总有不少研发伙伴问小傅哥:“为什么学设计模式、看框架源码、补技术知识,就一个普通的业务项目,会造飞机不也是天天写CRUD吗?”

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

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

二、源码:学设计模式

在 Mybatis 两万多行的框架源码实现中,使用了大量的设计模式来解耦工程架构中面对复杂场景的设计,这些是设计模式的巧妙使用才是整个框架的精华, 这也是小傅哥喜欢卷源码的重要原因 。经过小傅哥的整理有如下10种设计模式的使用,如图所示

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

好啦,不扯淡了,接下来小傅哥就以 《手写Mybatis:渐进式源码实践》 的学习,给大家列举出这10种设计模式,在Mybatis框架中都体现在哪里了!

三、类型:创建型模式

1. 工厂模式

源码详见: cn.bugstack.mybatis.session.SqlSessionFactory

public interface SqlSessionFactory {

   SqlSession openSession();

}

源码详见: cn.bugstack.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);
        }
    }

}

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

2. 单例模式

源码详见: cn.bugstack.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<>();
 
    //...
}

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

3. 建造者模式

源码详见: cn.bugstack.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 框架里的使用,那真是纱窗擦屁股,给你漏了一手。到处都是 XxxxBuilder,所有关于 XML 文件的解析到各类对象的封装,都使用建造者以及建造者助手来完成对象的封装。它的核心目的就是不希望把过多的关于对象的属性设置,写到其他业务流程中,而是用建造者的方式提供最佳的边界隔离。
  • 同类场景 : SqlSessionFactoryBuilder 、 XMLConfigBuilder 、 XMLMapperBuilder 、 XMLStatementBuilder 、 CacheBuilder

四、类型:结构型模式

1. 适配器模式

源码详见: cn.bugstack.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);

}

源码详见: cn.bugstack.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 工厂方法中进行使用。

2. 代理模式

源码详见: cn.bugstack.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 中的 MapperProxy 映射器代理实现类,它所实现的功能就是帮助我们完成 DAO 接口的具体实现类的方法操作,你的任何一个配置的 DAO 接口所调用的 CRUD 方法,都会被 MapperProxy 接管,调用到方法执行器等一系列操作,并返回最终的数据库执行结果。
  • 同类场景 : DriverProxy 、 Plugin 、 Invoker 、 MapperProxy

3. 组合模式

源码详见: cn.bugstack.mybatis.scripting.xmltags.SqlNode

public interface SqlNode {

    boolean apply(DynamicContext context);

}

源码详见: cn.bugstack.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;
    }

}

源码详见: cn.bugstack.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="cn.bugstack.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 XML 动态的 SQL 配置中,共提供了9种(trim/where/set/foreach/if/choose/when/otherwise/bind)标签的使用,让使用者可以组合出各类场景的 SQL 语句。而 SqlNode 接口的实现就是每一个组合结构中的规则节点,通过规则节点的组装完成一颗规则树组合模式的使用。 具体使用源码可以阅读 《手写Mybatis:渐进式源码实践》
  • 同类场景 :主要体现在对各类SQL标签的解析上,以实现 SqlNode 接口的各个子类为主。

4. 装饰器模式

源码详见: cn.bugstack.mybatis.session.Configuration

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

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

五、类型:行为型模式

1. 模板模式

源码详见: cn.bugstack.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;
}

源码详见: cn.bugstack.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 的 BaseExecutor 就是一个用于定义模板模式的抽象类,在这个类中把查询、修改的操作都定义出了一套标准的流程。
  • 同类场景 : BaseExecutor 、 SimpleExecutor 、 BaseTypeHandler

2. 策略模式

源码详见: cn.bugstack.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;

}

源码详见: cn.bugstack.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 处理 JDBC 执行后返回的结果时,需要按照不同的类型获取对应的值,这样就可以避免大量的 if 判断。所以这里基于 TypeHandler 接口对每个参数类型分别做了自己的策略实现。
  • 同类场景 : PooledDataSource\UnpooledDataSource 、 BatchExecutor\ResuseExecutor\SimpleExector\CachingExecutor 、 LongTypeHandler\StringTypeHandler\DateTypeHandler

3. 迭代器模式

源码详见: cn.bugstack.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);
        }
    }

        // ...

}

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

六、总结:“卷王”的心得

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

MyBatis ORM 框架源码设计模式的运用实例 通过梳理,MyBatis大约运用了10种左右设计模式。可以说,复杂且优秀的ORM 框架源码在设计和实现的过程中都会使用大量的设计模式。在解决复杂场景的问题时,需要采用分治、抽象的方法,运用设计模式和设计原则等相关知识,把问题合理切割为若干子问题,以便加以理解和解决。学习源码远不是只是为了应付面试,更重要的是学习优秀框架在复杂场景下的解决方案。通过学习这些优秀的方案技术,可以提高对技术设计和实现的理解,扩展编码思维,积累落地经验。只有经过这样长期的积累,我们才更有可能成为优秀的高级工程师和架构师。 阅读详情

相关推荐

MyBatis入门.pdf

MyBatis简单入门的一些介绍内容

二、自定义MyBatis,手写源码

以user实体类为例搭建一个入门案例(基于注解) 配置以及依赖与基于xml配置相同 将其中的映射文件删掉,只需要在UserDao中加入@select public interface UserDao { /** * 查询所有 * @return */ @Select("select * from user") List<User> getAll(); } 然后将配置文件中的代码稍作修改如下 将resource="com/lp/dao/Us

学习笔记 1032

Mybatis注解手写源码篇

通过手写模拟Mybatis底层源码实现,了解参数解析底层原理,了解执行SQL底层原理,了解Mybatis结果处理底层原理

普通二本,学这些东西,也能进大厂

已有3000人付费加入,到底提供了什么服务? Lottery秒杀抽奖、IM通信、手写Mybatis、全套的PDF、架构方案...,夯实技术,为续期职业生涯!

bugstack虫洞栈 4492

mybatis理论,mybatis完整的理论知识(pdf版本)

三层架构包含的三层: 界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer) 三层的职责 1. 界面层(表示层,视图层):主要功能是接受用户的数据,显示请求的处理结果。使用 web 页面和 用户交互, 手机 app 也就是表示层的,用户在 app 中操作,业务逻辑在服务器端处理。 2. 业务逻辑层:接收表示传递过来的数据, 检查数据,计算业务逻辑,调用数据访问层获取数据。 3. 数据访问层:与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交 给业务层, 同时将业务层处理的数据保存到数据库

手写mybatis框架源码由浅入深

手写mybatis框架源码 不借助其他任何框架,从最底层开始着手,用最简单的代码,将mybatis的底层框架,手写出来,让你对mybatis有一个更加深刻和直观的理解 详见:https://blog.youkuaiyun.com/weixin_43860634/article/details/127177579

手写Mybatis源码(原来真的很简单!!!)

主要分两部分,项目使用端:平常写代码所说的后台服务;持久层框架:即项目使用端引入的jar包类名定义角色定位分工协作Resources资源辅助类负责读取配置文件转化为输入流数据库资源类负责存储数据库连接信息SQL与结果集资源类负责存储SQL映射定义、存储结果集映射定义会话工厂构建者负责解析配置文件,创建会话工厂SqlSessionFactory会话工厂负责创建会话SqlSessionSqlSession会话指派执行器ExecutorExecutor执行器。

从基础到源码解析的学习记录 6158

淘宝链接跳转微信全攻略:自建合规中转页实现私域引流

在跨平台运营中,链接跳转是连接不同生态、实现流量引导的关键技术。其核心原理是通过服务器端或前端逻辑,将用户从一个平台的链接安全引导至目标页面,同时规避平台间的访问限制。这项技术的核心价值在于打破数据孤岛,实现跨平台用户沉淀与精细化运营,广泛应用于电商引流、私域流量构建和跨平台营销等场景。本文聚焦于**淘宝短链**与**企业微信**之间的跳转难题,提供了一套基于自建中转页面的完整工程解决方案,手把手教你如何通过合规的技术手段,将淘宝流量高效、安全地引导至微信生态,并实现数据追踪与客户分流。

weixin_34185512的博客 414

真香警告!Alibaba珍藏版mybatis手写文档,刷起来!

谈起MyBatis,我先提个问:什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录... 而今天咱要聊的就是Alibaba珍藏版mybatis手写文档,刷到的朋友真香警告呀!

mifffy_java的博客 1322

信奥编程启蒙 3068:【例13.3】时间差

现在给出一个时间,单位是秒,要求按照电子表格式输出,输出保证不会超过 2424 小时。3067:【例13.2】 电子表。把时间转化成电子表格式输出。电子表上的时间显示方法形如。

901

告别VMware Tools!用open-vm-tools在Ubuntu 22.04上搞定共享文件夹(附开机自动挂载)

本文详细介绍了在Ubuntu 22.04上使用open-vm-tools替代VMware Tools实现高效共享文件夹管理的方法。通过开源工具open-vm-tools,用户可避免传统方案的兼容性问题,享受自动适配内核、系统更新同步等优势。文章包含从安装配置到开机自动挂载的全流程指导,特别适合在VMware虚拟机中运行Ubuntu Desktop的开发者提升工作效率。

weixin_32520095的博客 585

《Mybatis 手撸专栏》第1章:开篇介绍,我要带你撸 Mybatis 啦

《Mybatis 手撸专栏》正式启动,此次内容会我们之前完成手写 Spring一样,拆解功能、简化流程、渐进实现,让读者能够更容易的学习到最有价值的知识。

bugstack虫洞栈 3296

算法 A 求稳健平均值和稳健标准差

文章目录算法原理代码 算法原理 算法 A 来自 GB/T 6379.5。应用此算法计算得到数据平均值和标准差的稳健值。稳健性是估计算法的特点,而不是其产生的估计值的特点,因此严格来说,称由此算法计算的平均值和标准差是稳健的是不确切的。然而,为避免使用繁琐的术语,“稳健均值”和“稳健标准差”应理解为利用稳健算法计算的总体均值和总体标准差的均值估计。 设数据为 xi,i∈(1,2,⋯ ,n)x_i, i\in(1,2,\cdots,n)xi​,i∈(1,2,⋯,n),记稳健平均值为 x∗,s∗x^*, s^*x

zhuo木鸟的博客 9260

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

23种设计模式,Mybatis源码中竟然用了一半!怒卷Mybatis设计模式,从源码中学习设计模式,太爽了!

bugstack虫洞栈 1803

Proteus仿真实战:用AT89C51+L298N搭建直流电机PID调速系统(附完整工程文件)

本文详细介绍了使用Proteus仿真平台结合AT89C51单片机和L298N驱动模块搭建直流电机PID闭环调速系统的全过程。从硬件选型、电路连接到PID算法实现与参数整定,提供了完整的开发指南和工程文件,帮助读者快速掌握闭环调速系统的设计与调试技巧。

weixin_28724661的博客 449

让数学变得更生动manim库的使用(1)-基本概念

0、概述 数学理论与数学公式的展示总是一件很枯燥的事情,但3b1b的manim动画库在github上开源之后,让这一切变得非常简单。目前这个库已经分成两个版本一个由3b1b自身维护。但作者并没有承诺长期维护该库,所以由专门团队再次基础上研发了相应的社区版,并对该版本进行长期维护。本文的所有案例都基于社区版的官方案例。本文主要介绍该库的基本概念相关的案例,文章主要以代码为主,关键代码都有相关注释。 另外本文的运行环境维jupyter这与其他的ide环境运行代码稍微有些不同 1、类库引入已经相关测试 引

amao1998的博客 9967

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

为什么学设计模式、看框架源码、补技术知识,就一个普通的业务项目,会造飞机不也是天天写CRUD吗?你说的没错,但你天天写CRUD,你觉得烦不?慌不?是不是既担心自己没有得到技术成长,也害怕将来没法用这些都是CRUD的项目去参加述职、晋升、答辩,甚至可能要被迫面试时,自己手里一点干货也没有的情况。所以,当然要扩充自己的知识储备,否则架构,架构思维不懂、设计,设计模式不会、源码、源码学习不深,最后就用一堆CRUD写简历吗?

DreamSun的博客 1685

华为OD机试 - 去除多余空格(Python/JS/C/C++ 2024 E卷 100分)

本专栏收录于《华为OD机试真题(Python/JS/C/C++)》。刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。去除文本多余空格,但不去除配对单引号之间的多余空格。给出关键词的起始和结束下标,去除多余空格后刷新关键词的起始和结束下标。条件约束:输入为两行字符串:第一行:待去除多余空格的文本,用例保证如果有单引号,则单引号成对出现,且单引号可能有多对。 第二

学Java,找哪吒 1217

从 Mybatis 源码中,学习到的 10 种设计模式(二)

从 Mybatis 源码中,学习到的 10 种设计模式(二)

weixin_49376454的博客 1032

DaVinci配置DID之三:代码实现

本系列主要基于DaVinCi工具链来展开具体DID读写的配置以及最终实现。1,CDD准备2,工具链配置3,代码实现4,验证结果本文讲述流程3话不多说,走你~博主是AutoSar小白,如果哪里表达的有问题,还请大佬们指点指点哈。接下来,让我们在下章关于测试验证的博文再相会哈~

weixin_48498880的博客 5365

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

从 Mybatis 源码中,学习到的 10 种设计模式(一)

从 Mybatis 源码中,学习到的 10 种设计模式(一)

weixin_49376454的博客 963

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

为什么 MyBatis 源码中,没有我那种 if···else

在MyBatis的两万多行的框架源码中,使用了大量的设计模式对工程架构中的复杂场景进行解耦,这些设计模式的巧妙使用是整个框架的精华。经过整理,大概有以下设计模式,如图1所示。01类型:创建型模式▊ 工厂模式SqlSessionFactory 的结构如图2所示。工厂模式:简单工厂是一种创建型模式,在父类中提供一个创建对象的方法,允许子类决定实例对象的类型。场景介绍:SqlSessionFactory...

qq_14958051的博客 172

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

【面试精讲】MyBatis设计模式及源码分析,MyBatis设计模式实现原理

每个设计模式的实现都体现了MyBatis设计的巧妙和优雅,通过这些模式的应用,MyBatis成功地将框架的灵活性、可扩展性和维护性提升到了一个新的高度。希望通过这三部分的解析,你能对MyBatis的设计和实现有一个更加深入的理解。

小明的Java问道之路 12万+
上一篇: Java系统架构师+开发应用面试突击宝典,值得学习
下一篇: 手把手教你实现 Tree 组件搜索过滤功能
JavaShark
博客等级 码龄4年 184粉丝 438原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值