предисловие
Хотя все мы знаем, что существует 26 шаблонов проектирования, большинство из них остаются на концептуальном уровне и редко встречаются в реальной разработке.Большое количество шаблонов проектирования используется в исходном коде Mybatis.Чтение исходного кода и наблюдение за приложением шаблонов проектирования в нем можно использовать более подробно Понимание шаблонов проектирования.
Mybatis, по крайней мере, сталкивался с использованием следующих шаблонов проектирования.
1. Режим строителя
Например, SqlSessionFactoryBuilder, XMLConfigBuilder, XMLMapperBuilder, XMLStatementBuilder, CacheBuilder.
2. Заводской режим
Например, SqlSessionFactory, ObjectFactory, MapperProxyFactory.
3. Одноэлементный режим
Например, ErrorContext и LogFactory.
4. Прокси-режим
Ядро реализации Mybatis, такое как MapperProxy и ConnectionLogger, использует динамический прокси jdk, а пакет executor.loader использует cglib или javassist для достижения эффекта отложенной загрузки.
5. Комбинированный режим
Например, SqlNode и различные подклассы ChooseSqlNode и т.д.
6. Паттерн шаблонного метода
Такие как BaseExecutor и SimpleExecutor, а также BaseTypeHandler и все подклассы, такие как IntegerTypeHandler.
7. Режим адаптера
Например, интерфейс журнала Mybatis и его адаптация к различным средам журналов, таким как jdbc и log4j.
8. Шаблон декоратора
Например, реализация каждого декоратора в подпакете cache.decorators в пакете Cache.
9. Режим итератора
Например, шаблон итератора PropertyTokenizer.
Далее я буду интерпретировать паттерны один за другим, сначала представлю знание самого паттерна, а затем интерпретирую, как паттерн применяется в Mybatis.
1. Режим строителя
Определение шаблона Builder состоит в том, чтобы «отделить построение сложного объекта от его представления, чтобы один и тот же процесс построения мог создавать разные представления». более сложный, выходящий за рамки Если объем конструктора может быть включен, можно использовать фабричный шаблон и шаблон Builder.По сравнению с фабричным шаблоном, который будет производить законченный продукт, Builder можно использовать для создания более сложных объектов, и даже построить только часть продукта.
В процессе инициализации среды Mybatis SqlSessionFactoryBuilder вызовет XMLConfigBuilder для чтения всех файлов MybatisMapConfig.xml и Mapper.xml, создания объекта конфигурации, основного объекта работающего Mybatis, а затем использования объекта конфигурации в качестве параметра для построения Объект SqlSessionFactory.
Когда XMLConfigBuilder создает объект Configuration, он также вызывает XMLMapperBuilder для чтения файлов *Mapper, а XMLMapperBuilder использует XMLStatementBuilder для чтения и построения всех операторов SQL.
В этом процессе есть аналогичная функция, что эти Builders будут читать файлы или конфигурации, а затем выполнять много синтаксического анализа XpathParser, синтаксического анализа конфигурации или грамматики, отражения для создания объектов и сохранения результатов в кеше.Это не то, что конструктор может включать, поэтому для ее решения используется большое количество паттернов Builder.
Для конкретных классов построителей большинство методов начинаются с build*, например SqlSessionFactoryBuilder, который содержит следующие методы:
То есть фабричный объект SqlSessionFactory создается в соответствии с разными входными параметрами.
Во-вторых, заводской режим
В Mybatis, например, SqlSessionFactory использует фабричный паттерн, который не имеет такой сложной логики и представляет собой простой фабричный паттерн.
Простой фабричный шаблон: также известный как статический фабричный метод, он относится к шаблону создания класса. В простом фабричном шаблоне экземпляры разных классов могут быть возвращены в зависимости от параметров. Простой фабричный шаблон специально определяет класс, ответственный за создание экземпляров других классов, и созданные экземпляры обычно имеют общий родительский класс.
SqlSession можно рассматривать как основной интерфейс для работы Mybatis, с помощью которого вы можете выполнять операторы SQL, получать Mappers и управлять транзакциями. Подобно объекту Connection, который подключается к MySQL.
Видно, что метод 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);
final Executor executor = configuration.newExecutor(tx, execType);
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.Этот метод сначала считывает соответствующую конфигурацию среды из конфигурации, затем инициализирует TransactionFactory для получения объекта Transaction, затем получает объект Executor через Transaction и, наконец, создает конфигурацию с помощью трех параметров конфигурации. , Executor и autoCommit.SqlSession.
На самом деле здесь тоже можно увидеть подсказку: выполнение SqlSession фактически делегируется соответствующему исполнителю.
А для LogFactory код его реализации:
public final class LogFactory {
private static Constructor<? extends Log> logConstructor;
private LogFactory() {
// disable construction
}
public static Log getLog(Class<?> aClass) {
return getLog(aClass.getName());
}
Здесь особое место, тип переменной Log — Constructor, что означает, что фабрика производит не один продукт, а серию продуктов с публичным интерфейсом Log, таких как Log4jImpl, Slf4jImpl и т.д. .конкретный лог.
3. Одноэлементный режим
Одноэлементный шаблон (Singleton Pattern): Одноэлементный шаблон гарантирует, что определенный класс имеет только один экземпляр, создает экземпляр самого себя и предоставляет этот экземпляр всей системе.Этот класс называется одноэлементным классом, который предоставляет методы для глобального доступа.
В шаблоне singleton есть три основных момента: во-первых, класс может иметь только один экземпляр; во-вторых, он должен создать этот экземпляр сам; в-третьих, он должен сам предоставить этот экземпляр всей системе. Шаблон синглтона — это шаблон создания объекта. Одноэлементный шаблон также известен как одноэлементный шаблон или одноэлементный шаблон.
В Mybatis есть два места, которые используют одноэлементный режим: ErrorContext и LogFactory, где ErrorContext — это одноэлементный элемент, используемый в рамках каждого потока для записи информации об ошибках среды выполнения потока, а LogFactory предоставляется всему Mybatis. Фабрика журналов используется для получения объекта журнала, настроенного для проекта.
Одноэлементный код реализации ErrorContext:
public class ErrorContext {
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();
private ErrorContext() {
}
public static ErrorContext instance() {
ErrorContext context = LOCAL.get();
if (context == null) {
context = new ErrorContext();
LOCAL.set(context);
}
return context;
}
Конструктор — приватная модификация, со статической локальной переменной экземпляра и методом получения экземплярной переменной.В методе получения экземпляра сначала определить, пустой ли он, и если да, то сначала создать его, а потом вернуть сконструированный объект.
Вот только здесь есть интересный момент.Статическая переменная экземпляра LOCAL украшена ThreadLocal, а это значит, что она принадлежит соответствующим данным каждого потока.В методе instance() сначала получается экземпляр потока, если нет, то создайте ErrorContext, уникальный для этого потока.
4. Агентский режим
Режим прокси можно рассматривать как режим, используемый ядром Mybatis, именно из-за этого режима нам нужно только написать интерфейс Mapper.java, не нужно его реализовывать, а фон Mybatis поможет нам завершить выполнение конкретного SQL.
Шаблон прокси: укажите прокси для объекта, а прокси-объект управляет ссылкой на исходный объект. Режим прокси на английском языке называется Proxy или Surrogate, что является режимом структуры объекта.
Режим прокси включает в себя следующие роли:
1.Subject:
абстрактная тематическая роль
2.Proxy:
Агент Субъект Роль
3.RealSubject:
настоящие тематические персонажи
Здесь есть два шага: первый — создать прокси заранее, а второй — автоматически запросить прокси при его использовании, после чего прокси выполнит конкретную транзакцию; Когда мы используем метод конфигурации getMapper, вызывается метод mapperRegistry.getMapper, который, в свою очередь, вызывает mapperProxyFactory.newInstance(sqlSession) для создания определенного прокси:
/**
* @author Lasse Voss
*/
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);
}
}
Здесь сначала получите объект MapperProxy с помощью метода T newInstance(SqlSession sqlSession), а затем вызовите T newInstance(MapperProxymapperProxy), чтобы сгенерировать прокси-объект и вернуть его.
Глядя на код 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);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
Как правило, класс MapperProxy реализует интерфейс InvocationHandler и реализует метод вызова интерфейса.
Таким образом, нам нужно только написать класс интерфейса Mapper.java.Когда интерфейс Mapper действительно выполняется, он будет переадресован методу MapperProxy.invoke, который вызовет последующий sqlSession.cud>executor.execute>prepareStatement. для ряда методов для завершения выполнения и возврата SQL.
5. Комбинированный режим
В режиме композиции несколько объектов объединяются в древовидную структуру для представления иерархии «целое-часть».
Шаблон композиции имеет согласованность для одиночных объектов (листовых объектов) и составных объектов (составных объектов), он организует объекты в древовидную структуру, которую можно использовать для описания отношений между целым и частью. В то же время он также размывает понятия простых элементов (объектов-листов) и сложных элементов (объектов-контейнеров), чтобы клиенты могли обрабатывать сложные элементы так же, как простые элементы, чтобы клиентские программы могли быть отделены от внутренней структуры сложных элементов. .
Одна вещь, на которую следует обратить внимание при использовании шаблона композиции, также является наиболее важной частью шаблона композиции: конечный объект и объект композиции реализуют один и тот же интерфейс. Вот почему режим композиции может последовательно обрабатывать конечные узлы и узлы объектов.
Mybatis поддерживает возможности динамического SQL, например следующий SQL:
<update id="update" parameterType="org.format.dynamicproxy.mybatis.bean.User">
UPDATE users
<trim prefix="SET" prefixOverrides=",">
<if test="name != null and name != ''">
name = #{name}
</if>
<if test="age != null and age != ''">
, age = #{age}
</if>
<if test="birthday != null and birthday != ''">
, birthday = #{birthday}
</if>
</trim>
where id = ${id}
</update>
При этом используются динамические элементы, такие как trim и if, а SQL в разных ситуациях может генерироваться по условиям;
В методе DynamicSqlSource.getBoundSql вызывается метод rootSqlNode.apply(context), который представляет собой интерфейс, реализуемый всеми динамическими узлами:
public interface SqlNode {
boolean apply(DynamicContext context);
}
Для всех узлов, реализующих интерфейс SqlSource, это каждый узел всего комбинированного дерева режимов:
Простота комбинированного режима заключается в том, что все дочерние узлы имеют один и тот же тип и могут выполняться рекурсивно вниз.Например, для TextSqlNode, поскольку это самый нижний конечный узел, он напрямую добавляет соответствующее содержимое к оператору SQL:
@Override
public boolean apply(DynamicContext context) {
GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));
context.appendSql(parser.parse(text));
return true;
}
Но для IfSqlNode сначала нужно вынести суждение.Если суждение вынесено, SqlNode дочернего элемента, то есть методcontents.apply, все равно будет вызываться для реализации рекурсивного анализа.
@Override
public boolean apply(DynamicContext context) {
if (evaluator.evaluateBoolean(test, context.getBindings())) {
contents.apply(context);
return true;
}
return false;
}
6. Паттерн шаблонного метода
Шаблон метода шаблона является одним из наиболее распространенных шаблонов среди всех шаблонов и представляет собой базовый метод повторного использования кода, основанный на наследовании.
Шаблон шаблонного метода требует совместной работы дизайнеров, разрабатывающих абстрактные классы и конкретные подклассы. Один разработчик отвечает за создание схемы и скелета алгоритма, а другие разработчики отвечают за предоставление различных логических шагов алгоритма. Метод, который представляет эти конкретные логические шаги, называется первичным методом; метод, объединяющий эти базовые методы, называется шаблонным методом, отсюда и название данного шаблона проектирования.
Классы-шаблоны определяют скелет алгоритма в операции, откладывая при этом некоторые шаги на подклассы. Позволяет подклассам переопределять определенные шаги алгоритма без изменения его структуры.
В Mybatis выполнение SQL-запроса sqlSession делегировано Executor, Executor содержит следующую структуру:
Среди них BaseExecutor принимает режим метода шаблона, который реализует большую часть логики выполнения SQL, а затем предоставляет следующие методы подклассам для настройки:
protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) throws SQLException;
Класс метода шаблона имеет конкретные реализации нескольких подклассов, использующих разные стратегии:
1. Простой SimpleExecutor
Каждый раз, когда выполняется обновление или выбор, открывается объект Statement, который закрывается сразу после использования. (может быть объектом Statement или PrepareStatement)
2. Повторно используйте ReuseExecutor
Выполните обновление или выберите, используйте sql в качестве ключа для поиска объекта Statement, используйте его, если он существует, и создайте его, если он не существует. (может быть объектом Statement или PrepareStatement)
3. Пакетный пакетный исполнитель
Выполнить обновление (без выбора, пакет JDBC не поддерживает выбор), добавить все sql в пакет (addBatch()), дождаться унифицированного выполнения (executeBatch()), он кэширует несколько объектов Statement, каждый объект Statement после addBatch() завершится, дождитесь выполнения пакетной обработки executeBatch() одну за другой; BatchExecutor эквивалентен поддержке нескольких корзин, каждая корзина содержит много своего собственного SQL, точно так же, как Apple blue имеет много яблок, в яблоках много помидоров. томатно-голубой, и, наконец, их заливают на склад. (может быть объектом Statement или PrepareStatement)
Например, реализуйте метод обновления в SimpleExecutor следующим образом:
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null,
null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
Семь, режим адаптера
Шаблон адаптера (шаблон адаптера): преобразование интерфейса в другой интерфейс, который хочет клиент.Шаблон адаптера позволяет этим классам с несовместимыми интерфейсами работать вместе, а его псевдоним является оболочкой (Wrapper). Шаблон адаптера может использоваться либо как шаблон структуры класса, либо как шаблон структуры объекта.
В пакете ведения журнала Mybatsi есть интерфейс журнала:
/**
* @author Clinton Begin
*/
public interface Log {
boolean isDebugEnabled();
boolean isTraceEnabled();
void error(String s, Throwable e);
void error(String s);
void debug(String s);
void trace(String s);
void warn(String s);
}
Этот интерфейс определяет метод журнала, непосредственно используемый Mybatis, и кто реализует интерфейс журнала? Mybatis предоставляет множество реализаций фреймворка журнала, все из которых соответствуют методам интерфейса, определенным этим интерфейсом журнала, и, наконец, реализуют адаптацию всех внешних фреймворков журналов к пакетам журналов Mybatis:
Например, для реализации Log4jImpl реализация содержит экземпляр org.apache.log4j.Logger, а затем этому экземпляру делегируются все методы журнала для реализации.
public class Log4jImpl implements Log {
private static final String FQCN = Log4jImpl.class.getName();
private Logger log;
public Log4jImpl(String clazz) {
log = Logger.getLogger(clazz);
}
@Override
public boolean isDebugEnabled() {
return log.isDebugEnabled();
}
@Override
public boolean isTraceEnabled() {
return log.isTraceEnabled();
}
@Override
public void error(String s, Throwable e) {
log.log(FQCN, Level.ERROR, s, e);
}
@Override
public void error(String s) {
log.log(FQCN, Level.ERROR, s, null);
}
@Override
public void debug(String s) {
log.log(FQCN, Level.DEBUG, s, null);
}
@Override
public void trace(String s) {
log.log(FQCN, Level.TRACE, s, null);
}
@Override
public void warn(String s) {
log.log(FQCN, Level.WARN, s, null);
}
}
Восемь, шаблон декоратора
Шаблон декоратора: динамическое добавление некоторых дополнительных обязанностей (Responsibility) к объекту.С точки зрения добавления функций объекта шаблон декоратора является более гибким, чем создание подклассов. Его псевдоним также можно назвать оберткой (Wrapper), который совпадает с псевдонимом шаблона адаптера, но подходят они для разных случаев. В зависимости от перевода шаблон украшения также называется «шаблон художника», который представляет собой шаблон структуры объекта.
В mybatis функция кэширования определяется корневым интерфейсом Cache (org.apache.ibatis.cache.Cache). Вся система принимает шаблон проектирования декоратора.Основные функции хранения и кэширования данных реализуются постоянным кешем PerpetualCache (org.apache.ibatis.cache.impl.PerpetualCache), а затем ряд декораторов используется для реализации стратегий кэширования для Постоянный кеш PerpetualCache и т.д. Удобное управление. Как показано ниже:
Есть 8 стандартных декораторов для декорирования PerpetualCache (все в пакете org.apache.ibatis.cache.decorators):
1.FifoCache:
Алгоритм FIFO, стратегия утилизации кеша
2.Кэш журналирования:
Информация журнала попаданий в кеш вывода
3. ЛруКэш:
Наименее используемый алгоритм, стратегия очистки кеша
4. Запланированный кэш:
Кэш планирования, отвечающий за регулярную очистку кеша
5. Сериализованный кеш:
Хранение сериализации и десериализации кеша
6. Мягкий кэш:
Стратегия управления кэшем на основе реализации программной ссылки
7. Синхронизированный кэш:
Синхронизированный декоратор кеша для предотвращения одновременного доступа нескольких потоков
8. Слабый кеш:
Стратегия управления кэшем на основе слабой ссылки
Кроме того, есть специальный декоратор TransactionalCache: транзакционный кеш
Как и большинство фреймворков уровня сохраняемости, кеш mybatis также делится на кеш первого уровня и кеш второго уровня.
1. Кэш 1-го уровня
Также называемый локальным кешем, это постоянный кеш типа PerpetualCache, который хранится в исполнителе (BaseExecutor), а исполнитель находится в SqlSession (DefaultSqlSession), поэтому жизненный цикл кеша первого уровня такой же, как у кеша первого уровня. SQLSession.
2. Кэш L2
Также известные как настраиваемый кеш, классы, реализующие интерфейс Cache, можно использовать в качестве кешей второго уровня, поэтому можно настроить сторонние кеши, такие как encache. Кэш второго уровня однозначно идентифицируется пространством имен пространства имен и хранится в основном объекте конфигурации Configuration.
3. Кэш-объект уровня 2
Тип по умолчанию — PerpetualCache.Если настроенный кеш является типом по умолчанию, mybatis автоматически добавит серию декораторов в соответствии с конфигурацией.
Порядок ссылок между объектами Cache:
SynchronizedCache->LoggingCache->SerializedCache->ScheduledCache->LruCache->PerpetualCache
Девять, режим итератора
Режим итератора (Iterator), также известный как режим курсора (Cursor). Определение, данное GOF, таково: предоставить способ доступа к каждому элементу в объекте-контейнере без раскрытия внутренних деталей объекта.
Итератор в Java — это интерфейс шаблона итератора. Пока этот интерфейс реализован, он эквивалентен применению шаблона итератора:
Например, PropertyTokenizer Mybatis — это тяжеловесный класс в пакете свойств, на который часто ссылаются другие классы в пакете отражения. Этот класс реализует интерфейс Iterator, и при его использовании часто используется функция hasNext в интерфейсе Iterator.
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
private String name;
private String indexedName;
private String index;
private String children;
public PropertyTokenizer(String fullname) {
int delim = fullname.indexOf('.');
if (delim > -1) {
name = fullname.substring(0, delim);
children = fullname.substring(delim + 1);
} else {
name = fullname;
children = null;
}
indexedName = name;
delim = name.indexOf('[');
if (delim > -1) {
index = name.substring(delim + 1, name.length() - 1);
name = name.substring(0, delim);
}
}
public String getName() {
return name;
}
public String getIndex() {
return index;
}
public String getIndexedName() {
return indexedName;
}
public String getChildren() {
return children;
}
@Override
public boolean hasNext() {
return children != null;
}
@Override
public PropertyTokenizer next() {
return new PropertyTokenizer(children);
}
@Override
public void remove() {
throw new UnsupportedOperationException(
"Remove is not supported, as it has no meaning in the context of properties.");
}
}
Как видите, этот класс передает строку конструктору, а затем предоставляет метод итератора для обхода проанализированной подстроки, что является очень распространенным классом методов.
Суммировать
Я собрал копию здесь: карта мозга Mybatis, информационные документы, связанные с Mybatis, серия семейных ведер Spring, систематическая информация по Java (включая основные точки знаний по Java, темы интервью и последние 20 лет реальных вопросов в Интернете, электронные книги и т. д. ) Есть друзья, которым это нужно, могут подписаться на официальный аккаунт [Программа Юань Сяован], чтобы получить его.