
1. 问题背景MyBatis源码中的条件处理差异第一次阅读MyBatis源码时很多开发者都会发现一个有趣的现象我们自己写的业务代码里充斥着各种if-else条件判断但MyBatis的核心源码中却很少见到这种传统分支结构。这种差异背后隐藏着框架设计的重要思想。以动态SQL处理为例当我们需要根据不同条件拼接SQL语句时新手可能会写出这样的代码if (param.getUsername() ! null) { sql AND username param.getUsername() ; } if (param.getStartTime() ! null) { sql AND create_time param.getStartTime(); } // 更多条件判断...而MyBatis内部处理同样需求时采用的是完全不同的实现方式。这种差异不是偶然的而是框架设计者有意为之的架构选择。2. 设计模式的应用为什么避免if-else2.1 策略模式替代条件分支MyBatis大量使用策略模式来处理原本需要if-else的场景。以SQL语句生成器为例框架定义了SqlNode接口不同节点类型实现各自的apply方法public interface SqlNode { boolean apply(DynamicContext context); } // 例如IfSqlNode的实现 public class IfSqlNode implements SqlNode { private ExpressionEvaluator evaluator; private String test; private SqlNode contents; Override public boolean apply(DynamicContext context) { if (evaluator.evaluateBoolean(test, context.getBindings())) { contents.apply(context); return true; } return false; } }这种设计将条件判断封装在各个策略实现中外部只需调用统一的apply接口避免了复杂的条件嵌套。2.2 责任链模式处理流程MyBatis的插件机制采用责任链模式实现InterceptorChain维护了所有拦截器public class InterceptorChain { private final ListInterceptor interceptors new ArrayList(); public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target interceptor.plugin(target); } return target; } }相比用if判断每个拦截条件这种设计使得拦截器的添加和移除更加灵活完全符合开闭原则。3. 动态SQL的实现原理3.1 OGNL表达式引擎MyBatis使用OGNL(Object-Graph Navigation Language)处理动态SQL中的条件判断select idfindUsers SELECT * FROM users where if testname ! null AND name #{name} /if if testage ! null AND age #{age} /if /where /select在解析阶段这些if标签会被转换为IfSqlNode对象test属性作为OGNL表达式在运行时求值。3.2 组合模式构建SQL树整个动态SQL被解析为一棵由SqlNode组成的语法树MixedSqlNode ├── StaticTextSqlNode (SELECT * FROM users) └── WhereSqlNode ├── IfSqlNode (name ! null) │ └── TextSqlNode (AND name ?) └── IfSqlNode (age ! null) └── TextSqlNode (AND age ?)这种结构使得SQL的生成过程变成对树的遍历完全避免了过程式代码中的条件判断。4. 源码中的设计取舍4.1 可扩展性优先MyBatis作为框架必须考虑各种扩展场景。例如XML配置的解析使用不同的Handlerpublic class XMLMapperBuilder { private void bindMapperForNamespace() { // 使用不同的Builder处理不同元素 configurationElement(parser.evalNode(/mapper)); } private void configurationElement(XNode context) { buildStatementFromContext(context.evalNodes(select|insert|update|delete)); } }这种设计允许在不修改核心代码的情况下通过新增Handler支持新的XML元素。4.2 性能优化考虑在SQL参数处理时MyBatis使用类型处理器注册表public final class TypeHandlerRegistry { private final MapJdbcType, TypeHandler? jdbcTypeHandlerMap new EnumMap(JdbcType.class); private final MapType, MapJdbcType, TypeHandler? typeHandlerMap new ConcurrentHashMap(); public T TypeHandlerT getTypeHandler(ClassT type, JdbcType jdbcType) { // 直接从Map获取处理器 } }相比链式if判断这种注册表方式在大量类型处理时性能更优。5. 实际开发中的启示5.1 何时该用if-else虽然MyBatis源码避免if-else但在业务代码中合理使用条件判断是必要的简单业务逻辑当条件分支少于3个时非扩展性需求确定不会新增条件的情况性能敏感路径需要极致优化的代码段5.2 何时考虑重构当出现以下情况时建议参考MyBatis的设计模式嵌套层级超过3层经常需要新增条件分支相同条件判断在多处重复需要动态添加/移除处理逻辑5.3 重构示例将传统条件判断改为策略模式重构前public String generateReport(ReportType type) { if (type ReportType.EXCEL) { return generateExcel(); } else if (type ReportType.PDF) { return generatePDF(); } else if (type ReportType.HTML) { return generateHTML(); } throw new UnsupportedOperationException(); }重构后public interface ReportGenerator { boolean supports(ReportType type); String generate(); } public class ReportGeneratorFactory { private ListReportGenerator generators; public String generate(ReportType type) { return generators.stream() .filter(g - g.supports(type)) .findFirst() .orElseThrow(UnsupportedOperationException::new) .generate(); } }6. 常见问题与解决方案6.1 动态SQL安全问题使用${}导致的SQL注入问题!-- 不安全的写法 -- select idfindByTable SELECT * FROM ${tableName} /select !-- 安全的写法 -- select idfindByTable SELECT * FROM choose when testtableName usersusers/when when testtableName ordersorders/when otherwiseproducts/otherwise /choose /select6.2 复杂条件处理对于特别复杂的条件逻辑可以考虑使用