Mybatis之设计模式

设计模式

目前java中有三类23中设计模式,但大都停留在概念层面,Mybatis源码中使用了大量的设计模式,观察设计模式在其中的应用,能够更深入的理解设计模式

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

接下来对Builder构建者模式、工厂模式、代理模式进行解读,先介绍模式自身的知识,然后解读在Mybatis中怎样应用了该模式。

1、构建者模式

Builder模式的定义是"将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。”,它属于创建类模式,一般来说,如果一个对象的构建比较复杂,超出了构造函数所能包含的范围,就可以使用工厂模式和Builder模式,相对于工厂模式会产出一个完整的产品,Builder应用于更加 复杂的对象的构建,甚至只会构建产品的一个部分,直白来说,就是使用多个简单的对象一步一步构建成一个复杂的对象。

例子:使用构建者设计模式来生产computer

主要步骤:

1、将需要构建的目标类分成多个部件(电脑可以分为主机、显示器、键盘、音箱等部件);

2、 创建构建类;

3、 依次创建部件;

4、 将部件组装成目标对象

(1)、定义

computer

public class Computer {
    private String displayer;
    private String mainUnit;
    private String mouse;
    private String keyboard;
	
    public String getDisplayer() {
       return displayer;
    }
	public void setDisplayer(String displayer) { 
        this.displayer = displayer;
	}
    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{" + "displayer='" + displayer + '\'' + ", mainUnit='" + mainUnit 		+ '\'' + ", mouse='" + mouse + '\'' + ", keyboard='" + keyboard + '\'' + '}';

}

ComputerBuilder

public static class ComputerBuilder{
	private ComputerBuilder target =new ComputerBuilder();
	public Builder installDisplayer(String displayer){ 
        target.setDisplayer(displayer);
		return this;
	}

    public Builder installMainUnit(String mainUnit){ 
        target.setMainUnit(mainUnit);
        return this;
    }

    public Builder installMouse(String mouse){ 
        target.setMouse(mouse);
        return this;
    }

    public Builder installKeybord(String keyboard){ 
        target.setKeyboard(keyboard);
        return this;
    }

    public ComputerBuilder build(){
        return target;
    }

}

(2)、调用

public static void main(String[] args) {

	ComputerBuilder computerBuilder = new ComputerBuilder();

	computerBuilder. installDisplayer ("显不器");

	computerBuilder. installMainUnit ("主机");

 	computerBuilder.installKeybord("键盘");

	computerBuilder. installMouse ("鼠标");

	Computer computer = computerBuilder.Builder();

	System.out.println(computer);

}

Mybatis中的体现

SqlSessionFactory 的构建过程:

Mybatis的初始化工作非常复杂,不是只用一个构造函数就能搞定的。所以使用了建造者模式,使用了大 量的Builder,进行分层构造,核心对象Configuration使用了 XmlConfigBuilder来进行构造

在Mybatis环境的初始化过程中,SqlSessionFactoryBuilder会调用XMLConfigBuilder读取所有的 MybatisMapConfig .xml 和所有的 *Mapper. xml 文件,构建 Mybatis 运行的核心对象 Configuration 对 象,然后将该Configuration对象作为参数构建一个SqlSessionFactory对象。

其中 XMLConfigBuilder 在构建 Configuration 对象时,也会调用 XMLMapperBuilder 用于读取 *Mapper 文件,而XMLMappe rBuilder会使用XMLStatementBuilder来读取和build所有的SQL语句。

在这个过程中,有一个相似的特点,就是这些Builder会读取文件或者配置,然后做大量的XpathParser 解析、配置或语法的解析、反射生成对象、存入结果缓存等步骤,这么多的工作都不是一个构造函数所 能包括的,因此大量采用了 Builder模式来解决。

SqlSessionFactoryBuilder类根据不同的输入参数来构建SqlSessionFactory这个工厂对象。

2、工厂模式

在Mybatis中比如SqlSessionFactory使用的是工厂模式,该工厂没有那么复杂的逻辑,是一个简单工厂 模式。

简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于创 建型模式。

在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建 其他类的实例,被创建的实例通常都具有共同的父类

例子:生产电脑

假设有一个电脑的代工生产商,它目前已经可以代工生产联想电脑了,随着业务的拓展,这个代工生产 商还要生产惠普的电脑,我们就需要用一个单独的类来专门生产电脑,这就用到了简单工厂模式。下面 我们来实现简单工厂模式:

  1. 创建抽象产品类

我们创建一个电脑的抽象产品类,他有一个抽象方法用于启动电脑:

public abstract class Computer {

    /**
    *产品的抽象方法,由具体的产品类去实现
    */
	public abstract void start();

}
  1. 创建具体产品类

接着我们创建各个品牌的电脑,他们都继承了他们的父类Computer ,并实现了父类的start方法:

public class LenovoComputer extends Computer{ 

    @Override
    public void start() { 
        System.out.println("联想电脑启动”)}
}
public class HpComputer extends Computer{

	@Override public void start() {
		System.out.println("惠普电脑启动”)}
}
  1. 创建工厂类

接下来创建一个工厂类,它提供了一个静态方法createComputer用来生产电脑。你只需要传入你想生 产的电脑的品牌,它就会实例化相应品牌的电脑对象。

public class ComputerFactory {

    public static Computer createComputer(String type){
        Computer mComputer=null;
        switch (type) {

            case "lenovo": 
                mComputer=new LenovoComputer(); 
                break;

            case "hp": 
                mComputer=new HpComputer(); 
                break;
    	}
		return mComputer;
	}
}

客户端调用工厂类

客户端调用工厂类,传入“hp”生产出惠普电脑并调用该电脑对象的start方法:

public class CreatComputer {

    public static void main(String[]args){ 
        ComputerFactory.createComputer("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, 					autoCommit);

        //根据参数创建制定类型的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。

3、代理模式

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

举例:

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

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

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

/**
*创建一个名为Bob的人的实现类
*/
public class Bob implements Person {

	public void doSomething() { 
        System.out.println("Bob doing something!");
	}
}

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

/**
*	JDK动态代理
*	需实现 InvocationHandler 接口
*/
public class JDKDynamicProxy implements InvocationHandler {

    //被代理的对象
    Person target;

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

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

    //动态代理i nvoke方法
    public Person invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //被代理方法前执行
        System.out.println("JDKDynamicProxy do something before!");

        //执行被代理的方法
        Person result = (Person) method.invoke(target, args);

        //被代理方法后执行
        System.out.println("JDKDynamicProxy do something after!"); return result;
	}

}

创建JDK动态代理测试类 JDKDynamicTest

/**
* JDK动态代理测试
*/

public class JDKDynamicTest {

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

        // 调用 doSomething 方法
        person.doSomething();

		System.out.println("分割线");
		System.out.println("使用代理类,调用doSomething方法。”)//获取代理类
        Person proxyPerson = new JDKDynamicProxy(new Bob()).getTarget();

		// 调用 doSomething 方法 proxyPerson.doSomething();
	}
}

Mybatis中实现:

代理模式可以认为是Mybatis的核心使用的模式,正是由于这个模式,我们只需要编写Mapper.java接 口,不需要实现,由 Mybatis后台帮我们完成具体SQL的执行。

当我们使用Configuration的getMapper方法时,会调用mapperRegistry.getMapper()方法,而该方法又 会调用 mapperProxyFactory.newlnstance(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接口类,当真正执行一个Mappe r接口的时候,就会转发绐 MapperPr oxy.invoke方法,而该方法则会调用后续的sqlSession.cud>executor.execute >prepareStatement 等一系列方法,完成 SQL 的执行和返回。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值