我们都知道3类23中设计模式,但是大多数停留在概念上面,很抽象,不知道我们写代码过程中哪些地方运行了哪些设计模式,今天我们来看看MyBatis中运用了哪些设计模式
先看看大纲总体
模式 | 表现 |
Builder模式 | 例如 |
工厂方法模式 | |
单例模式 | 例如 |
代理工厂模式 | MyBatis实现的核心,例如 |
组合模式 | 例如 |
模板方法模式 | 例如 |
适配器模式 | 例如Log的MyBatis接口和它对jdbc、log4等各种日志框架的适配实现 |
装饰者模式 | 例如Cache包中的 cache.decorators 子包中等各个装饰者的实现 |
迭代器模式 | 例如 |
1、Builder构建者模式
SqlSessionFactory
的构建过程就是构建者模式
我们先看看我们是如何创建SqlSessionFactory
public class MybatisUtils {
public static SqlSessionFactory sqlSessionFactory;
static {
try {
//获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
//读取文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//构建sqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
点击进入SqlSessionFactoryBuilder
的源码
在初始化过程中,SqlSessionFactoryBuilder
会调用XMLConfigBuilder
读取所有的MyBatisConfig.xml
配置文件和*Mapper.xml
,构建MyBatis
运行的核心对象Configuration
,然后作为参数调用 parse()方法 构建SqlSessionFactory
对象。
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
SqlSessionFactory var5;
try {
//调用XMLConfigBuilder读取所有的配置文件和*Mapper.xml
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException var13) {
}
}
return var5;
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
接着点击进入XMLConfigBuilder
的源码的parse()方法
public Configuration parse() {
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
private void parseConfiguration(XNode root) {
try {
//解析 <properties>标签,后面都是类似解析标签,就不一一注释了
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.loadCustomLogImpl(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
在构建Configuration
对象的时候,重点关注解析mapper文件
this.mapperElement(root.evalNode("mappers"));
深入点击源码一步一步探索,发现会调用XMLMapperBuilder
用于读取*Mapper.xml文件
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(true) {
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String resource;
if ("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
this.configuration.addMappers(resource);
} else {
resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
XMLMapperBuilder mapperParser;
InputStream inputStream;
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
inputStream = Resources.getResourceAsStream(resource);
//读取文件
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
inputStream = Resources.getUrlAsStream(url);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} else {
if (resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
再进一步探索,点击进入XMLMapperBuilder
,发现会使用XMLStatementBuilder
来读取和build所有的sql语句
在这一个过程中,有一个相似的特点就是:在这些Builder读取配置文件和*Mapper.xml文件时,做了大量的XPathParser
解析、配置、语法的解析、反射生成对象、存入结果缓存等步骤,内部采用了大量的Builder模式来解决
2、工厂模式
MyBatis中执行sql语句、获取Mappers、管理事务的核心接口SqlSession的创建过程使用到了工厂模式
SqlSessionFactory
可以看到,DefaultSqlSessionFactory
的openSession
方法有很多个,分别支持autoCommit
、ExecutorType
、TransactionIsolationLevel
等参数的传入,来构建核心SqlSession
,在该类中,有一个方法可以看出工厂如何产出一个产品的
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
//读取环境变量
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//根据参数创建Executor
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
//返回DefaultSqlSession
return var8;
}
这是一个openSession的底层方法,该方法先从configuration
中读取环境配置,然后初始化一个TransactionFactory
获得一个
transactionFactory对象,通过Transaction
的对象tx和ExecutorType
对象获得一个Executor对象,最后通过
configuration, executor, autoCommit三个参数构建一个SqlSession
3、代理模式
代理模式可以认为是 MyBatis 的核心使用模式,由于这个模式,我们只需要写Mapper.java接口,不需要具体实现,MyBatis 后台帮我们完成具体SQLd的执行
当使用Configuration的getMapper()方法时,会调用mapperRegistry.getMapper()方法,而该方法又会调用mapperProxyFactory.newInstance生成具体的代理对象
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
public MapperRegistry(Configuration config) {
this.config = config;
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
......
}
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return this.methodCache;
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}
在这里,先通过T newInstance(SqlSession sqlSession)方法获得一个MapperProxy对象。然后生成代理对象返回
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
if (method.isDefault()) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
}
这个类实现了InvocationHandler
接口,实现了invoke方法,通过这种方式,我们只需要编写Mapper.java接口类,当真正执行一个Mapper的时候,就会转发给MapperProxy.invoke方法,而该方法则会调用后续的一系列方法,完成SQL的执行和返回。
其他设计模式大家可以自己去探索发现,也能提高自己源码理解能力哦!