отношение решает все.
1. Конфигурация контейнера2. Динамические шаблоны SQL1. MappedStatement (сопоставитель)2. Процесс анализа3. SQL-сессия1. Основное введение2. Классификация3.Executor4. Mapper (один и тот же пункт назначения разными путями)1. Смысл существования2. Принцип работы5. Кэш1. Кэш 1-го уровня2. Кэш L22.1 Основная информация2.2 Как это работает6. Плагины7. Отображение результатов8. Резюме
После просмотра Mybatis я думаю, что Mybatis маленький, но у него есть все внутренние органы и изысканный дизайн.
Какова конструкция этого черного ящика? Позвольте мне рассказать вам, как я понимаю
1. Конфигурация контейнера
Configuration
Как и менеджер Mybatis, вся информация о конфигурации Mybatis хранится здесь, кроме того, он также предоставляет метод для настройки этой информации о конфигурации. Конфигурация может получать значения свойств из конфигурационных файлов или задавать их напрямую через программы.
Кратко опишите конфигурацию одним предложением, он类似Spring中的容器概念
, так и на уровне центрального контейнера, в котором хранится большая часть того, что нужно для запуска Mybatis.
2. Динамические шаблоны SQL
С mybatis, что мы проводим большую часть нашего времени? Напишите шаблон SQL в XML или напишите шаблон SQL в интерфейсе
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- mapper:根标签,namespace:命名空间,命名空间唯一 -->
<mapper namespace="UserMapper">
<select id="selectUser" resultType="com.wqd.model.User">
select * from user where id= #{id}
</select>
</mapper>
или
@Mapper
public interface UserMapper {
@Insert("insert into user( name, age) " +
"values(#{user.name}, #{user.age})")
void save(@Param("user") User user);
@Select("select * from user where id=#{id}")
User getById(@Param("id")String id);
}
Что это означает для внутреннего устройства фреймворка Mybatis?
1. MappedStatement (сопоставитель)
Так же, как с весной, мы пишем
Controller类对于Spring 框架来说
определяетBeanDefinition
Такой же.Когда мы настраиваем конфигурацию XML и настраиваем шаблон SQL в интерфейсе, мы определяем значение домена Mybatis.
MappedStatement
Шаблон SQL соответствуетMappedStatement
Когда mybatis запускается, он анализирует заданный вами шаблон SQL в унифицированныйMappedStatement
объект, положить в контейнерConfiguration
середина. каждыйMappedStatement
Объекты имеют свойство ID. Этот идентификатор аналогичен идентификатору в нашей обычной библиотеке mysql. Это единственный способ найти шаблон SQL. Правило именования для этого идентификатора: пространство имен + имя метода
Spring BeanDefinition, Mybatis MappedStatement
2. Процесс анализа
Как и Spring, мы можем определить bean-компоненты в xml или настроить их в классах java. Есть два метода загрузки.
Вот краткое введение в запись двух методов синтаксического анализа:
1. Разбор XML:
при условииXMLConfigBuilder
компоненты, парсинг XML-файлов, этот процесс является не только процессом создания Конфигурационного контейнера, но иMappedStatement
процесс разбора.
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
Configuration config = parser.parse()
2. При использовании с Spring:
зарегистрируетMapperFactoryBean
, когда создается экземпляр MapperFactoryBean, выполнить дляafterPropertiesSet()
, курокMappedStatement
Анализ
В конечном итоге позвоню тому, который предоставил MybatisMapperAnnotationBuilder
Компонент, как видно из его названия, это в виде аннотацииMappedStatement
殊途同归
Эти два метода очень наглядны, если интересно, можете посмотреть исходный код.
3. SQL-сессия
1. Основное введение
С шаблоном SQL, передачей параметров и получением данных из базы данных это то, что делает SqlSession.
SqlSession представляет сеанс, который у нас есть с базой данных через Mybatis. С Mybatis мы просто используемSqlSession
взаимодействовать с базой данных.
Ставим id шаблона SQL, т.е.MappedStatement
Идентификатор и параметры говорят SqlSession, SqlSession найдет соответствующий шаблон в соответствии с идентификатором шаблона.MappedStatement
, затем взаимодействуйте с данными и возвращайте интерактивный результат
User user = sqlSession.selectOne("com.wqd.dao.UserMapper.selectUser", 1);
2. Классификация
- DefaultSqlSession: самая простая реализация sqlsession, все выполнение в конечном итоге будет падать на этот
DefaultSqlSession
Вкл., поток небезопасен - SqlSessionManager : потокобезопасный Sqlsession, через
ThreadLocal
Реализуйте потокобезопасность.
3.Executor
Sqlsession немного похож门面模式
, SqlSession — это фасадный интерфейс, внутренняя работа которого делегируетсяExecutor
Законченный.
public class DefaultSqlSession implements SqlSession {
private Configuration configuration;
private Executor executor;//就是他
}
мы называемSqlSession
метод, поExecutor
Законченный.
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
----交给Executor
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
4. Mapper (один и тот же пункт назначения разными путями)
1. Смысл существования
UserMapper userMapper = sqlsession.getMapper(UserMapper.class);
User user = userMapper.getById("51");
Значение Mapper состоит в том, чтобы позволить пользователям выполнять SQL, как вызов методов.
Разница в том, что вам нужно отобразить идентификатор входящего шаблона SQL и способ выполнения SQL.
User user = sqlSession.selectOne("com.wqd.dao.UserMapper.getById", 1);
2. Принцип работы
играет роль! ! ! играет роль! ! ! играет роль! ! ! Mapper реализует этот процесс через механизм прокси.
1、MapperProxyFactory
: Создайте прокси для нашего интерфейса Mapper.
При использовании только Mybatis
Mybatis会调用MapperRegistry.addMapper()
метод для интерфейса UserDao для созданияnew MapperProxyFactory(type)
При использовании с Spring,
MapperScannerRegistrar组件触发ClassPathMapperScanner组件的doScan方法
Установите для BeanClass BeanDefinition UserDao значение MapperProxyFactory и получите объект экземпляра (т. е. прокси-объект) UserDao из MapperProxyFactory при создании экземпляра SpringBean.
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
MapperProxyFactory помогает нам создать прокси-класс в памяти с помощью технологии динамического прокси JDK. (Хотя вы этого не видите, он существует)
2、MapperProxy
: улучшение при создании прокси выше
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);
}
Для нестандартных, необъектных методов (то есть наших бизнес-методов) он будет инкапсулирован вMapperMethod
, звонокMapperMethod.execute
3、MapperMethod
Когда бизнес-метод выполняется, он будет инкапсулирован какMapperMethod
, Когда MapperMethod будет выполнен, он будет вызван сноваSqlsession
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case SELECT:
...
result = sqlSession.selectOne(command.getName(), param);
...
break;
....
}
Походив неделю, я, наконец, вернулся к самому простому способу звонка.
result = sqlSession.selectOne(command.getName(), param);
User user = sqlSession.selectOne("com.wqd.dao.UserMapper.getById", 1);
Обобщить:
- Самое простое использование = sqlsession.selectOne(statement.id, параметр)
-
Mapper=Прокси-класс пользователя getById
---》
Метод MapperProxy.invoke---》
MapperMethod.execute()---》
sqlsession.selectOne(statement.id, параметры)
Очевидно, что этот обходной путь удобен для разработчиков, но создает дополнительную нагрузку на систему.
5. Кэш
Mybatis также добавил дизайн кэша.
Разделяется на кэш L1 и кэш L2.
1. Кэш 1-го уровня
Как это выглядит сначала? Это оказалась инкапсуляция HashMap
public class PerpetualCache implements Cache {
private String id;
private Map<Object, Object> cache = new HashMap<Object, Object>();
public PerpetualCache(String id) {
this.id = id;
}
}
Где это находится? в видеBaseExecutor
Атрибут существования.
public abstract class BaseExecutor implements Executor {
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.localCache = new PerpetualCache("LocalCache");
}
}
Executor
Как упоминалось выше, способность Sqlsession на самом деле делегироватьExecutor
Завершенный .Executor существует как свойство Sqlsession.
так:Жизненный цикл кэша первого уровня MyBatis соответствует циклу SqlSession..
2. Кэш L2
2.1 Основная информация
Кэш L2 имеет более сложную конструкцию, чем кеш L1.
Взяв за пример конфигурацию xml, кеш второго уровня необходимо настроить для включения и настройки для использования.namespace
середина.
<setting name="cacheEnabled" value="true"/>
<mapper namespace="mapper.StudentMapper">
<cache/>
</mapper>
тот самыйnamespace
все подMappedStatement
используют один и тот же кэш L2. Жизненный цикл кэша L2 следует жизненному циклу всего приложения, и кэш L2 также реализует то же самое.namespace
ВнизSqlSession
совместное использование данных.
После того, как конфигурация кэша L2 включена, его структура данных по умолчанию остается той же.PerpetualCache
. Это то же самое, что и кеш первого уровня.
Но при построении кеша второго уровня mybatis использует типичный шаблон проектирования装饰模式
,правильноPerpetualCache
Были внесены послойные улучшения, чтобы сделать кэш второго уровня многоуровневым украшением.PerpetualCache
, каждый декорированный слой имеет разные возможности, поэтому кеш второго уровня намного богаче, чем кеш первого уровня.
Декоративные классы это:
- LoggingCache: функция журнала, класс оформления, используемый для записи частоты попаданий в кеш, если включен режим DEBUG, будет выводиться журнал частоты попаданий.
- LruCache: реализация кэширования с использованием алгоритма Lru, удаление наименее использовавшегося ключа/значения.
- ScheduledCache: сделать его способным к очистке по времени
- BlockingCache: сделайте его блокирующим
层层装饰
private Cache setStandardDecorators(Cache cache) {
try {
MetaObject metaCache = SystemMetaObject.forObject(cache);
if (size != null && metaCache.hasSetter("size")) {
metaCache.setValue("size", size);
}
if (clearInterval != null) {
cache = new ScheduledCache(cache);
((ScheduledCache) cache).setClearInterval(clearInterval);
}
if (readWrite) {
cache = new SerializedCache(cache);
}
cache = new LoggingCache(cache);
cache = new SynchronizedCache(cache);
if (blocking) {
cache = new BlockingCache(cache);
}
return cache;
} catch (Exception e) {
throw new CacheException("Error building standard cache decorators. Cause: " + e, e);
}
}
2.2 Как это работает
До сих пор используется принцип работы кэша второго уровня.装饰模式
, но на этот раз оформленныйExecutor
. использоватьCachingExecutor
украсить выполнение SQLExecutor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);//装饰
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
При выполнении запроса сначала запрашивайте из кеша второго уровня, и переходите, когда кеша второго уровня нет.Executor
запрос
private Executor delegate;
private TransactionalCacheManager tcm = new TransactionalCacheManager();
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
....
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
вTransactionalCacheManager
Атрибуты предоставляют транзакционные возможности для кэша второго уровня.
public void commit(boolean required) throws SQLException {
delegate.commit(required);
tcm.commit();也就是事务提交时才会将数据放入到二级缓存中去
}
Суммируйте кеш следующего уровня
- Кэш L2 является многоуровневым украшением
- Кэш L2 работает, украшая общие исполнители
- Украсьте исполнителя для использования
TransactionalCacheManager
Предоставляет транзакционные возможности для кэша L2
6. Плагины
Опишите плагин mybaits одним предложением: прокси, прокси, прокси или прокси.
Принцип подключаемого модуля Mybatis также является технологией динамического прокси.
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
..
executor = new SimpleExecutor(this, transaction);
....
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
插件的入口
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
InterceptorChain
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
Возьмем, к примеру, плагин подкачки.
После того, как Executor будет создан, плагин будет выполненplugn
метод, плагинplugn
позвонюPlugin.wrap
метод, в этом методе мы видим технологию динамического прокси JDK для наших свойств. СоздайтеExecutor
Класс агента усиливается в плагине.
QueryInterceptor
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public class Plugin implements InvocationHandler {
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
}
Окончательная цепочка выполнения: Метод прокси-класса Executor -- "Метод Plugin.invoke --" Метод plugin.intercept -- "Метод класса Executor
7. Отображение результатов
Поскольку сопоставление результатов более сложное, давайте откроем другую статью для деталей.
8. Резюме
Можно сказать, что Mybatis максимально использует шаблон декоратора и динамический прокси. Очень стоит нашего исследования.
Фреймворк, оставленный пользователю, должен быть базовой единицей работы каркаса, то есть концепцией ценности домена.Пользователю нужно только определить сырье, а затем операцию черного ящика.
Например:
- Весна
BeanDefinition
- Мибатис
MappedStatement
Mybatis — это фреймворк, который стоит прочитать По сравнению с Spring, Mybatis очень подходит для использования в качестве первого фреймворка для изучения исходного кода.
Если в этой статье есть какие-либо ошибки, пожалуйста, критикуйте и советуйте, это очень ценится! Если вы считаете, что статья хорошая,поставить лайкБар
Нажмите и удерживайте, чтобы скопировать и добавить мой WeChat: wqd147拉你入伙
, поговорим об исходном коде