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;
}
@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;
}
}
调用
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⽤来⽣产电脑。你只需要传⼊你想⽣ 产的电脑的品牌,它就会实例化相应品牌的电脑对象
```java
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 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,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
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) 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;
}
}
测试 ```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 = 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对象,然后调⽤TnewInstance(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);
}
⾮常典型的,该MapperProxy类实现了InvocationHandler接⼝,并且实现了该接⼝的invoke⽅法。通过这种⽅式,我们只需要编写Mapper.java接⼝类,当真正执⾏⼀个Mapper接⼝的时候,就会转发给MapperProxy.invoke⽅法,⽽该⽅法则会调⽤后续的sqlSession.cud>executor.execute>prepareStatement 等⼀系列⽅法,完成 SQL 的执⾏和返回