设计模式【创建型】--》Spring和Mybatis框架源码和JDK源码

单例模式:

个人理解:一般都会使用饿汉式或者枚举来实现单例,,懒汉式也有一些应用

                        双检锁实现的坑
 /**
* @author 云之君
* 双检锁 懒汉式,实现线程安全的单例
* 关键词:JVM指令重排、volatile、反射攻击
*/
public class Singleton3 {
    /**
     * 对于我们初级开发来说,这个volatile在实际开发中可能见过,但很少会用到
     * 这里加个volatile进行修饰,也是本单例模式的精髓所在。
     * 下面的 instance = new Singleton3(); 这行代码在JVM中其实是分三步执行的:
     * 1、分配内存空间;
     * 2、初始化对象;
     * 3、将instance指向分配的内存地址。
     * 但JVM具有指令重排的特性,实际的执行顺序可能会是1、3、2,导致多线程情况下出问题,
     * 使用volatile修饰instance变量 可以 避免上述的指令重排
     * tips:不太理解的是 第一个线程在执行第2步之前就已经释放了锁吗?导致其它线程进入synchronized代码块
     *      执行 instance == null 的判断?
     *  回答:第一个线程在执行第2步之前就已经释放了锁吗?(没有)。如果不使用volatile修饰instance变量,那么其他线程进来的时候,看到的instance就有可能不是null的,因为已经执行了第3步,那么此时这个线程(执行 return instance;)使用的instance是一个没有初始化的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;
    }
}

  1. 枚举实现:枚举实现的单例在面对 复杂的序列化及反射攻击时,依然能够保持自己的单例状态,所以被认为是单例的最佳实践。比如,mybatis 在定义 SQL 命令类型时就使用到了枚举。
package org.apache.ibatis.mapping;

/**
 * @author Clinton Begin
 */
public enum SqlCommandType {
  UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;
}

3:JDK中饿汉式例子 Runtime类

/**
 * 每个Java应用程序都有一个单例的Runtime对象,通过getRuntime()方法获得
 * @author  unascribed
 * @see     java.lang.Runtime#getRuntime()
 * @since   JDK1.0
 */
public class Runtime {
    /** 很明显,这里用的是饿汉式 实现单例 */
    private static Runtime currentRuntime = new Runtime();

    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}
}

Spring 的单例 bean 是如何实现的?

Spring 实现单例 bean 是使用 map 注册表和 synchronized 同步机制实现的,通过分析 spring 的 AbstractBeanFactory 中的 doGetBean 方法和 DefaultSingletonBeanRegistry 的 getSingleton()方法,可以理解其实现原理。

单例模式:

1:最好的方式用枚举类型
2:static 修饰也是可以一份的,可以达到单例的效果
3:单例模式
A:饿汉式:(线程安全,但是可能造成空间的浪费,创建了对象木有用)
//私有的构造方法
Private Singleton(){}
//私有的静态属性(如果不是static,在类加载的时候,会出现内存溢出,套娃的感觉)
Private static Singleton single=new Singleton();
//静态方法,提供对象
Public static Singleton getInstance(){
Return single;
}
B:懒汉式(注意线程安全问题)
//私有构造方法
Private Singleton(){}
//私有静态属性
Private static Singleton single;
//共有静态方法(不用创建对象就可以调用)
//这里是防止指令重排序,加入volatile
Public static volatile Singleton getInstance(){
//用到了双重判断,保证线程安全
If(singlenull){
//在这里加锁效率会更高,在方法上加锁,效率会变慢
Synchronized(this/Singleton.class){
If(single
null){
Single=new Singleton();
}
}
}
}

volatile的使用

Volatile是Java虚拟机提供的轻量级同步机制
1 保证可见性
保证可见性:
可见性就是当一个线程 修改一个共享变量时,另外一个线程能读到这个修改的值。
2 不保证原子性
不保证原子性:
什么叫原子性: 不可分割
线程A在执行任务的时候,是不能被打扰的,要么同时成功,要么同时失败
不保证原子性是指:
private volatile static int num=0;
public static void add() {
num++;
}
num++操作不是原子性(++不是原子操作),可能多个线程同时操作
解决方法:我们可以加synchronized
public synchronized static void add() {
num++;
}
3 禁止指令重排
禁止指令重排序
指令重排是指在程序执行过程中,为了提高性能, 编译器和CPU可能会对指令进行重新排序。
1:保证线程可见性
2:防止指令重排序
被volatile修饰的变量的值,将不会被本地线程缓存所以对该变量的读写都是直接操作共享内存,从而保证多个线程能正确的处理改变量。volatile一个线程修改了值,其他线程可以读取的到。
原子性:创建对象jvm为了加快性能,创建对象分为三部分,1:开辟空间 2:加载信息 3:将地址赋值给single 注意2:3:可能会顺序重拍,只有123全部做完,一个完整的对象才能创建成功
在这里插入图片描述

简单工厂

我们对于一类的事物,我们一般定义一个总的接口或者抽象类,来管理这一类事物,优点可以隐藏具体的类,返回值的时候我们直接返回接口类型,这样也可以返回具体不同子类在这里插入图片描述在这里插入图片描述

工厂方法模式

有一个总工厂不干活,里面有一个抽象方法来返回具体工厂,子类工厂是生产工厂的,继承这个总的工厂,让创建工厂时,我们直接new 具体工厂,用总父类工厂来接收,然后子类工厂重写了抽象方法,也就返回了这个具体工厂(牢记,总工厂不干活(抽象),有子工厂)
在这里插入图片描述在这里插入图片描述

Mybatis 中的范例

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();
  }
}


public interface DataSource  extends CommonDataSource, Wrapper {

  Connection getConnection() throws SQLException;

  Connection getConnection(String username, String password)
    throws SQLException;
}

抽象工厂模式

抽象工厂中的总工厂中抽象方法多个,那么就是抽象工厂模式,如果抽象方法变成了一个,那么就是工厂模式,工厂模式和抽象工厂模式都有抽象类,只有简单工厂木有抽象类,,这就是三个工厂的区别在这里插入图片描述

建造者:

MyBatis 的初始化过程使用了建造者模式,抽象类 BaseBuilder 扮演了“建造者接口”的角色,对一些公用方法进行了实现,并定义了公共属性。XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder 等实现类扮演了“具体建造者”的角色,分别用于解析 mybatis-config.xml 配置文件、映射配置文件 以及 SQL 节点。Configuration 和 SqlSessionFactoryBuilder 则分别扮演了“产品” 和 “导演”的角色。即,SqlSessionFactoryBuilder 使用了 BaseBuilder 建造者组件 对复杂对象 Configuration 进行了构建。
BaseBuilder 组件的设计与上面标准的建造者模式是有很大不同的,BaseBuilder 的建造者模式主要是为了将复杂对象 Configuration 的构建过程分解的层次更清晰,将整个构建过程分解到多个“具体构造者”类中,需要这些“具体构造者”共同配合才能完成 Configuration 的构造,单个“具体构造者”不具有单独构造产品的能力,这与 StringBuilder 及 StringBuffer 是不同的。
个人理解的构建者模式 其核心就是用来构建复杂对象的,比如 mybatis 对 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();
  }
}


public class XMLConfigBuilder extends BaseBuilder {

  /** 标识是否已经解析过 mybatis-config.xml 配置文件 */
  private boolean parsed;
  /** 用于解析 mybatis-config.xml 配置文件 */
  private final XPathParser parser;
  /** 标识 <environment> 配置的名称,默认读取 <environment> 标签的 default 属性 */
  private String environment;
  /** 负责创建和缓存 Reflector 对象 */
  private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // 在 mybatis-config.xml 配置文件中查找<configuration>节点,并开始解析
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      // 解析<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);
      // read it after objectFactory and objectWrapperFactory issue #631
      // 解析<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);
    }
  }
}


public class XMLMapperBuilder extends BaseBuilder {

  private final XPathParser parser;
  private final MapperBuilderAssistant builderAssistant;
  private final Map<String, XNode> sqlFragments;
  private final String resource;

  public void parse() {
    // 判断是否已经加载过该映射文件
    if (!configuration.isResourceLoaded(resource)) {
      // 处理<mapper>节点
      configurationElement(parser.evalNode("/mapper"));
      // 将 resource 添加到 Configuration.loadedResources 集合中保存,
      // 它是 HashSet<String> 类型的集合,其中记录了已经加载过的映射文件
      configuration.addLoadedResource(resource);
      // 注册 Mapper 接口
      bindMapperForNamespace();
    }
    // 处理 configurationElement() 方法中解析失败的<resultMap>节点
    parsePendingResultMaps();
    // 处理 configurationElement() 方法中解析失败的<cache-ref>节点
    parsePendingCacheRefs();
    // 处理 configurationElement() 方法中解析失败的 SQL 语句节点
    parsePendingStatements();
  }

  private void configurationElement(XNode context) {
    try {
      // 获取<mapper>节点的 namespace 属性,若 namespace 属性为空,则抛出异常
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      // 设置 MapperBuilderAssistant 的 currentNamespace 字段,记录当前命名空间
      builderAssistant.setCurrentNamespace(namespace);
      // 解析<cache-ref>节点
      cacheRefElement(context.evalNode("cache-ref"));
      // 解析<cache>节点
      cacheElement(context.evalNode("cache"));
      // 解析<parameterMap>节点,(该节点 已废弃,不再推荐使用)
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      // 解析<resultMap>节点
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      // 解析<sql>节点
      sqlElement(context.evalNodes("/mapper/sql"));
      // 解析<select>、<insert>、<update>、<delete>等SQL节点
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }
}


public class XMLStatementBuilder extends BaseBuilder {

  private final MapperBuilderAssistant builderAssistant;
  private final XNode context;
  private final String requiredDatabaseId;

  public void parseStatementNode() {
    // 获取 SQL 节点的 id 以及 databaseId 属性,若其 databaseId属性值与当前使用的数据库不匹配,
    // 则不加载该 SQL 节点;若存在相同 id 且 databaseId 不为空的 SQL 节点,则不再加载该 SQL 节点
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }

    // 根据 SQL 节点的名称决定其 SqlCommandType
    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // 在解析 SQL 语句之前,先处理其中的<include>节点
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);

    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    // 处理<selectKey>节点
    processSelectKeyNodes(id, parameterTypeClass, langDriver);

    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }

    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String resultType = context.getStringAttribute("resultType");
    Class<?> resultTypeClass = resolveClass(resultType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultSetType = context.getStringAttribute("resultSetType");
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    if (resultSetTypeEnum == null) {
      resultSetTypeEnum = configuration.getDefaultResultSetType();
    }
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    String resultSets = context.getStringAttribute("resultSets");

    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }
}


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);
  }
}

原型模式

在这里插入图片描述

Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值