虽然我们都知道有
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 , 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
11.3 代理模式
代理模式
(Proxy Pattern):
给某⼀个对象提供⼀个代理,并由代理对象控制对原对象的引⽤。代理模式
的英⽂叫做
Proxy
,它是⼀种对象结构型模式,代理模式分为静态代理和动态代理,我们来介绍动态代
理
举例:
创建⼀个抽象类,
Person
接⼝,使其拥有⼀个没有返回值的
doSomething
⽅法。
package com.ch.dynamicproxy; /** * 抽象类⼈ */ public interface Person { void doSomething(); }
创建⼀个名为Bob的Person接⼝的实现类,使其实现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 = 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
对象,然后调⽤
T
newInstance(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 );}final MapperMethod mapperMethod = cachedMapperMethod ( method );return mapperMethod . execute ( sqlSession , args );}
⾮常典型的,该
MapperProxy
类实现了
InvocationHandler
接⼝,并且实现了该接⼝的
invoke
⽅法。通
过这种⽅式,我们只需要编写
Mapper.java
接⼝类,当真正执⾏⼀个
Mapper
接⼝的时候,就会转发给
MapperProxy.invoke
⽅法,⽽该⽅法则会调⽤后续的
sqlSession.cud>executor.execute>prepareStatement
等⼀系列⽅法,完成
SQL
的执⾏和返回