MyBatis源码涉及的设计模式

205 阅读4分钟

动态代理设计模式

Mybatis中实现

代理模式可以认为是Mybatis的核心使用的模式,正是由于这个模式,我们只需要编写Mapper.java接 口,不需要实现,由Mybati s后台帮我们完成具体SQL的执行。 当我们使用Configuration的getMapper方法时,会调用mapperRegistry.getMapper方法,而该方法又 会调用mapperProxyFactory.newInstance(sqlSession)来生成一个具体的代理:

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class<T> getMapperInterface() {
        return this.mapperInterface;
    }

    public Map<Method, MapperMethod> getMethodCache() {
        return this.methodCache;
    }
    
    //通过JDK动态代理创建出Dao接口的代理对象
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

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

在这里,先通过T newInstance(SqlSession sqlSession)方法会得到一个MapperProxy对象,然后调用T newInstance(MapperProxy mapperProxy)生成代理对象然后返回。而查看MapperProxy的代码,可 以看到如下内容:

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

    //调用dao接口的方法最终都会执行该方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }

            if (this.isDefaultMethod(method)) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }

        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        return mapperMethod.execute(this.sqlSession, args);
    }
}

非常典型的,该MapperProxy类实现了InvocationHandler接口,并且实现了该接口的invoke方法。通 过这种方式,我们只需要编写Mapper.java接口类,当真正执行一个Mapper接口的时候,就会转发给 MapperProxy.invoke方法,而该方法则会调用后续的sqlSession.cud>executor.execute>prepareStatement 等一系列方法,完成 SQL 的执行和返回

举例

1.创建一个抽象类,Person接口,使其拥有一个没有返回值的doSomething方法。

public interface Person {
    public void doSomething();

}

2.创建一个名为Bob的Person接口的实现类,使其实现doSomething方法

public class Bob implements Person {

    @Override
    public void doSomething() {
        System.out.println("Bob doing Something");
    }
}

3.创建JDK动态代理类,使其实现InvocationHandler接口。拥有一个名为target的变量,并创建 getTa rget获取代理对象方法。

public class JDKDynamicProxy implements InvocationHandler {

    // 声明被代理的对象
    private Person person;

    //构造函数
    public JDKDynamicProxy(Person person) {
        this.person = person;
    }

    //获取代理对象
    public Object getTarget(){
        return Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), this);
    }

    /**
     * @param proxy  代理对象,一般这个参数没怎么用
     * @param method 被代理的方法
     * @param args   被代理方法的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("对原方法进行了前置增强");
        //原方法执行
        Object invoke = method.invoke(person, args);
        System.out.println("对原方法进行了后置增强");
        return invoke;
    }
}

4.创建JDK动态代理测试类J DKDynamicTest

public class proxyTest {
    public static void main(String[] args) {
        System.out.println("不使用代理类,调用doSomething");
        Person person = new Bob();
        person.doSomething();

        System.out.println("--------------------------");

        System.out.println("使用代理类,调用doSomething");
        Person proxy = (Person) new JDKDynamicProxy(new Bob()).getTarget();
        proxy.doSomething();
    }
}

适配器设计模式

MyBatis中实现

StatementHandler接口如下:

public interface StatementHandler {
  //创建数据库操作对象Statement对象
  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;

  void parameterize(Statement statement)
      throws SQLException;

  void batch(Statement statement)
      throws SQLException;

  int update(Statement statement)
      throws SQLException;

  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;

  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;

  BoundSql getBoundSql();

  ParameterHandler getParameterHandler();

}

StatementHandler接口的继承结构如下

image.png

从上面可知StatementHandler接口中的prepare方法是用来创建数据库操作对象Statement或者PreparedStatement或者CallableStatement的,并且StatementHandler接口的实现类BaseStatementHandler中只是简单的实现,并没有真正创建数据库操作对象,如下:

image.png

image.png 由以上可知创建数据库操作对象的instantiateStatement方法在BaseStatementHandler类中是一个抽象方法,其具体的实现在BaseStatementHandler的三个子类中实现,即SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler

查看他们的instantiateStatement方法如下:

image.png

image.png

image.png

举例

注意:以下命名不规范,单纯只是为了能讲清楚适配器设计模式。

1.有一个学生守则接口,如下:

public interface 学生守则接口 {

   //不受实现类欢迎方法
   public void 考试后家长签字();
   //受到实现类欢迎方法
   public void 每周免费看一次电影();
}

2.对于小学生而言,他只想看电影,不想考试后家长签字,怎么办呢?这时候他找到了校门口附近的零食小商贩帮他在试卷上签字,如下:

public abstract class 零食小商贩 implements 学生守则接口{

   @Override
   public void 考试后家长签字() {
      System.out.println("家长阅");
   }
}

3.接着小学生很开心地认了小商贩作为他地干爹,自己每周都可以免费看电影,而不用考试后找家长签字了,如下:

public class 小学生  extends 零食小商贩 {

   @Override
   public void 每周免费看一次电影() {
      System.out.println("看电影");
   }
}

4.测试类如下:

public class TestMain {

   public static void main(String[] args) {     
      学生守则接口  mike = new 小学生();
      //mike.考试后家长签字();
      mike.每周免费看一次电影();
   }
}