mybatis使用到的设计模式:

模式 mybatis体现
Builder模式 例如SqlSessionFactoryBuilder、Environment
工厂方法模式 例如SqlSessionFactory、TransactionFactory、LogFactory
单例模式 例如 ErrorContext 和 LogFactory
代理模式 Mybatis实现的核⼼,⽐如MapperProxy、ConnectionLogger,⽤的jdk的动态代理
还有executor.loader包使⽤了 cglib或者javassist达到延迟加载的效果
组合模式 例如SqlNode和各个⼦类ChooseSqlNode等
模板方法模式 例如 BaseExecutor 和 SimpleExecutor,还有 BaseTypeHandler 和所有的⼦类例如
IntegerTypeHandler
适配器模式 例如Log的Mybatis接⼝和它对jdbc、log4j等各种⽇志框架的适配实现
装饰者模式 例如Cache包中的cache.decorators⼦包中等各个装饰者的实现
迭代器模式 例如迭代器模式PropertyTokenizer

1. Builder构建者模式

Builder模式的定义是:“将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以构建不同的表示”。直白来说,就是使用多个简单的对象一步一步构建成一个复杂的对象。
例子:使用构建者模式来生产computer
主要步骤:

  1. 将需要构建的目标类分成多个部件(电脑可以分为主机、显示器、键盘、鼠标等)
  2. 创建构建类
  3. 依次创建部件
  4. 将部件组成目标对象

    1.1 例子

  5. 定义computer

    1. public class Computer {
    2. // 显示器
    3. private String displayer;
    4. // 主机
    5. private String mainUnit;
    6. // 鼠标
    7. private String mouse;
    8. // 键盘
    9. private String keyboard;
    10. public String getDisplayer() {
    11. return displayer;
    12. }
    13. public void setDisplayer(String displayer) {
    14. this.displayer = displayer;
    15. }
    16. public String getMainUnit() {
    17. return mainUnit;
    18. }
    19. public void setMainUnit(String mainUnit) {
    20. this.mainUnit = mainUnit;
    21. }
    22. public String getMouse() {
    23. return mouse;
    24. }
    25. public void setMouse(String mouse) {
    26. this.mouse = mouse;
    27. }
    28. public String getKeyboard() {
    29. return keyboard;
    30. }
    31. public void setKeyboard(String keyboard) {
    32. this.keyboard = keyboard;
    33. }
    34. @Override
    35. public String toString() {
    36. return "Computer{" + "displayer='" + displayer + '\'' + ", mainUnit='"
    37. + mainUnit + '\'' + ", mouse='" + mouse + '\'' + ", keyboard='" + keyboard +
    38. '\'' + '}';
    39. }
  6. 定义computerBuilder

    1. public static class ComputerBuilder {
    2. private ComputerBuilder target = new ComputerBuilder();
    3. public Builder installDisplayer(String displayer) {
    4. target.setDisplayer(displayer);
    5. return this;
    6. }
    7. public Builder installMainUnit(String mainUnit) {
    8. target.setMainUnit(mainUnit);
    9. return this;
    10. }
    11. public Builder installMouse(String mouse) {
    12. target.setMouse(mouse);
    13. return this;
    14. }
    15. public Builder installKeybord(String keyboard) {
    16. target.setKeyboard(keyboard);
    17. return this;
    18. }
    19. public ComputerBuilder build() {
    20. return target;
    21. }
    22. }
  7. 调用

    1. public static void main(String[]args){
    2. ComputerBuilder computerBuilder=new ComputerBuilder();
    3. computerBuilder.installDisplayer("显万器");
    4. computerBuilder.installMainUnit("主机");
    5. computerBuilder.installKeybord("键盘");
    6. computerBuilder.installMouse("⿏标");
    7. Computer computer=computerBuilder.Builder();
    8. System.out.println(computer);
    9. }

    1.2 Mybatis中构建者模式的体现

    SqlSessionFactory 的构建过程:

  8. Mybatis的初始化⼯作⾮常复杂,不是只⽤⼀个构造函数就能搞定的。所以使⽤了建造者模式,使⽤了

⼤ 量的Builder,进⾏分层构造,核⼼对象Configuration使⽤了 XmlConfigBuilder来进⾏构造。

  1. 在Mybatis环境的初始化过程中,SqlSessionFactoryBuilder会调⽤XMLConfigBuilder读取所有的MybatisMapConfig.xml 和所有的 *Mapper.xml ⽂件,构建 Mybatis 运⾏的核⼼对象 Configuration对 象,然后将该Configuration对象作为参数构建⼀个SqlSessionFactory对象。
  2. 其中 XMLConfigBuilder 在构建 Configuration 对象时,也会调⽤ XMLMapperBuilder ⽤于读取*Mapper ⽂件,⽽XMLMapperBuilder会使⽤XMLStatementBuilder来读取和build所有的SQL语句。
  3. 在这个过程中,有⼀个相似的特点,就是这些Builder会读取⽂件或者配置,然后做⼤量的XpathParser解析、配置或语法的解析、反射⽣成对象、存⼊结果缓存等步骤,这么多的⼯作都不是⼀个构造函数所能包括的,因此⼤量采⽤了 Builder模式来解决
  4. SqlSessionFactoryBuilder类根据不同的输⼊参数来构建SqlSessionFactory这个⼯⼚对象

2. 工厂模式

在Mybatis中⽐如SqlSessionFactory使⽤的是⼯⼚模式,该⼯⼚没有那么复杂的逻辑,是⼀个简单⼯⼚
模式。
简单⼯⼚模式(Simple Factory Pattern):⼜称为静态⼯⼚⽅法(Static Factory Method)模式,它属于创
建型模式。
在简单⼯⼚模式中,可以根据参数的不同返回不同类的实例。简单⼯⼚模式专⻔定义⼀个类来负责创建
其他类的实例,被创建的实例通常都具有共同的⽗类

2.1 例子

假设有⼀个电脑的代⼯⽣产商,它⽬前已经可以代⼯⽣产联想电脑了,随着业务的拓展,这个代⼯⽣产
商还要⽣产惠普的电脑,我们就需要⽤⼀个单独的类来专⻔⽣产电脑,这就⽤到了简单⼯⼚模式。

  1. 创建抽象产品类:我们创建⼀个电脑的抽象产品类,他有⼀个抽象⽅法⽤于启动电脑:

    1. public abstract class Computer {
    2. /**
    3. *
    4. 产品的抽象⽅法,由具体的产品类去实现
    5. */
    6. public abstract void start();
    7. }
  2. 创建具体产品类:接着我们创建各个品牌的电脑,他们都继承了他们的⽗类Computer,并实现了⽗类的start⽅法: ```java public class LenovoComputer extends Computer{ @Override public void start() {

    1. System.out.println("联想电脑启动");

    } }

public class HpComputer extends Computer{ @Override public void start() { System.out.println(“惠普电脑启动”); } }

  1. 3. 创建⼯⼚类:接下来创建⼀个⼯⼚类,它提供了⼀个静态⽅法createComputer⽤来⽣产电脑。你只需要传⼊你想⽣ 产的电脑的品牌,它就会实例化相应品牌的电脑对象
  2. ```java
  3. public class ComputerFactory {
  4. public static Computer createComputer(String type){
  5. Computer mComputer=null;
  6. switch (type) {
  7. case "lenovo":
  8. mComputer=new LenovoComputer();
  9. break;
  10. case "hp":
  11. mComputer=new HpComputer();
  12. break;
  13. }
  14. return mComputer;
  15. }
  16. }
  1. 客户端调⽤⼯⼚类:客户端调⽤⼯⼚类,传⼊“hp”⽣产出惠普电脑并调⽤该电脑对象的start⽅法:

    1. public static void main(String[]args){
    2. ComputerFactory.createComputer("hp").start();
    3. }

    2.2 mybatis中工厂模式的体现

    Mybatis中执⾏Sql语句、获取Mappers、管理事务的核⼼接⼝SqlSession的创建过程使⽤到了⼯⼚模
    式。有⼀个 SqlSessionFactory 来负责 SqlSession 的创建

  2. SqlSessionFactory:可以看到,该Factory的openSession ()⽅法重载了很多个,分别⽀持autoCommit、Executor、Transaction等参数的输⼊,来构建核⼼的SqlSession对象。

  3. 在DefaultSqlSessionFactory的默认⼯⼚实现⾥,有⼀个⽅法可以看出⼯⼚怎么产出⼀个产品:

    1. private SqlSession openSessionFromDataSource(ExecutorType execType,
    2. TransactionIsolationLevel level,boolean autoCommit){
    3. Transaction tx=null;
    4. try{
    5. final Environment environment=configuration.getEnvironment();
    6. final TransactionFactory transactionFactory=
    7. getTransactionFactoryFromEnvironment(environment);
    8. tx=transactionFactory.newTransaction(environment.getDataSource(),level,autoCo
    9. mmit);
    10. //根据参数创建制定类型的Executor
    11. final Executor executor=configuration.newExecutor(tx,execType);
    12. //返回的是 DefaultSqlSession
    13. return new DefaultSqlSession(configuration,executor,autoCommit);
    14. }catch(Exception e){
    15. closeTransaction(tx); // may have fetched a connection so lets call
    16. close()
    17. throw ExceptionFactory.wrapException("Error opening session. Cause: "+
    18. e,e);
    19. }finally{
    20. ErrorContext.instance().reset();
    21. }
    22. }
  4. 这是⼀个openSession调⽤的底层⽅法,该⽅法先从configuration读取对应的环境配置,然后初始化TransactionFactory 获得⼀个 Transaction 对象,然后通过 Transaction 获取⼀个 Executor 对象,最后通过configuration、Executor、是否autoCommit三个参数构建了 SqlSession

    3. 代理模式

    代理模式(Proxy Pattern):给某⼀个对象提供⼀个代理,并由代理对象控制对原对象的引⽤。代理模式
    的英⽂叫做Proxy,它是⼀种对象结构型模式,代理模式分为静态代理和动态代理,我们来介绍动态代

    3.1 例子

  5. 创建⼀个抽象类,Person接⼝,使其拥有⼀个没有返回值的doSomething⽅法。

    1. /**
    2. *
    3. 抽象类⼈
    4. */
    5. public interface Person {
    6. void doSomething();
    7. }
  6. 创建⼀个名为Bob的Person接⼝的实现类,使其实现doSomething⽅法

    1. /**
    2. *
    3. 创建⼀个名为Bob的⼈的实现类
    4. */
    5. public class Bob implements Person {
    6. public void doSomething() {
    7. System.out.println("Bob doing something!");
    8. }
    9. }
  7. 创建JDK动态代理类,使其实现InvocationHandler接⼝。拥有⼀个名为target的变量,并创建getTarget获取代理对象⽅法

    1. /**
    2. *
    3. JDK动态代理
    4. *
    5. 需实现 InvocationHandler 接⼝ */
    6. public class JDKDynamicProxy implements InvocationHandler {
    7. //被代理的对象
    8. Person target;
    9. // JDKDynamicProxy 构造函数
    10. public JDKDynamicProxy(Person person) {
    11. this.target = person;
    12. }
    13. //获取代理对象
    14. public Person getTarget() {
    15. return (Person)Proxy.newProxylnstance(target.getClass().getClassLoader(),
    16. target.getClass().getInterfaces(), this);
    17. }
    18. //动态代理invoke⽅法
    19. public Person invoke(Object proxy, Method method, Object[] args) throws
    20. Throwable {
    21. //被代理⽅法前执⾏
    22. System.out.println("JDKDynamicProxy do something before!");
    23. //执⾏被代理的⽅法
    24. Person result = (Person) method.invoke(target, args);
    25. //被代理⽅法后执⾏
    26. System.out.println("JDKDynamicProxy do something after!");
    27. return result;
    28. }
    29. }
  8. 测试 ```java /**

  • JDK动态代理测试 */ public class JDKDynamicTest { public static void main(String[] args) {
    1. System.out.println("不使⽤代理类,调⽤doSomething⽅法。");
    2. //不使⽤代理类
    3. Person person = new Bob();
    4. // 调⽤ doSomething ⽅法
    5. person.doSomething();
    6. System.out.println("分割线-----------");
    7. System.out.println("使⽤代理类,调⽤doSomething⽅法。");
    8. //获取代理类
    9. Person proxyPerson = new JDKDynamicProxy(new Bob()).getTarget();
    10. // 调⽤ doSomething ⽅法 proxyPerson.doSomething();
    } } ```

    3.2 mybatis中代理模式的体现

    代理模式可以认为是Mybatis的核⼼使⽤的模式,正是由于这个模式,我们只需要编写Mapper.java接
    ⼝,不需要实现,由Mybati s后台帮我们完成具体SQL的执⾏。
  1. 当我们使⽤Configuration的getMapper⽅法时,会调⽤mapperRegistry.getMapper⽅法,⽽该⽅法⼜会调⽤ mapperProxyFactory.newInstance(sqlSession)来⽣成⼀个具体的代理:

    1. public class MapperProxyFactory<T> {
    2. private final Class<T> mapperInterface;
    3. private final Map<Method, MapperMethod> methodCache = new
    4. ConcurrentHashMap<Method, MapperMethod>();
    5. public MapperProxyFactory(Class<T> mapperInterface) {
    6. this.mapperInterface = mapperInterface;
    7. }
    8. public Class<T> getMapperInterface() {
    9. return mapperInterface;
    10. }
    11. public Map<Method, MapperMethod> getMethodCache() {
    12. return methodCache;
    13. @SuppressWarnings("unchecked")
    14. protected T newInstance(MapperProxy<T> mapperProxy) {
    15. return (T)
    16. Proxy.newProxyInstance(mapperInterface.getClassLoader(), new
    17. Class[] { mapperInterface },
    18. mapperProxy);
    19. }
    20. public T newInstance(SqlSession sqlSession) {
    21. final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession,
    22. mapperInterface, methodCache);
    23. return newInstance(mapperProxy);
    24. }
    25. }
  2. 在这⾥,先通过T newInstance(SqlSession sqlSession)⽅法会得到⼀个MapperProxy对象,然后调⽤TnewInstance(MapperProxy mapperProxy)⽣成代理对象然后返回。⽽查看MapperProxy的代码,可以看到如下内容:

    1. public class MapperProxy<T> implements InvocationHandler, Serializable {
    2. @Override
    3. public Object invoke(Object proxy, Method method, Object[] args) throws
    4. Throwable {
    5. try {
    6. if (Object.class.equals(method.getDeclaringClass())) {
    7. return method.invoke(this, args);
    8. } else if (isDefaultMethod(method)) {
    9. return invokeDefaultMethod(proxy, method, args);
    10. }
    11. } catch (Throwable t) {
    12. throw ExceptionUtil.unwrapThrowable(t);
    13. }
  3. ⾮常典型的,该MapperProxy类实现了InvocationHandler接⼝,并且实现了该接⼝的invoke⽅法。通过这种⽅式,我们只需要编写Mapper.java接⼝类,当真正执⾏⼀个Mapper接⼝的时候,就会转发给MapperProxy.invoke⽅法,⽽该⽅法则会调⽤后续的sqlSession.cud>executor.execute>prepareStatement 等⼀系列⽅法,完成 SQL 的执⾏和返回