资讯动态

从 Spring 与 Mybatis 源码学习创建型设计模式:单例、工厂与建造者的工业级实践

发布时间:2026/9/13 18:02:55 来源:尧图企业网站定制
从 Spring 与 Mybatis 源码学习创建型设计模式单例、工厂与建造者的工业级实践【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/GitHub_Trending/so/source-code-hunter本篇技术指南以 docs/LearningExperience/DesignPattern/从Spring及Mybatis框架源码中学习设计模式(创建型).md.md) 为主体脉络系统梳理创建型设计模式单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式的概念、经典实现与踩坑点并结合当前仓库对 Spring 与 Mybatis 源码的分析深入到DefaultSingletonBeanRegistry单例注册表、MybatisDataSourceFactory数据源工厂族、BaseBuilder建造者体系等真实实现中帮助你既看懂模式是什么也看透大神的代码怎么用。适用场景希望在 Spring 全家桶、Mybatis、JDK 源码中寻找设计模式最佳实践并将之迁移到个人项目的开发者。读完本篇你将能手写线程安全且抗反射/序列化攻击的单例理解 Spring 单例 bean 的三级缓存与双重加锁机制分清三种工厂模式各自的使用时机理解 Mybatis 初始化中BaseBuilder建造者体系的协作方式。一、单例模式确保全局只有一个实例1.1 个人理解单例模式的核心诉求是确保某个类只有一个实例并提供该实例的获取方法。无论是框架、JDK 还是实际项目开发单例的应用都非常普遍。从实践经验看绝大多数场景使用饿汉式或枚举来实现单例即可懒汉式也有应用但通过双检锁机制Double-Checked LockingDCL实现的线程安全单例在真实代码中反而很少见因为它的正确性高度依赖对 JVM 内存模型的深入理解。1.2 双检锁懒汉式的坑volatile 与指令重排最简单的实现方式是一个私有构造函数 一个私有静态变量 一个公共静态方法。懒汉式、饿汉式的简单写法这里不再赘述重点强调双检锁懒汉式实现的坑/** * 双检锁懒汉式实现线程安全的单例 * 关键词JVM指令重排、volatile、反射攻击 */ public class Singleton3 { /** * instance new Singleton3(); 在JVM中实际分三步执行 * 1、分配内存空间 * 2、初始化对象 * 3、将instance指向分配的内存地址。 * 但JVM具有指令重排的特性实际的执行顺序可能会是1、3、2导致多线程情况下出问题。 * 使用volatile修饰instance变量可以避免上述的指令重排 */ private volatile static Singleton3 instance; private Singleton3() { } public static Singleton3 getInstance() { if (instance null) { synchronized (Singleton3.class) { if (instance null) { instance new Singleton3(); } } } return instance; } }这里有两个容易困惑的点务必理解清楚外层判空的意义绝大多数线程进来时instance已非 null直接返回避免每次都进入synchronized代码块竞争锁这是双检性能上的核心价值为什么必须加 volatile第一个线程执行new Singleton3()时如果 JVM 把指向内存地址第 3 步重排到了初始化对象第 2 步之前那么其他线程看到instance ! null后直接return instance拿到的就是一个尚未初始化完成的对象。volatile通过内存屏障禁止这种重排保证初始化完成先行发生于引用发布。1.3 为什么枚举是单例的最佳实践上述Singleton3如果实现了Serializable接口每次序列化都会创建一个新对象要保证单例必须把所有字段声明为transient并额外提供readResolve()方法。反射攻击则可以通过setAccessible(true)将私有构造方法公共化进而绕过检查实例化出第二个对象除非在构造方法里手工编写禁止实例化第二个对象的防御代码。枚举实现的单例在面对复杂的序列化及反射攻击时依然能够保持自己的单例状态因此被认为是单例的最佳实践。JDK 对枚举的序列化有专门机制Enum实现了Serializable反序列化时通过valueOf按名称返回已有实例且反射 API 明确禁止通过Constructor#newInstance创建枚举实例。Mybatis 在定义 SQL 命令类型时就用到了枚举见org.apache.ibatis.mapping.SqlCommandTypepackage org.apache.ibatis.mapping; /** * author Clinton Begin */ public enum SqlCommandType { UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH; }这个枚举在 Mybatis 初始化解析 SQL 节点时被直接使用XMLStatementBuilder.parseStatementNode()中通过SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH))根据 SQL 节点的名称确定其命令类型进而决定flushCache、useCache等默认行为详细解析见 docs/Mybatis/核心处理层/1、MyBatis初始化.md。1.4 JDK 中的范例Runtime 与 Desktop饿汉式范例 —— java.lang.RuntimeJDK 1.0 起public class Runtime { /** 很明显这里用的是饿汉式实现单例 */ private static Runtime currentRuntime new Runtime(); public static Runtime getRuntime() { return currentRuntime; } /** Dont let anyone else instantiate this class */ private Runtime() {} }每个 Java 应用程序都有一个单例的Runtime对象通过getRuntime()获得。类加载阶段即完成初始化天然线程安全代价是无论是否使用都会创建实例。懒汉式范例 —— java.awt.Desktoppublic class Desktop { /** * Suppresses default constructor for noninstantiability. */ private Desktop() { peer Toolkit.getDefaultToolkit().createDesktopPeer(this); } /** * 由于对象较大这里使用了懒汉式延迟加载 * 方式比较简单直接把锁加在方法上。 */ public static synchronized Desktop getDesktop() { if (GraphicsEnvironment.isHeadless()) throw new HeadlessException(); if (!Desktop.isDesktopSupported()) { throw new UnsupportedOperationException(Desktop API is not supported on the current platform); } sun.awt.AppContext context sun.awt.AppContext.getAppContext(); Desktop desktop (Desktop)context.get(Desktop.class); if (desktop null) { desktop new Desktop(); context.put(Desktop.class, desktop); } return desktop; } }注意这里synchronized直接加在方法上粗粒度加锁对象存于AppContext这个线程局部上下文中。可见 JDK 官方也未在单例上使用双检锁。1.5 Spring 的单例 bean 是如何实现的源码级剖析Spring 实现单例 bean不是用我们上面手写的双检锁而是ConcurrentHashMap 注册表 synchronized 同步机制的组合。核心是AbstractBeanFactory.doGetBean()与DefaultSingletonBeanRegistry.getSingleton()两者在本仓库均有专门解析见 docs/Spring/clazz/Spring-DefaultSingletonBeanRegistry.md 与 docs/Spring/IoC/4、依赖注入(DI).md.md)。第一步doGetBean() 中判断是否为单例并用 ObjectFactory 匿名内部类延迟创建public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { /** * 真正实现向IoC容器获取Bean的功能也是触发依赖注入(DI)功能的地方 */ SuppressWarnings(unchecked) protected T T doGetBean(final String name, final ClassT requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { ...... //创建单例模式bean的实例对象 if (mbd.isSingleton()) { //这里使用了一个匿名内部类创建Bean实例对象并且注册给所依赖的对象 sharedInstance getSingleton(beanName, new ObjectFactoryObject() { public Object getObject() throws BeansException { try { /** * 创建一个指定的Bean实例对象如果有父级继承则合并子类和父类的定义 * 走子类中的实现 */ return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } } }); //获取给定Bean的实例对象 bean getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } } }第二步getSingleton() 中先查缓存未命中则创建并注册/** * 默认的单例bean注册器 */ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { /** 单例的bean实例的缓存 */ private final MapString, Object singletonObjects new ConcurrentHashMapString, Object(64); /** * 返回给定beanName的已经注册的单例bean如果没有注册则注册并返回 */ public Object getSingleton(String beanName, ObjectFactory? singletonFactory) { Assert.notNull(beanName, beanName must not be null); // 加锁保证单例bean在多线程环境下不会创建多个 synchronized (this.singletonObjects) { // 先从缓存中取有就直接返回没有就创建、注册到singletonObjects、返回 Object singletonObject this.singletonObjects.get(beanName); if (singletonObject null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, Singleton bean creation not allowed while the singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)); } beforeSingletonCreation(beanName); boolean recordSuppressedExceptions (this.suppressedExceptions null); if (recordSuppressedExceptions) { this.suppressedExceptions new LinkedHashSetException(); } try { singletonObject singletonFactory.getObject(); } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions null; } afterSingletonCreation(beanName); } // 注册到单例bean的缓存 addSingleton(beanName, singletonObject); } return (singletonObject ! NULL_OBJECT ? singletonObject : null); } } }与手写单例的对照关系一目了然手写单例要素Spring 的对应实现私有静态变量instancesingletonObjects注册表ConcurrentHashMapsynchronized代码块synchronized (this.singletonObjects)同步块双重判空get()判空 创建后addSingleton()注册对象的创建逻辑外部传入的ObjectFactory.getObject()进阶Spring 的三级缓存与循环依赖。上述getSingleton(beanName, singletonFactory)是创建型入口而读取侧还维护了三层缓存一级singletonObjects完整 bean、二级earlySingletonObjects半成品仅实例化未初始化、三级singletonFactoriesObjectFactorylambda 表达式。当一个 bean 实例化完毕但尚未初始化时Spring 会提前把() - getEarlyBeanReference(beanName, mbd, bean)放入三级缓存依赖它的另一个 bean 在属性填充时populateBean→applyPropertyValues→resolveReference→getBean从三级缓存取出这个半成品从而解决循环依赖。详细调用链见 docs/Spring/IoC/循环依赖.md。二、简单工厂模式集中管控同一系列类的实例化2.1 个人理解简单工厂模式把同一系列类的实例化交由一个工厂类集中管控。与其说它是一种设计模式倒不如把它看成一种编程习惯——因为它不符合开闭原则增加新的产品类需要修改工厂类的代码。2.2 简单实现public interface Hero { void speak(); } public class DaJi implements Hero { Override public void speak() { System.out.println(妲己陪你玩 ~); } } public class LiBai implements Hero { Override public void speak() { System.out.println(今朝有酒 今朝醉 ~); } } /** 对各种英雄进行集中管理 */ public class HeroFactory { public static Hero getShibing(String name) { if (LiBai.equals(name)) return new LiBai(); else if (DaJi.equals(name)) return new DaJi(); else return null; } }这种设计方式只在FBM 资金管理模块中看到过对 100 个按钮类进行了集中管控但其设计结构比上面这种要复杂得多例如引入注册表/配置驱动来缓解每次加按钮都要改工厂的问题。2.3 使用时机工厂方法模式符合开闭原则增加新的产品类不用修改既有代码应当优先考虑如果产品类结构简单且数量庞大如上百个按钮类简单工厂反而更容易维护——把所有if/else集中在一个类里比分散到上百个工厂类里更直观。三、工厂方法模式一个子工厂对应一个产品3.1 个人理解在顶级工厂接口/抽象类中定义产品类的获取方法由具体的子工厂实例化对应的产品。一般一个子工厂对应一个特定的产品实现对产品的集中管控并且符合开闭原则——新增产品只需新增一个工厂子类不改动既有代码。3.2 Mybatis 中的范例DataSourceFactory 家族Mybatis 中数据源DataSource的获取就使用了该设计模式。接口DataSourceFactory定义了获取DataSource对象的方法各实现类完成获取对应类型DataSource对象的实现。本仓库对这块的完整源码解析见 docs/Mybatis/基础支持层/2、DataSource及Transaction模块.md 与 docs/Mybatis/核心处理层/Mybatis-DataSource.md。public interface DataSourceFactory { // 设置DataSource的属性一般紧跟在DataSource初始化之后 void setProperties(Properties props); // 获取DataSource对象 DataSource getDataSource(); } public class JndiDataSourceFactory implements DataSourceFactory { private DataSource dataSource; Override public DataSource getDataSource() { return dataSource; } Override public void setProperties(Properties properties) { try { InitialContext initCtx; Properties env getEnvProperties(properties); if (env null) { initCtx new InitialContext(); } else { initCtx new InitialContext(env); } if (properties.containsKey(INITIAL_CONTEXT) properties.containsKey(DATA_SOURCE)) { Context ctx (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT)); dataSource (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE)); } else if (properties.containsKey(DATA_SOURCE)) { dataSource (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE)); } } catch (NamingException e) { throw new DataSourceException(There was an error configuring JndiDataSourceTransactionPool. Cause: e, e); } } } public class UnpooledDataSourceFactory implements DataSourceFactory { protected DataSource dataSource; // 在实例化该工厂时就完成了DataSource的实例化 public UnpooledDataSourceFactory() { this.dataSource new UnpooledDataSource(); } Override public DataSource getDataSource() { return dataSource; } } public class PooledDataSourceFactory extends UnpooledDataSourceFactory { // 与UnpooledDataSourceFactory的不同之处是其初始化的DataSource为PooledDataSource public PooledDataSourceFactory() { this.dataSource new PooledDataSource(); } }三个具体工厂各司其职工厂类产品对象适用场景JndiDataSourceFactory从 JNDI 上下文查找的DataSource应用服务器托管的连接池如 Tomcat 数据源UnpooledDataSourceFactoryUnpooledDataSource每次getConnection()直接新建连接无池化PooledDataSourceFactoryPooledDataSource内部持有UnpooledDataSource自带简易连接池实现连接复用工厂方法模式在 Mybatis 中的真实调用链可从 docs/Mybatis/核心处理层/1、MyBatis初始化.md 的environmentsElement()看到解析environments节点时若未指定 environment 则取environments default...属性dataSourceElement()读取dataSource typePOOLED的type属性通过TypeAliasRegistry注册的别名反射创建工厂typeAliasRegistry.registerAlias(POOLED, PooledDataSourceFactory.class)、registerAlias(UNPOOLED, UnpooledDataSourceFactory.class)、registerAlias(JNDI, JndiDataSourceFactory.class)调用factory.setProperties(props)注入property配置再factory.getDataSource()得到DataSource最终封装进Environment.Builder注入到全局唯一的Configuration对象。UnpooledDataSourceFactory.setProperties()还有一个实现细节值得注意它以driver.为前缀识别驱动级参数其余参数通过MetaObject反射判断DataSource是否有对应 setter 再注入遇到未知属性会抛出DataSourceException(Unknown DataSource property: ...)——这种属性前缀分类 反射校验的写法在解析配置类工厂时非常实用。什么时候该用简单工厂、什么时候该用工厂方法工厂方法符合开闭原则新增产品类不用修改代码应优先考虑若产品类结构简单且数量庞大简单工厂更容易维护。四、抽象工厂模式一个子工厂对应一组相关产品4.1 个人理解设计结构上与工厂方法模式很像最主要的区别是工厂方法模式一个子工厂只对应一个具体的产品抽象工厂模式一个子工厂对应一组具有相关性的产品即存在多个获取不同产品的方法。这种设计模式在实践中最少见反倒是工厂方法模式见的最多。4.2 简单实现public abstract class AbstractFactory { abstract protected AbstractProductA createProductA(); abstract protected AbstractProductB createProductB(); } public class ConcreteFactory1 extends AbstractFactory { Override protected AbstractProductA createProductA() { return new ProductA1(); } Override protected AbstractProductB createProductB() { return new ProductB1(); } } public class ConcreteFactory2 extends AbstractFactory { Override protected AbstractProductA createProductA() { return new ProductA2(); } Override protected AbstractProductB createProductB() { return new ProductB2(); } } public class Client { public static void main(String[] args) { AbstractFactory factory new ConcreteFactory1(); AbstractProductA productA factory.createProductA(); AbstractProductB productB factory.createProductB(); // 结合使用productA和productB进行后续操作保证产品族的一致性 } }4.3 JDK 中的范例javax.xml.transform.TransformerFactoryJDK 的javax.xml.transform.TransformerFactory组件使用了类似抽象工厂模式的设计抽象类TransformerFactory定义了两个抽象方法newTransformer()和newTemplates()分别用于生成Transformer对象和Templates对象其子类进行了不同的实现以下为 JDK 1.8 源码public abstract class TransformerFactory { public abstract Transformer newTransformer(Source source) throws TransformerConfigurationException; public abstract Templates newTemplates(Source source) throws TransformerConfigurationException; } /** * SAXTransformerFactory 继承了 TransformerFactory */ public class TransformerFactoryImpl extends SAXTransformerFactory implements SourceLoader, ErrorListener { Override public Transformer newTransformer(Source source) throws TransformerConfigurationException { final Templates templates newTemplates(source); final Transformer transformer templates.newTransformer(); if (_uriResolver ! null) { transformer.setURIResolver(_uriResolver); } return (transformer); } Override public Templates newTemplates(Source source) throws TransformerConfigurationException { ...... return new TemplatesImpl(bytecodes, transletName, xsltc.getOutputProperties(), _indentNumber, this); } } public class SmartTransformerFactoryImpl extends SAXTransformerFactory { public Transformer newTransformer(Source source) throws TransformerConfigurationException { if (_xalanFactory null) { createXalanTransformerFactory(); } if (_errorlistener ! null) { _xalanFactory.setErrorListener(_errorlistener); } if (_uriresolver ! null) { _xalanFactory.setURIResolver(_uriresolver); } _currFactory _xalanFactory; return _currFactory.newTransformer(source); } public Templates newTemplates(Source source) throws TransformerConfigurationException { if (_xsltcFactory null) { createXSLTCTransformerFactory(); } if (_errorlistener ! null) { _xsltcFactory.setErrorListener(_errorlistener); } if (_uriresolver ! null) { _xsltcFactory.setURIResolver(_uriresolver); } _currFactory _xsltcFactory; return _currFactory.newTemplates(source); } }两个子工厂分别产出XSLTC 编译器实现与Xalan 实现两组相关产品Transformer 与 Templates客户端面向抽象工厂编程无需关心具体实现族。五、建造者模式把复杂对象的构建拆分成清晰步骤5.1 个人理解与类图该模式主要用于将复杂对象的构建过程分解成一个一个简单的步骤或分摊到多个类中构建保证构建过程层次清晰、代码不过分臃肿屏蔽掉复杂对象内部的具体构建细节其类图结构如下该模式的主要角色建造者接口Builder定义建造者构建产品对象的各种公共行为主要分为建造方法与获取构建好的产品对象具体建造者ConcreteBuilder实现上述接口方法导演Director通过调用具体建造者创建需要的产品对象产品Product被建造的复杂对象。导演角色不必了解产品类的内部细节只提供需要的信息给建造者由具体建造者处理这些信息处理过程可能比较复杂并完成产品构造使产品对象的上层代码与产品对象的创建过程解耦。建造者模式将复杂产品的创建过程分散到不同构造步骤中既实现了对创建过程的精细控制也使过程更清晰。每个具体建造者都能创建出完整的产品对象且具体建造者之间相互独立因此系统可以通过不同的具体建造者得到不同的产品对象当有新产品出现时无需修改原有代码只需添加新的具体建造者即可完成扩展符合开放-封闭原则。5.2 典型范例StringBuilder 与 StringBuffer拼 SQL 语句时常用的StringBuffer和StringBuilder就使用了建造者设计模式JDK 1.8 源码abstract class AbstractStringBuilder implements Appendable, CharSequence { /** The value is used for character storage. */ char[] value; /** The count is the number of characters used. */ int count; AbstractStringBuilder(int capacity) { value new char[capacity]; } public AbstractStringBuilder append(String str) { if (str null) return appendNull(); int len str.length(); ensureCapacityInternal(count len); // 这里完成了对复杂String的构造将str拼接到当前对象后面 str.getChars(0, len, value, count); count len; return this; } } /** * since JDK 1.5 */ public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence { public StringBuilder() { super(16); } Override public StringBuilder append(String str) { super.append(str); return this; } Override public String toString() { // Create a copy, dont share the array return new String(value, 0, count); } } /** * since JDK 1.0 */ public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence { /** toString返回的最后一个值的缓存。在修改StringBuffer时清除。 */ private transient char[] toStringCache; public StringBuffer() { super(16); } /** * 与StringBuilder建造者最大的不同就是增加了线程安全机制 */ Override public synchronized StringBuffer append(String str) { toStringCache null; super.append(str); return this; } }在建造者模式的角色划分中Appendable接口append方法扮演建造者接口AbstractStringBuilder与StringBuilder/StringBuffer扮演具体建造者默认容量 16超出自动扩容最终产品是被构造出来的String。StringBuilder与StringBuffer的核心差异仅在于append等方法是否加synchronized——即线程安全由建造者自身保证这正是建造者模式通过不同具体建造者得到不同产品行为的体现。5.3 Mybatis 中的范例BaseBuilder 建造者体系Mybatis 的初始化过程使用了建造者模式详见 docs/Mybatis/核心处理层/1、MyBatis初始化.md角色划分如下建造者模式角色Mybatis 中的类建造者接口Builder抽象类BaseBuilder实现公用方法、定义公共属性具体建造者ConcreteBuilderXMLConfigBuilder解析 mybatis-config.xml、XMLMapperBuilder解析映射配置文件、XMLStatementBuilder解析 SQL 节点产品Product全局唯一、All-In-One 的Configuration对象导演DirectorSqlSessionFactoryBuilder即SqlSessionFactoryBuilder使用BaseBuilder建造者组件对复杂对象Configuration进行了构建。public abstract class BaseBuilder { /** * Configuration 是 MyBatis 初始化过程的核心对象并且全局唯一 * MyBatis 中几乎全部的配置信息会保存到 Configuration 对象中。 * 也有人称它是一个All-In-One配置对象 */ protected final Configuration configuration; /** * 在 mybatis-config.xml 配置文件中可以使用typeAliases标签定义别名 * 这些定义的别名都会记录在该 TypeAliasRegistry 对象中 */ protected final TypeAliasRegistry typeAliasRegistry; /** * 在 mybatis-config.xml 配置文件中可以使用typeHandlers标签添加自定义 * TypeHandler完成指定数据库类型与 Java 类型的转换这些 TypeHandler * 都会记录在 TypeHandlerRegistry 中 */ protected final TypeHandlerRegistry typeHandlerRegistry; /** * BaseBuilder 中记录的 TypeAliasRegistry 对象和 TypeHandlerRegistry 对象 * 其实是全局唯一的它们都是在 Configuration 对象初始化时创建的 */ public BaseBuilder(Configuration configuration) { this.configuration configuration; this.typeAliasRegistry this.configuration.getTypeAliasRegistry(); this.typeHandlerRegistry this.configuration.getTypeHandlerRegistry(); } }导演角色SqlSessionFactoryBuilder的编排逻辑public class SqlSessionFactoryBuilder { public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { // 读取配置文件 XMLConfigBuilder parser new XMLConfigBuilder(inputStream, environment, properties); // 解析配置文件得到 Configuration 对象然后用其创建 DefaultSqlSessionFactory 对象 return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException(Error building SqlSession., e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } }具体建造者XMLConfigBuilder把 mybatis-config.xml 中每个节点封装成独立解析方法parseConfiguration()依次调用private void parseConfiguration(XNode root) { try { // 解析properties节点 propertiesElement(root.evalNode(properties)); // 解析settings节点 Properties settings settingsAsProperties(root.evalNode(settings)); loadCustomVfs(settings); loadCustomLogImpl(settings); // 解析typeAliases节点 typeAliasesElement(root.evalNode(typeAliases)); // 解析plugins节点 pluginElement(root.evalNode(plugins)); // 解析objectFactory节点 objectFactoryElement(root.evalNode(objectFactory)); // 解析objectWrapperFactory节点 objectWrapperFactoryElement(root.evalNode(objectWrapperFactory)); // 解析reflectorFactory节点 reflectorFactoryElement(root.evalNode(reflectorFactory)); settingsElement(settings); // 解析environments节点 environmentsElement(root.evalNode(environments)); // 解析databaseIdProvider节点 databaseIdProviderElement(root.evalNode(databaseIdProvider)); // 解析typeHandlers节点 typeHandlerElement(root.evalNode(typeHandlers)); // 解析mappers节点 mapperElement(root.evalNode(mappers)); } catch (Exception e) { throw new BuilderException(Error parsing SQL Mapper Configuration. Cause: e, e); } }具体建造者XMLMapperBuilder负责解析映射配置文件parse()中先通过configuration.isResourceLoaded(resource)判重再调用configurationElement()依次解析cache-ref、cache、resultMap、sql、select|insert|update|delete等节点最后通过bindMapperForNamespace()将命名空间与 Mapper 接口绑定注册进MapperRegistry解析过程中出现的前向引用如尚未解析的resultMap会记录到Configuration的 incomplete 集合由parsePendingResultMaps()等补偿机制兜底重试。具体建造者XMLStatementBuilder负责把单个 SQL 节点解析成MappedStatement先校验id与databaseId是否匹配当前数据库用SqlCommandType.valueOf(...)确定命令类型处理include、selectKey再通过LanguageDriver.createSqlSource()创建SqlSource最终调用builderAssistant.addMappedStatement(...)注册到Configuration。BaseBuilder 体系与标准建造者模式的关键差异BaseBuilder的建造者模式主要是为了将复杂对象Configuration的构建过程拆解得更清晰把整个构建过程分解到多个具体建造者类中需要这些具体建造者共同配合才能完成Configuration的构造单个具体建造者不具有单独构造产品的能力——这与StringBuilder/StringBuffer单个建造者即可完成产品构造不同。这种多建造者协作的变体正是应对配置文件种类多、节点多、依赖关系复杂这类真实场景的工程化答案如果把这些解析逻辑全部塞进一个类该类会极其臃肿、难以维护拆分到多个类中代码规整、思路清晰且符合开闭原则。六、创建型模式选择指南把五种创建型模式放在一起对照实战选型会更加清晰模式核心思想是否满足开闭原则典型应用单例模式全局唯一实例—java.lang.Runtime、Spring 单例 bean、MybatisConfiguration简单工厂一个工厂集中创建同类产品否新增产品需改工厂FBM 资金管理模块 100 按钮类工厂方法一个子工厂对应一个产品是MybatisDataSourceFactory家族抽象工厂一个子工厂对应一组相关产品是JDKTransformerFactory建造者拆分复杂对象的构建步骤是StringBuilder/StringBuffer、MybatisBaseBuilder初始化体系实践要点回顾单例优先用枚举或饿汉式必须懒加载时用双检锁并务必加 volatile防止指令重排发布未初始化对象Spring 的单例不是一个类怎么保证唯一而是一个注册表 一把锁的容器级单例管理配合三级缓存解决循环依赖工厂选择上产品多且简单 → 简单工厂集中管理产品可扩展 → 工厂方法产品存在族关系 → 抽象工厂遇到构造参数多、构建流程复杂的对象如 Mybatis 的Configuration用建造者模式把构建过程拆分到多个协作类中保持代码层次清晰。延伸阅读当前仓库单例的容器级实现docs/Spring/clazz/Spring-DefaultSingletonBeanRegistry.md、docs/Spring/IoC/循环依赖.md工厂方法在 Mybatis 数据源中的应用docs/Mybatis/基础支持层/2、DataSource及Transaction模块.md、docs/Mybatis/核心处理层/Mybatis-DataSource.md建造者模式在 Mybatis 初始化中的应用docs/Mybatis/核心处理层/1、MyBatis初始化.md本系列其余篇目结构型设计模式.md)、行为型设计模式.md)、从框架源码中学习设计模式的感悟【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/GitHub_Trending/so/source-code-hunter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价