Хотя все мы знаем, что существует 26 шаблонов проектирования, большинство из них остаются на концептуальном уровне, что редко встречается в реальной разработке.Большое количество шаблонов проектирования используется в исходном коде Mybatis.Чтение исходного кода и наблюдение за приложением шаблонов проектирования в нем можно использовать более углубленно.
Mybatis столкнулся, по крайней мере, с использованием следующих шаблонов проектирования:
- Режим построителя, например SqlSessionFactoryBuilder, XMLConfigBuilder, XMLMapperBuilder, XMLStatementBuilder, CacheBuilder;
- Заводской режим, такой как SqlSessionFactory, ObjectFactory, MapperProxyFactory;
- Одноэлементные шаблоны, такие как ErrorContext и LogFactory;
- Режим прокси, ядро реализации Mybatis, такое как MapperProxy, ConnectionLogger, использует динамический прокси jdk, а пакет executor.loader использует cglib или javassist для достижения эффекта отложенной загрузки;
- Комбинированные режимы, такие как SqlNode и различные подклассы ChooseSqlNode и т. д.;
- Шаблоны методов шаблонов, такие как BaseExecutor и SimpleExecutor, а также BaseTypeHandler и все подклассы, такие как IntegerTypeHandler;
- Режим адаптера, такой как интерфейс Log Mybatis и его адаптация к различным платформам журналов, таким как jdbc и log4j;
- режим декоратора, такой как реализация каждого декоратора в подпакете cache.decorators в пакете Cache;
- Шаблоны итераторов, такие как шаблон итератора PropertyTokenizer;
Далее я буду интерпретировать паттерны один за другим, сначала представлю знание самого паттерна, а затем интерпретирую, как паттерн применяется в Mybatis.
1. Режим строителя
Определение шаблона Builder состоит в том, чтобы «отделить построение сложного объекта от его представления, чтобы один и тот же процесс построения мог создавать разные представления». более сложный, выходящий за рамки Если объем конструктора может быть включен, можно использовать фабричный шаблон и шаблон Builder.По сравнению с фабричным шаблоном, который будет производить законченный продукт, Builder можно использовать для создания более сложных объектов, и даже построить только часть продукта.
Во время инициализации среды Mybatis SqlSessionFactoryBuilder вызовет XMLConfigBuilder для чтения всех файлов MybatisMapConfig.xml и *Mapper.xml, создания основного объекта объекта Configuration, запускаемого Mybatis, а затем использования объекта Configuration в качестве параметра для построения объекта SqlSessionFactory. .
Когда XMLConfigBuilder создает объект Configuration, он также вызывает XMLMapperBuilder для чтения файлов *Mapper, а XMLMapperBuilder использует XMLStatementBuilder для чтения и построения всех операторов SQL.
В этом процессе есть аналогичная функция, что эти Builders будут читать файлы или конфигурации, а затем выполнять много синтаксического анализа XpathParser, синтаксического анализа конфигурации или грамматики, отражения для создания объектов и сохранения результатов в кеше.Это не то, что конструктор может включать, поэтому для ее решения используется большое количество паттернов Builder.
Для конкретных классов Builder метод использует build * start, например SQLSessionFactoryBuilder, который содержит следующие методы:
То есть фабричный объект SqlSessionFactory создается в соответствии с разными входными параметрами.
2. Заводской режим
В Mybatis, например, SqlSessionFactory использует фабричный паттерн, который не имеет такой сложной логики и представляет собой простой фабричный паттерн.
Простой фабричный шаблон: также известный как статический фабричный метод, он относится к шаблону создания класса. В простом фабричном шаблоне экземпляры разных классов могут быть возвращены в зависимости от параметров. Простой фабричный шаблон специально определяет класс, ответственный за создание экземпляров других классов, и созданные экземпляры обычно имеют общий родительский класс.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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 код его реализации:
1
2
3
4
5
6
7
8
9
10
|
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 — ConstructorextendsLog>, то есть фабрика производит не один продукт, а серию продуктов с публичным интерфейсом Log, таких как Log4jImpl, Slf4jImpl и многие другие специфические Logs.
3. Одноэлементный режим
Синглтон (Singleton Pattern): Синглтон гарантирует, что только один экземпляр определенного класса, а также для создания экземпляра всей системы и предоставления этого класса называется классом синглтона, который обеспечивает глобальный метод доступа.
В шаблоне singleton есть три основных момента: во-первых, класс может иметь только один экземпляр; во-вторых, он должен создать этот экземпляр сам; в-третьих, он должен сам предоставить этот экземпляр всей системе. Шаблон синглтона — это шаблон создания объекта. Одноэлементный шаблон также известен как одноэлементный шаблон или одноэлементный шаблон.
В Mybatis есть два места, которые используют одноэлементный режим: ErrorContext и LogFactory, где ErrorContext — это одноэлементный элемент, используемый в рамках каждого потока для записи информации об ошибках среды выполнения потока, а LogFactory предоставляется всему Mybatis. Фабрика журналов используется для получения объекта журнала, настроенного для проекта.
Одноэлементный код реализации ErrorContext:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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, это объектно-структурированный режим.
Режим прокси включает в себя следующие роли:
- Тема: роль абстрактного субъекта
- Прокси: Роль субъекта прокси
- RealSubject: реальная личность субъекта
Здесь есть два шага, первый — заранее создать прокси, а второй — запросить прокси при использовании, а затем выполнить конкретную транзакцию через прокси;
Когда мы используем метод конфигурации getMapper, вызывает метод mapperRegistry.getMapper, и этот метод вызывает mapperProxyFactory.newInstance (sqlSession) для создания определенного агента:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
/**
* @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(MapperProxy
Глядя на код MapperProxy, вы можете увидеть следующее:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<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), который представляет собой интерфейс, реализуемый всеми динамическими узлами:
1
2
3
|
public interface SqlNode {
boolean apply(DynamicContext context);
}
|
Для всех узлов, реализующих интерфейс SqlSource, это каждый узел всего комбинированного дерева режимов:
Простота комбинированного режима заключается в том, что все дочерние узлы имеют один и тот же тип и могут выполняться рекурсивно вниз.Например, для TextSqlNode, поскольку это самый нижний конечный узел, он напрямую добавляет соответствующее содержимое к оператору SQL:
1
2
3
4
5
6
|
@Override
public boolean apply(DynamicContext context) {
GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));
context.appendSql(parser.parse(text));
return true;
}
|
Но для IfSqlNode сначала нужно вынести суждение.Если суждение вынесено, SqlNode дочернего элемента, то есть методcontents.apply, все равно будет вызываться для реализации рекурсивного анализа.
1
2
3
4
5
6
7
8
|
@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, а затем предоставляет следующие методы подклассам для настройки:
1
2
3
4
5
6
|
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;
|
Класс метода шаблона имеет конкретные реализации нескольких подклассов, использующих разные стратегии:
- Simple SimpleExecutor: каждый раз, когда выполняется обновление или выбор, открывается объект Statement, который закрывается сразу после использования. (может быть объектом Statement или PrepareStatement)
- Повторно используйте ReuseExecutor: выполните обновление или выберите, используйте sql в качестве ключа для поиска объекта Statement, используйте его, если он существует, и создайте его, если он не существует.После использования объект Statement не закрывается, а помещается в Map для следующего использования. (может быть объектом Statement или PrepareStatement)
- Batch BatchExecutor: выполнить обновление (без выбора, пакет JDBC не поддерживает выбор), добавить все sql в пакет (addBatch()), дождаться унифицированного выполнения (executeBatch()), кэшировать несколько объектов Statement, каждый после addBatch() завершен, каждый объект Statement ожидает выполнения пакетов executeBatch() один за другим; BatchExecutor эквивалентен поддержке нескольких сегментов, и каждый сегмент содержит множество собственных SQL-запросов, как Apple Blue. помидоры томатно-голубого цвета, и, наконец, все они свалены на склад. (может быть объектом Statement или PrepareStatement)
Например, реализуйте метод обновления в SimpleExecutor следующим образом:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@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);
}
}
|
7. Режим адаптера
Шаблон адаптера (шаблон адаптера): преобразование интерфейса в другой интерфейс, который хочет клиент.Шаблон адаптера позволяет этим классам с несовместимыми интерфейсами работать вместе, а его псевдоним является оболочкой (Wrapper). Шаблон адаптера может использоваться либо как шаблон структуры класса, либо как объектно-структурный шаблон.
В пакете ведения журнала Mybatsi есть интерфейс журнала:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/**
* @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, а затем этому экземпляру делегируются все методы журнала для реализации.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
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);
}
}
|
8. Шаблон декоратора
Шаблон декоратора: динамическое добавление некоторых дополнительных обязанностей (Responsibility) к объекту.С точки зрения добавления функций объекта шаблон декоратора является более гибким, чем создание подклассов. Его псевдоним также можно назвать оберткой (Wrapper), который совпадает с псевдонимом шаблона адаптера, но подходят они для разных случаев. В зависимости от перевода шаблон украшения также называется «шаблон художника», который представляет собой шаблон структуры объекта.
В mybatis функция кэширования определяется корневым интерфейсом Cache (org.apache.ibatis.cache.Cache). Вся система принимает шаблон проектирования декоратора.Основные функции хранения и кэширования данных реализуются постоянным кешем PerpetualCache (org.apache.ibatis.cache.impl.PerpetualCache), а затем ряд декораторов используется для реализации стратегий кэширования для Постоянный кеш PerpetualCache и т.д. Удобное управление. Как показано ниже:
Есть 8 стандартных декораторов для декорирования PerpetualCache (все в пакете org.apache.ibatis.cache.decorators):
- FifoCache: алгоритм FIFO, политика кэширования сбора
- LoggingCache: вывод информации журнала в кэш-попаданиях.
- LruCache: наименее часто используемый алгоритм, стратегия повторного использования кэша
- ScheduledCache: запланированный кеш, отвечающий за регулярную очистку кеша.
- SerializedCache: Кэш-хранилище для сериализации и десериализации
- SoftCache: стратегия управления кэшем, основанная на реализации программной ссылки.
- SynchronizedCache: декоратор синхронизированного кеша для предотвращения одновременного доступа нескольких потоков.
- WeakCache: стратегия управления кэшем, основанная на слабой эталонной реализации
Кроме того, есть специальный декоратор TransactionalCache: транзакционный кеш
Как и большинство фреймворков уровня сохраняемости, кеш mybatis также делится на кеш первого уровня и кеш второго уровня.
- Кэш первого уровня, также известный как локальный кеш, представляет собой постоянный кеш типа PerpetualCache, который хранится в исполнителе (BaseExecutor), а исполнитель находится в SqlSession (DefaultSqlSession), поэтому жизненный цикл первого Кэш уровня такой же, как у SqlSession.
- Кэш второго уровня, также известный как пользовательский кеш, классы, реализующие интерфейс Cache, могут использоваться в качестве кеша второго уровня, поэтому можно настроить сторонние кеши, такие как encache. Кэш второго уровня однозначно идентифицируется пространством имен пространства имен и хранится в основном объекте конфигурации Configuration.
Тип объекта кеша второго уровня по умолчанию — PerpetualCache.Если настроенный кеш является типом по умолчанию, mybatis автоматически добавит серию декораторов в соответствии с конфигурацией.
Порядок ссылок между объектами кэша:
SynchronizedCache->LoggingCache->SerializedCache->ScheduledCache->LruCache->PerpetualCache
9. Режим итератора
Режим итератора (Iterator), также известный как режим курсора (Cursor). Определение, данное GOF, таково: предоставить способ доступа к каждому элементу в объекте-контейнере без раскрытия внутренних деталей объекта.
Итератор в Java — это интерфейс шаблона итератора. Пока этот интерфейс реализован, он эквивалентен применению шаблона итератора:
Например, PropertyTokenizer Mybatis — это тяжеловесный класс в пакете свойств, на который часто ссылаются другие классы в пакете отражения. Этот класс реализует интерфейс Iterator, и при его использовании часто используется функция hasNext в интерфейсе Iterator.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
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.");
}
}
|
Как видите, этот класс передает строку конструктору, а затем предоставляет метод итератора для обхода проанализированной подстроки, что является очень распространенным классом методов.
- Шаблон дизайна иллюстрации:design-patterns.read the doc S.IO/this_cn/latshan…
- Серия Mybatis (десять) — анализ процесса выполнения SQL (исходный код):блог woo woo woo.cn на.com/dongying/afraid/…
- Примечания к чтению в режиме разработки — комбинированный режимБлог Woohoo.cn on.com/Chen's College/Afraid/3…
- MyBatis3.3.x Technology Insider (D): пять мышей в центре города Токио Оригинальный дизайн исполнительника приводаblog.CSDN.net/Crock Pot Catering/art IC…
- Подробное объяснение механизма кэширования mybatis (1) - КэшOSCHINA.net/Li Xin91/No…