11.设计模式

虽然我们都知道有 3 23 种设计模式,但是⼤多停留在概念层⾯, Mybatis 源码中使⽤了⼤量的设计模
 
式,观察设计模式在其中的应⽤,能够更深⼊的理解设计模式
 
Mybati s ⾄少⽤到了以下的设计模式的使⽤
 

 接下来对Builder构建者模式、⼯⼚模式、代理模式进⾏解读,先介绍模式⾃身的知识,然后解读在

Mybatis 中怎样应⽤了该模式。
 

11.1 Builder构建者模式

Builder 模式的定义是 " 将⼀个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表
 
示。 ,它属于创建类模式,⼀般来说,如果⼀个对象的构建⽐较复杂,超出了构造函数所能包含的范
 
围,就可以使⽤⼯⼚模式和 Builder 模式,相对于⼯⼚模式会产出⼀个完整的产品, Builder 应⽤于更加
 
复杂的对象的构建,甚⾄只会构建产品的⼀个部分,直⽩来说,就是使⽤多个简单的对象⼀步⼀步构建
 
成⼀个复杂的对象
 
例⼦:使⽤构建者设计模式来⽣产 computer
主要步骤:
1 、将需要构建的⽬标类分成多个部件(电脑可以分为主机、显示器、键盘、⾳箱等部件);
2 、 创建构建类;
3 、 依次创建部件;
4 、 将部件组装成⽬标对象
1. 定义 computer

 

package com.ch.constructor;

public class Computer {

    //显示器
    private String display;
    //主机
    private String mainUnit;
    //鼠标
    private String mouse;
    //键盘
    private String keyboard;

    public String getDisplay() {
        return display;
    }

    public void setDisplay(String display) {
        this.display = display;
    }

    public String getMainUnit() {
        return mainUnit;
    }

    public void setMainUnit(String mainUnit) {
        this.mainUnit = mainUnit;
    }

    public String getMouse() {
        return mouse;
    }

    public void setMouse(String mouse) {
        this.mouse = mouse;
    }

    public String getKeyboard() {
        return keyboard;
    }

    public void setKeyboard(String keyboard) {
        this.keyboard = keyboard;
    }

    @Override
    public String toString() {
        return "Computer{" +
                "display='" + display + '\'' +
                ", mainUnit='" + mainUnit + '\'' +
                ", mouse='" + mouse + '\'' +
                ", keyboard='" + keyboard + '\'' +
                '}';
    }
}

ComputerBuilder

 

package com.ch.constructor;

public class ComputerBuilder {

    private Computer computer=new Computer();

    public void installDisplay(String display){
          computer.setDisplay(display);
    }
    public void installMainUnit(String mainUnit){
          computer.setMainUnit(mainUnit);
    }
    public void installmouse(String mouse){
        computer.setMouse(mouse);
    }
    public void installKeyBoard(String keyBoard){
        computer.setKeyboard(keyBoard);
    }

    public Computer getComputer(){
        return computer;
    }
}

测试:

package com.ch.constructor;

public class ContructorTest {

    public static void main(String[] args) {

        ComputerBuilder computerBuilder = new ComputerBuilder();
        computerBuilder.installDisplay("显示器");
        computerBuilder.installMainUnit("主机");
        computerBuilder.installmouse("鼠标");
        computerBuilder.installKeyBoard("键盘");
        Computer computer = computerBuilder.getComputer();
        System.out.println(computer);
    }
}

测试结果:

Computer{display='显示器', mainUnit='主机', mouse='鼠标', keyboard='键盘'}

Mybatis 中的体现
SqlSessionFactory 的构建过程:
Mybatis 的初始化⼯作⾮常复杂,不是只⽤⼀个构造函数就能搞定的。所以使⽤了建造者模式,使⽤了
⼤ 量的 Builder ,进⾏分层构造,核⼼对象 Configuration 使⽤了 XmlConfigBuilder 来进⾏构造
 

 

Mybatis 环境的初始化过程中, SqlSessionFactoryBuilder 会调⽤ XMLConfigBuilder 读取所有的
MybatisMapConfig.xml 和所有的 *Mapper.xml ⽂件,构建 Mybatis 运⾏的核⼼对象 Configuration
对 象,然后将该 Configuration 对象作为参数构建⼀个 SqlSessionFactory 对象。
 
private void parseConfiguration ( XNode root ) {
try {
//issue #117 read properties first
// 解析 <properties /> 标签
propertiesElement ( root . evalNode ( "properties" ));
// 解析 <settings /> 标签
Properties settings = settingsAsProperties ( root . evalNode ( "settings" ));
// 加载⾃定义的 VFS 实现类
loadCustomVfs ( settings );
// 解析 <typeAliases /> 标签
typeAliasesElement ( root . evalNode ( "typeAliases" ));
// 解析 <plugins /> 标签
pluginElement ( root . evalNode ( "plugins" ));
// 解析 <objectFactory /> 标签
objectFactoryElement ( root . evaINode ( "obj ectFactory" ));
// 解析 <objectWrapper Factory /> 标签
obj ectWrappe rFacto ryElement ( root . evalNode ( "objectWrapperFactory" ));
// 解析 <reflectorFactory /> 标签
reflectorFactoryElement ( root . evalNode ( "reflectorFactory" ));
// 赋值 <settings /> Configuration 属性
settingsElement ( settings );
// read it after objectFactory and objectWrapperFactory issue #631
// 解析 <environments /> 标签
environmentsElement ( root . evalNode ( "environments" ));
// 解析 <databaseIdProvider /> 标签
databaseldProviderElement ( root . evalNode ( "databaseldProvider" ));
}
 

 其中 XMLConfigBuilder 在构建 Configuration 对象时,也会调⽤ XMLMapperBuilder ⽤于读取

*Mapper ⽂件,⽽ XMLMapperBuilder 会使⽤ XMLStatementBuilder 来读取和 build 所有的 SQL 语句。
 
// 解析 <mappers /> 标签
mapperElement ( root . evalNode ( "mappers" ))
在这个过程中,有⼀个相似的特点,就是这些 Builder 会读取⽂件或者配置,然后做⼤量的 XpathParser
解析、配置或语法的解析、反射⽣成对象、存⼊结果缓存等步骤,这么多的⼯作都不是⼀个构造函数所
能包括的,因此⼤量采⽤了 Builder 模式来解决
 

 

SqlSessionFactoryBuilder 类根据不同的输⼊参数来构建 SqlSessionFactory 这个⼯⼚对象

 

11.2 ⼯⼚模式

Mybatis 中⽐如 SqlSessionFactory 使⽤的是⼯⼚模式,该⼯⼚没有那么复杂的逻辑,是⼀个简单⼯⼚
模式。
简单⼯⼚模式 (Simple Factory Pattern) :⼜称为静态⼯⼚⽅法 (Static Factory Method) 模式,它属于创
建型模式。
在简单⼯⼚模式中,可以根据参数的不同返回不同类的实例。 简单⼯⼚模式专⻔定义⼀个类来负责创建
其他类的实例,被创建的实例通常都具有共同的⽗类
例⼦:⽣产电脑
假设有⼀个电脑的代⼯⽣产商,它⽬前已经可以代⼯⽣产联想电脑了,随着业务的拓展,这个代⼯⽣产
商还要⽣产惠普的电脑,我们就需要⽤⼀个单独的类来专⻔⽣产电脑,这就⽤到了简单⼯⼚模式。
下⾯我们来实现简单⼯⼚模式:
1. 创建抽象产品类
我们创建⼀个电脑的抽象产品类
 
package com.ch.simpleFactory;

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

 

package com.ch.simpleFactory;

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

 package com.ch.simpleFactory;

public class LenovoComputer extends Computer{
    public void start() {
        System.out.println("联想电脑启动");
    }
}
3. 创建⼯⼚类
接下来创建⼀个⼯⼚类,它提供了⼀个静态⽅法 createComputer ⽤来⽣产电脑。你只需要传⼊你
想⽣ 产的电脑的品牌,它就会实例化相应品牌的电脑对象
 
package com.ch.simpleFactory;

public class ComputerFactory {

    public  static Computer createComputer(String type){
        Computer computer=null;
        switch (type){
            case "lenovo":
                computer=new LenovoComputer();
            case "hp":
                computer=new HpComputer();
        }
        return computer;
    }
}

 测试:

package com.ch.simpleFactory;

public class SimpleFactoryTest {
    public static void main(String[] args) {
        Computer hp = ComputerFactory.createComputer("hp");

        hp.start();
    }
}

结果:

惠普电脑启动

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

SqlSessionFactory

可以看到,该 Factory openSession () ⽅法重载了很多个,分别⽀
autoCommit Executor Transaction 等参数的输⼊,来构建核⼼的 SqlSession 对象。
DefaultSqlSessionFactory 的默认⼯⼚实现⾥,有⼀个⽅法可以看出⼯⼚怎么产出⼀个产品 :
 
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 , autoCo
mmit );
// 根据参数创建制定类型的 Executor
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 ();
}
}
这是⼀个 openSession 调⽤的底层⽅法,该⽅法先从 configuration 读取对应的环境配置,然后初始化
TransactionFactory 获得⼀个 Transaction 对象,然后通过 Transaction 获取⼀个 Executor 对象,最
后通过 configuration Executor 、是否 autoCommit 三个参数构建了 SqlSession

11.3 代理模式

代理模式 (Proxy Pattern): 给某⼀个对象提供⼀个代理,并由代理对象控制对原对象的引⽤。代理模式
的英⽂叫做 Proxy ,它是⼀种对象结构型模式,代理模式分为静态代理和动态代理,我们来介绍动态代
举例:
创建⼀个抽象类, Person 接⼝,使其拥有⼀个没有返回值的 doSomething ⽅法。
 
package com.ch.dynamicproxy;

/**
 * 抽象类⼈
 */
public interface Person {
    void doSomething();
}

 创建⼀个名为BobPerson接⼝的实现类,使其实现doSomething⽅法

package com.ch.dynamicproxy;

/**
 * 创建⼀个名为Bob的⼈的实现类
 */
public class Bob implements Person {
    public void doSomething() {
        System.out.println("Bob doing something!");
    } }

 测试:

package com.ch.dynamicproxy;

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

结果:

不使用代理类,调用doSomething
Bob doing something!
-------------------
使用代理类,调用doSomething
对原方法进行前置增强
Bob doing something!
对原方法进行后置增强

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 < Method , MapperMethod > ();
public MapperProxyFactory ( Class < T > mapperInterface ) {
this . mapperInterface = mapperInterface ;
}
public Class < T > getMapperInterface () {
return mapperInterface ;
}
public Map < Method , MapperMethod > 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 < T > ( sqlSession ,
mapperInterface , methodCache );
return newInstance ( mapperProxy );
}
}
在这⾥,先通过 T newInstance(SqlSession sqlSession) ⽅法会得到⼀个 MapperProxy 对象,然后调⽤ T
newInstance(MapperProxy mapperProxy) ⽣成代理对象然后返回。⽽查看 MapperProxy 的代码,可
以看到如下内容:
 
public class MapperProxy < T > implements InvocationHandler , Serializable {
@Override
public Object invoke ( Object proxy , Method method , Object [] args ) throws
Throwable {
try {
if ( Object . class . equals ( method . getDeclaringClass ())) {
return method . invoke ( this , args );
} else if ( isDefaultMethod ( method )) {
return invokeDefaultMethod ( proxy , method , args );
}
} catch ( Throwable t ) {
throw ExceptionUtil . unwrapThrowable ( t );
 
}
final MapperMethod mapperMethod = cachedMapperMethod ( method );
return mapperMethod . execute ( sqlSession , args );
}
⾮常典型的,该 MapperProxy 类实现了 InvocationHandler 接⼝,并且实现了该接⼝的 invoke ⽅法。通
过这种⽅式,我们只需要编写 Mapper.java 接⼝类,当真正执⾏⼀个 Mapper 接⼝的时候,就会转发给
MapperProxy.invoke ⽅法,⽽该⽅法则会调⽤后续的
sqlSession.cud>executor.execute>prepareStatement 等⼀系列⽅法,完成 SQL 的执⾏和返回
 
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值