暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

mybatis-工厂模式

臭虫说 2021-05-09
358

java的工厂模式有 3 种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。属于创建型设计模式

简单工厂模式定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。

优点:

  1. 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。

  2. 客户端无需知道所创建具体产品的类名,只需知道参数即可。

  3. 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。

缺点:

  1. 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。

  2. 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度

  3. 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂

  4. 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。

应用场景

    对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。

简单工厂模式的结构图如下:

    public class Client {
    public static void main(String[] args) {
    }


    //抽象产品
    public interface Product {
    void show();
    }


    //具体产品:ProductA
    static class ConcreteProduct1 implements Product {
    public void show() {
    System.out.println("具体产品1显示...");
    }
    }


    //具体产品:ProductB
    static class ConcreteProduct2 implements Product {
    public void show() {
    System.out.println("具体产品2显示...");
    }
    }


    final class Const {
    static final int PRODUCT_A = 0;
    static final int PRODUCT_B = 1;
    static final int PRODUCT_C = 2;
    }


    static class SimpleFactory {
    public static Product makeProduct(int kind) {
    switch (kind) {
    case Const.PRODUCT_A:
    return new ConcreteProduct1();
    case Const.PRODUCT_B:
    return new ConcreteProduct2();
    }
    return null;
    }
    }
    }
    复制

    工厂方法模式

        是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。

    优点:

    • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。

    • 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。

    • 的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

    缺点:

    • 类的个数容易过多,增加复杂度

    • 增加了系统的抽象性和理解难度

    • 抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。

    应用场景:

    • 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。

    • 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。

    • 客户不关心创建产品的细节,只关心产品的品牌

      package FactoryMethod;


      public class AbstractFactoryTest {
      public static void main(String[] args) {
      try {
      Product a;
      AbstractFactory af;
      af = (AbstractFactory) ReadXML1.getObject();
      a = af.newProduct();
      a.show();
      } catch (Exception e) {
      System.out.println(e.getMessage());
      }
      }
      }


      //抽象产品:提供了产品的接口
      interface Product {
      public void show();
      }


      //具体产品1:实现抽象产品中的抽象方法
      class ConcreteProduct1 implements Product {
      public void show() {
      System.out.println("具体产品1显示...");
      }
      }


      //具体产品2:实现抽象产品中的抽象方法
      class ConcreteProduct2 implements Product {
      public void show() {
      System.out.println("具体产品2显示...");
      }
      }


      //抽象工厂:提供了厂品的生成方法
      interface AbstractFactory {
      public Product newProduct();
      }


      //具体工厂1:实现了厂品的生成方法
      class ConcreteFactory1 implements AbstractFactory {
      public Product newProduct() {
      System.out.println("具体工厂1生成-->具体产品1...");
      return new ConcreteProduct1();
      }
      }


      //具体工厂2:实现了厂品的生成方法
      class ConcreteFactory2 implements AbstractFactory {
      public Product newProduct() {
      System.out.println("具体工厂2生成-->具体产品2...");
      return new ConcreteProduct2();
      }
      }
      复制

      抽象工厂模式是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

      使用抽象工厂模式一般要满足以下条件。

      • 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。

      • 系统一次只可能消费其中某一族产品,即同族的产品一起使用。

      抽象工厂模式的主要角色如下。

      1. 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。

      2. 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。

      3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。

      4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

      模式的应用场景

          抽象工厂模式最早的应用是用于创建属于不同操作系统的视窗构件。如 Java 的 AWT 中的 Button 和 Text 等构件在 Windows 和 UNIX 中的本地实现是不同的。

      抽象工厂模式通常适用于以下场景:

      1. 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。

      2. 统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。

      3. 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

      模式的扩展

      抽象工厂模式的扩展有一定的“开闭原则”倾斜性:

      1. 当增加一个新的产品族时只需增加一个新的具体工厂,不需要修改原代码,满足开闭原则。

      2. 当产品族中需要增加一个新种类的产品时,则所有的工厂类都需要进行修改,不满足开闭原则。

      在myabtis中的实现

          SqlSessionFactory:创建 SqlSession, openSession 通过不同的参数和实现,创建出Sqlsession

        package org.apache.ibatis.session;
        import java.sql.Connection;
        public interface SqlSessionFactory {
        SqlSession openSession();
        SqlSession openSession(boolean autoCommit);
        SqlSession openSession(Connection connection);
        SqlSession openSession(TransactionIsolationLevel level);
        SqlSession openSession(ExecutorType execType);
        SqlSession openSession(ExecutorType execType, boolean autoCommit);
        SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
        SqlSession openSession(ExecutorType execType, Connection connection);
        Configuration getConfiguration();
        }
        复制

            它有两个实现类:DefaultSqlSessionFactory,SqlSessionManager

          DefaultSqlSessionFactory.java中对 openSession进行实现,调用一下方法获取 SqlSession
          private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
          //声明事务
          Transaction tx = null;
          try {
          //加载配置文件中的 数据源环境
          final Environment environment = configuration.getEnvironment();
          //加载配置文件,获取事务工厂
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          //从事务工厂中获取事务
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          //选择执行器
          final Executor executor = configuration.newExecutor(tx, execType);
          //返回一个DefaultSqlSession
          return new DefaultSqlSession(configuration, executor, autoCommit);
          } catch (Exception e) {
          closeTransaction(tx); // may have fetched a connection so lets call close()
          throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
          } finally {
          ErrorContext.instance().reset();
          }
          }


          private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
          try {
          boolean autoCommit;
          try {
          //获取是否自动提交
          autoCommit = connection.getAutoCommit();
          } catch (SQLException e) {
          // Failover to true, as most poor drivers
          // or databases won't support transactions
          // 有异常就设置自动提交事务
          autoCommit = true;
          }
          //获取事务
          //1.加载数据源环境
          final Environment environment = configuration.getEnvironment();
          //2.加载配置文件,获取事务工厂
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          //3.获取事务
          final Transaction tx = transactionFactory.newTransaction(connection);
          //获取sql执行器
          final Executor executor = configuration.newExecutor(tx, execType);
          //返回一个默认的会话DefaultSqlSession
          return new DefaultSqlSession(configuration, executor, autoCommit);
          } catch (Exception e) {
          //没有关闭事务
          throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
          } finally {
          ErrorContext.instance().reset();
          }
          }
          复制
            SqlSessionManager实现类对 DefaultSqlSessionFactory 进行了jdk的动态代理
            private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
            this.sqlSessionFactory = sqlSessionFactory;
            this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
            SqlSessionFactory.class.getClassLoader(),
            new Class[]{SqlSession.class},
            new SqlSessionInterceptor());
            }
            复制

            MapperProxyFactory

                为了生成MapperProxy

              package org.apache.ibatis.binding;
              import java.lang.reflect.Method;
              import java.lang.reflect.Proxy;
              import java.util.Map;
              import java.util.concurrent.ConcurrentHashMap;
              import org.apache.ibatis.binding.MapperProxy.MapperMethodInvoker;
              import org.apache.ibatis.session.SqlSession;
              public class MapperProxyFactory<T> {
              //mapper接口
              private final Class<T> mapperInterface;
              private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
              //下面是一些构造函数和 get/set方法
              public MapperProxyFactory(Class<T> mapperInterface) {
              this.mapperInterface = mapperInterface;
              }
              public Class<T> getMapperInterface() {
              return mapperInterface;
              }
              public Map<Method, MapperMethodInvoker> getMethodCache() {
              return methodCache;
              }

              @SuppressWarnings("unchecked")
              protected T newInstance(MapperProxy<T> mapperProxy) {
              return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
              }


              public T newInstance(SqlSession sqlSession) {
              final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
              return newInstance(mapperProxy);
              }
              }
              复制

              MapperProxy

                //实现了 InvocationHandler 接口 标准的jdk动态代理
                public class MapperProxy<T> implements InvocationHandler, Serializable {


                private static final long serialVersionUID = -6424540398559729838L;
                private final 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;
                }
                //如果不是就把方法Method包装成MapperMethod,我们前面已经提到了MapperMethod
                //主要就是处理方法的注解,参数,返回值,参数与SQL语句中的参数的对应关系
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (Object.class.equals(method.getDeclaringClass())) {
                try {
                return method.invoke(this, args);
                } catch (Throwable t) {
                throw ExceptionUtil.unwrapThrowable(t);
                }
                }
                final MapperMethod mapperMethod = cachedMapperMethod(method);
                return mapperMethod.execute(sqlSession, args);
                }


                private MapperMethod cachedMapperMethod(Method method) {
                MapperMethod mapperMethod = methodCache.get(method);
                if (mapperMethod == null) {
                mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
                methodCache.put(method, mapperMethod);
                }
                return mapperMethod;
                }
                }
                复制

                    MyBatis添加Mapper的操作实际上添加的是MapperProxyFactory,这个是MapperProxy的工厂类,但是MapperProxyFactory生产的也不是MapperProxy,而是Mapper的Proxy代理。使用的InvocationHandler是MapperProxy,MapperProxy的invoke方法实际上是把Mapper接口方法包装为了MapperMethod,并执行的MapperMethod的execute方法。MapperMethod处理的逻辑是Mapper方法与xml中的SQL的一些映射关系。例如@MapKey等注解的处理,一些如RowBounds特殊参数的处理以及一些其他关于Mapper方法与SQL映射的处理。执行SQL的逻辑其实是委托给了SqlSession相关的逻辑

                ObjectFactory

                    mybatis中的ObjectFactory每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。可以说这个对象工厂就是用来创建实体类的,MyBatis有一个DefaultObjectFactory默认对象工厂类,就像上面所说的默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。它不做其他任何的处理。如果我们想在创建实例化一个目标类的时候做点啥其他的动作,可以继承DefaultObjectFactory,覆写父类方法,并在mybatis-config.xml中注册配置这个对象工厂类。


                文章转载自臭虫说,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                评论