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.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;}@Overridepublic 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;}}
调用
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);}
1.2 Mybatis中构建者模式的体现
SqlSessionFactory 的构建过程:
Mybatis的初始化⼯作⾮常复杂,不是只⽤⼀个构造函数就能搞定的。所以使⽤了建造者模式,使⽤了
⼤ 量的Builder,进⾏分层构造,核⼼对象Configuration使⽤了 XmlConfigBuilder来进⾏构造。
- 在Mybatis环境的初始化过程中,SqlSessionFactoryBuilder会调⽤XMLConfigBuilder读取所有的MybatisMapConfig.xml 和所有的 *Mapper.xml ⽂件,构建 Mybatis 运⾏的核⼼对象 Configuration对 象,然后将该Configuration对象作为参数构建⼀个SqlSessionFactory对象。
- 其中 XMLConfigBuilder 在构建 Configuration 对象时,也会调⽤ XMLMapperBuilder ⽤于读取*Mapper ⽂件,⽽XMLMapperBuilder会使⽤XMLStatementBuilder来读取和build所有的SQL语句。
- 在这个过程中,有⼀个相似的特点,就是这些Builder会读取⽂件或者配置,然后做⼤量的XpathParser解析、配置或语法的解析、反射⽣成对象、存⼊结果缓存等步骤,这么多的⼯作都不是⼀个构造函数所能包括的,因此⼤量采⽤了 Builder模式来解决
- SqlSessionFactoryBuilder类根据不同的输⼊参数来构建SqlSessionFactory这个⼯⼚对象
2. 工厂模式
在Mybatis中⽐如SqlSessionFactory使⽤的是⼯⼚模式,该⼯⼚没有那么复杂的逻辑,是⼀个简单⼯⼚
模式。
简单⼯⼚模式(Simple Factory Pattern):⼜称为静态⼯⼚⽅法(Static Factory Method)模式,它属于创
建型模式。
在简单⼯⼚模式中,可以根据参数的不同返回不同类的实例。简单⼯⼚模式专⻔定义⼀个类来负责创建
其他类的实例,被创建的实例通常都具有共同的⽗类
2.1 例子
假设有⼀个电脑的代⼯⽣产商,它⽬前已经可以代⼯⽣产联想电脑了,随着业务的拓展,这个代⼯⽣产
商还要⽣产惠普的电脑,我们就需要⽤⼀个单独的类来专⻔⽣产电脑,这就⽤到了简单⼯⼚模式。
创建抽象产品类:我们创建⼀个电脑的抽象产品类,他有⼀个抽象⽅法⽤于启动电脑:
public abstract class Computer {/***产品的抽象⽅法,由具体的产品类去实现*/public abstract void start();}
创建具体产品类:接着我们创建各个品牌的电脑,他们都继承了他们的⽗类Computer,并实现了⽗类的start⽅法: ```java public class LenovoComputer extends Computer{ @Override public void start() {
System.out.println("联想电脑启动");
} }
public class HpComputer extends Computer{ @Override public void start() { System.out.println(“惠普电脑启动”); } }
3. 创建⼯⼚类:接下来创建⼀个⼯⼚类,它提供了⼀个静态⽅法createComputer⽤来⽣产电脑。你只需要传⼊你想⽣ 产的电脑的品牌,它就会实例化相应品牌的电脑对象```javapublic 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 static void main(String[]args){ComputerFactory.createComputer("hp").start();}
2.2 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);//根据参数创建制定类型的Executorfinal Executor executor=configuration.newExecutor(tx,execType);//返回的是 DefaultSqlSessionreturn new DefaultSqlSession(configuration,executor,autoCommit);}catch(Exception e){closeTransaction(tx); // may have fetched a connection so lets callclose()throw ExceptionFactory.wrapException("Error opening session. Cause: "+e,e);}finally{ErrorContext.instance().reset();}}
这是⼀个openSession调⽤的底层⽅法,该⽅法先从configuration读取对应的环境配置,然后初始化TransactionFactory 获得⼀个 Transaction 对象,然后通过 Transaction 获取⼀个 Executor 对象,最后通过configuration、Executor、是否autoCommit三个参数构建了 SqlSession
3. 代理模式
代理模式(Proxy Pattern):给某⼀个对象提供⼀个代理,并由代理对象控制对原对象的引⽤。代理模式
的英⽂叫做Proxy,它是⼀种对象结构型模式,代理模式分为静态代理和动态代理,我们来介绍动态代
理3.1 例子
创建⼀个抽象类,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!");}}
创建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.newProxylnstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), this);}//动态代理invoke⽅法public Person invoke(Object proxy, Method method, Object[] args) throwsThrowable {//被代理⽅法前执⾏System.out.println("JDKDynamicProxy do something before!");//执⾏被代理的⽅法Person result = (Person) method.invoke(target, args);//被代理⽅法后执⾏System.out.println("JDKDynamicProxy do something after!");return result;}}
测试 ```java /**
- 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();
3.2 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 = newConcurrentHashMap<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(), newClass[] { 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对象,然后调⽤TnewInstance(MapperProxy mapperProxy)⽣成代理对象然后返回。⽽查看MapperProxy的代码,可以看到如下内容:
public class MapperProxy<T> implements InvocationHandler, Serializable {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throwsThrowable {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);}
⾮常典型的,该MapperProxy类实现了InvocationHandler接⼝,并且实现了该接⼝的invoke⽅法。通过这种⽅式,我们只需要编写Mapper.java接⼝类,当真正执⾏⼀个Mapper接⼝的时候,就会转发给MapperProxy.invoke⽅法,⽽该⽅法则会调⽤后续的sqlSession.cud>executor.execute>prepareStatement 等⼀系列⽅法,完成 SQL 的执⾏和返回
