- 工厂模式
工厂模式应用的 常见类 SqlSessionFactory MyByatis 中通过SqlSession 来执行SQL命令 获取映射器示列和管理事务 而SqlSessionFactory就是用来创建SqlSession类对象的
比如在SqlSessionFactory 接口中的 openSession() 方法 他调用的是
DefaultSqlSessionFactory中的openSessionFromDataSource 方法,代码如下
在代码中有方法 Executor executor = configuration.newExecutor(tx, execType); 就是一个标准的工厂模式,它根据传入的type类型不同,返回不同的 executor
- 建造者模式(Builder)
在Mybatis中建造者模式最直观的应用是SqlSessionFactoryBuilder对象
获取SqlSessionFactory对象的方法
下面展示一些 内联代码片
。
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
build(parser.parse()) 方法源码如下
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
var5 = this.build(parser.parse());
// 其中parser.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 {
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);
}
}
- 单列模式
只有一个单纯的对象
public class ErrorContext {
private static final String LINE_SEPARATOR = System.lineSeparator();
// 每个线程变量存储的容器 ThreadLocal
private static final ThreadLocal<ErrorContext> LOCAL = ThreadLocal.withInitial(ErrorContext::new);
private ErrorContext stored;
private String resource;
private String activity;
private String object;
private String message;
private String sql;
private Throwable cause;
private ErrorContext() {
}
public static ErrorContext instance() {
return (ErrorContext)LOCAL.get();
}
public ErrorContext store() {
ErrorContext newContext = new ErrorContext();
newContext.stored = this;
LOCAL.set(newContext);
return (ErrorContext)LOCAL.get();
}
public ErrorContext recall() {
if (this.stored != null) {
LOCAL.set(this.stored);
this.stored = null;
}
return (ErrorContext)LOCAL.get();
}
public ErrorContext resource(String resource) {
this.resource = resource;
return this;
}
public ErrorContext activity(String activity) {
this.activity = activity;
return this;
}
public ErrorContext object(String object) {
this.object = object;
return this;
}
public ErrorContext message(String message) {
this.message = message;
return this;
}
public ErrorContext sql(String sql) {
this.sql = sql;
return this;
}
public ErrorContext cause(Throwable cause) {
this.cause = cause;
return this;
}
public ErrorContext reset() {
this.resource = null;
this.activity = null;
this.object = null;
this.message = null;
this.sql = null;
this.cause = null;
LOCAL.remove();
return this;
}
public String toString() {
StringBuilder description = new StringBuilder();
if (this.message != null) {
description.append(LINE_SEPARATOR);
description.append("### ");
description.append(this.message);
}
if (this.resource != null) {
description.append(LINE_SEPARATOR);
description.append("### The error may exist in ");
description.append(this.resource);
}
if (this.object != null) {
description.append(LINE_SEPARATOR);
description.append("### The error may involve ");
description.append(this.object);
}
if (this.activity != null) {
description.append(LINE_SEPARATOR);
description.append("### The error occurred while ");
description.append(this.activity);
}
if (this.sql != null) {
description.append(LINE_SEPARATOR);
description.append("### SQL: ");
description.append(this.sql.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ').trim());
}
if (this.cause != null) {
description.append(LINE_SEPARATOR);
description.append("### Cause: ");
description.append(this.cause.toString());
}
return description.toString();
}
}
ErrorContext使用private 修饰的ThreadLocal来保证每个线程拥有一个ErrorContext对象
在调用instance()方法时再从ThreadLocal中获取此单例对象
- 适配器模式
●指将一个不兼容的接口转换成另一个可以兼容的接口
这样就可以使那些不兼容的类可以一起工作
●例如,最早之前我们用的耳机都是圆形的,而现在大多数的耳机和电源都
统一成了方形的typec接口,那之前的圆形耳机就不能使用了
只能买一个适配器把圆形接口转化成方形的
个转换头就相当于程序中的适配器模式
适配器模式在MyBatis中的典型代表是Log
MyBatis中的日志模块适配了以下多种日志类型:
SLF4J
Apache Commons Logging
Log4j 2
Log4j
JDK logging
Log 类的源码如下
public interface Log {
boolean isDebugEnabled();
boolean isTraceEnabled();
void error(String var1, Throwable var2);
void error(String var1);
void debug(String var1);
void trace(String var1);
void warn(String var1);
}
然后MyBatis定义了多个适配接口,例如Log4j2实现源码如下:
public class Log4j2Impl implements Log {
private final Log log;
public Log4j2Impl(String clazz) {
Logger logger = LogManager.getLogger(clazz);
if (logger instanceof AbstractLogger) {
this.log = new Log4j2AbstractLoggerImpl((AbstractLogger)logger);
} else {
this.log = new Log4j2LoggerImpl(logger);
}
}
public boolean isDebugEnabled() {
return this.log.isDebugEnabled();
}
public boolean isTraceEnabled() {
return this.log.isTraceEnabled();
}
public void error(String s, Throwable e) {
this.log.error(s, e);
}
public void error(String s) {
this.log.error(s);
}
public void debug(String s) {
this.log.debug(s);
}
public void trace(String s) {
this.log.trace(s);
}
public void warn(String s) {
this.log.warn(s);
}
}
Slf4jImpl 也是类似这样
public class Slf4jImpl implements Log {
private Log log;
public Slf4jImpl(String clazz) {
Logger logger = LoggerFactory.getLogger(clazz);
if (logger instanceof LocationAwareLogger) {
try {
logger.getClass().getMethod("log", Marker.class, String.class, Integer.TYPE, String.class, Object[].class, Throwable.class);
this.log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger)logger);
return;
} catch (NoSuchMethodException | SecurityException var4) {
}
}
this.log = new Slf4jLoggerImpl(logger);
}
public boolean isDebugEnabled() {
return this.log.isDebugEnabled();
}
public boolean isTraceEnabled() {
return this.log.isTraceEnabled();
}
public void error(String s, Throwable e) {
this.log.error(s, e);
}
public void error(String s) {
this.log.error(s);
}
public void debug(String s) {
this.log.debug(s);
}
public void trace(String s) {
this.log.trace(s);
}
public void warn(String s) {
this.log.warn(s);
}
}
tip: 查看接口被那些类继承 ctrl+h / control + h
- 代理模式
●指的是给某一个对象提供一 个代理对象, 并由代理对象控制原对象的调用
●代理模式在生活中也比较常见
比如超市、小卖店都是-一个个“代理”,他们的最上游是一个个生产厂家
这些代理负责把厂家生产出来的产品卖出去
●代理模式在MyBatis中的典型代表是MapperProxyFactory
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public Map<Method, MapperMethodInvoker> getMethodCache() {
return this.methodCache;
}
/**
* newInstance 就是生成一个代理类 来实现具体的功能
*/
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);
}
}
- 模板方法模式
最常用的设计模式之-
●指定义一个操作算法的骨架,一些步骤的实现延迟到子类中去实现
使得子类可以不改变一个算法的结构,即可重定义该算法的某些特定步骤
●此模式是基于继承的思想实现代码复用的
整个过程都是固定的,唯一变的就是泡入茶叶种类的不同
比如今天喝的是绿茶,明天可能喝的是红茶,那么我们就可以把流程定义为- -个模板
而把茶叶的种类延伸到子类中去实现,这就是模板方法的实现思路
模板方法在MyBatis中的典型代表是BaseExecutor
比如这里的BaseExecutor.doUpdate 方法在SimpleExecutor 中的实现如下
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
int var6;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var6 = handler.update(stmt);
} finally {
// 每次调用完毕的时候,都会关闭Statement
this.closeStatement(stmt);
}
return var6;
}
而在 ReuseExecutor 中每次使用完毕后则不会关闭Statement
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
Statement stmt = this.prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
}
- 装饰器模式
●允许向一个现有的对象添加新的功能,同时又不改变其结构
这种类型的设计模式属于结构型模式,它是作为现有类的一个包装
装饰器模式在生活中很常见,比如装修房子,我们在不改变房子结构的同时
给房子添加了很多的点缀;比如安装了天然气报警器
增加了热水器等附加的功能都属于装饰器模式
装饰器模式在MyBatis中的典型代表是Cache
●Cache除了有数据存储和缓存的基本功能外(由PerpetualCache永久缓存实现)
还有其他附加的Cache类,比如先进先出的FifoCache、最近最少使用的LruCache
●防止多线程并发访问的SynchronizedCache等众多附加功能的缓存类