предисловие
Я давно не вел блог. Недавно я наконец-то смог устроиться и прочитать основной исходный код (главным образом потому, что мое рабочее время относительно свободно (●'◡'●)). Часть содержания этой статьи касается контейнера Spring.Заинтересованные студенты могут прочитать мою статью об анализе запуска контейнера (первая статья, которую я написал, довольно плоха, и я должен реорганизовать ее, когда у меня будет время).
Основной принцип автоматической настройки springboot
SpringBoot сначала считывает классы автоконфигурации, фильтры, прослушиватели, ApplicationContextInitializers и т. д. пути «META-INF/spring.factories» в проекте при его запуске. Автоматическая сборка mybatis представляет класс MybatisAutoConfiguration.
Прежде чем анализировать автоматическую сборку mybatis, необходимо проанализировать аннотацию @EnableAutoConfiguration. С этой аннотацией поставляется аннотация запуска SpringBoot @SpringBootApplication. Эта аннотация вводит два класса: AutoConfigurationImportSelector, AutoConfigurationPackages.Registrar.
- AutoConfigurationImportSelector: этот класс реализует интерфейс DeferredImportSelector (наследует интерфейс ImportSelector), поэтому, когда Spring обновляет контейнер, он вызывает метод selectImports этого класса для регистрации некоторых компонентов в контейнере.
Метод selectImports этого класса:
- Считайте содержимое файла "META-INF/spring-autoconfigure-metadata.properties" из проекта (классы, представленные автомонтированием).
- Получить имя класса EnableAutoConfiguration из кеша (класс автоматической настройки читается при запуске SpringBoot);
- Удалите настроенный класс игнорирования (конфигурация атрибута exclude @SpringBootApplication/@EnableAutoConfiguration)
- Используйте фильтры для фильтрации классов автоконфигурации. SpringBoot регистрирует фильтр OnClassCondition по умолчанию.В файле конфигурации spring-autoconfigure-metadata.properties вам необходимо настроить значение, соответствующее ConditionalOnClass, который является классом, который будет оцениваться классом OnClassCondition.) Этот фильтр отфильтровывает автоматически подключенные классы, в которых отсутствует требуемый класс в ClassLoader.
- AutoConfigurationPackages.Registrar: Зарегистрируйте в контейнере класс AutoConfigurationPackages.Непонятно, что делает этот класс. Официальное описание: класс для хранения пакетов автоконфигурации для дальнейшего использования (например, с помощью сканера сущностей JPA).
SpringBoot читает блок-схему класса автоматической сборки следующим образом:
Автоматическая настройка, связанная с транзакцией (JDBC)
Есть три класса, на которые необходимо обратить внимание при автоматической настройке, связанной с транзакциями, первый из них@EnableTransactionManagement
Springboot используется для запуска аннотации транзакций, а два других класса — это классы конфигурации, считываемые через spring.factories.TransactionAutoConfiguration
,DataSourceTransactionManagerAutoConfiguration
Эти классы используются для внедрения некоторых классов ключей, связанных с транзакциями, в контейнер Spring. (Разбор здесь идет в сторону JDBC, и принцип JTA аналогичен)
DataSourceTransactionManagerAutoConfiguration
DataSourceTransactionManagerAutoConfiguration Этот класс в основном внедряет в контейнер класс PlatformTransactionManager, а здесь встраивается DataSourceTransactionManager (обратите внимание, что этот класс автоматически внедряется с самым низким приоритетом, что следует учитывать, чтобы сначала можно было зарегистрировать DataSource и т. д.)
TransactionAutoConfiguration
TransactionAutoConfiguration Этот класс в основном внедряет TransactionTemplate в контейнер и поддерживает управление транзакциями Spring.Пока в контейнере есть класс PlatformTransactionManager, транзакция будет автоматически открыта. Так много руководств по управлению открытыми транзакциями Springboot нужно добавить@EnableTransactionManagement
Аннотация на самом деле полностью избыточна!Пока вы внедряете менеджер транзакций с Spring в контейнер, все будет в порядке. (jdbc-starter и jta-starter springboot помогают нам внедрять менеджеры транзакций)
EnableTransactionManagement
Этот класс использует @Import для внедрения класса TransactionManagementConfigurationSelector в контейнер, который реализует ImportSelector Интерфейс, который внедряет в контейнер классы AutoProxyRegistrar и ProxyTransactionManagementConfiguration (прокси-сервер по умолчанию). Важно обратить внимание на класс ProxyTransactionManagementConfiguration.
ProxyTransactionManagementConfiguration
ProxyTransactionManagementConfiguration реализует класс AbstractTransactionManagementConfiguration. Этот класс имеетsetConfigurers
метод, этот метод назначит возврат PlatformTransactionManager, возвращенный классом реализации TransactionManagementConfigurer, в TransactionInterceptor для управления по умолчанию диспетчерами нескольких транзакций.
ProxyTransactionManagementConfiguration, как контейнер spring, добавляет три:
- TransactionalEventListenerFactory, прослушиватель транзакции, это не имеет значения.
- AnnotationTransactionAttributeSource, который используется для разрешения аннотаций транзакций в классах или методах.
- TransactionInterceptor, этот класс является основным классом, в котором работают транзакции.Когда выполняется аспект транзакции, выполняется метод вызова этого класса.
- BeanFactoryTransactionAttributeSourceAdvisor, этот класс будет добавлен в цепочку перехватчиков aop в качестве класса управления, когда aop создаст прокси-класс для запуска обратных вызовов и выполнения транзакционных операций. Атрибут TransactionAttributeSourcePointcut класса вызывает метод match для сканирования класса и всех его методов при создании прокси-объекта, чтобы определить, требуется ли обработка класса перехватчиком. (Выполнение аоп можно посмотреть в другом моем блоге).
Анализ принципов бизнеса
В основном смотрите на метод invoke TransactionInterceptor.Как упоминалось ранее, при выполнении метода прокси-объекта все перехватчики метода будут получены для обработки. Транзакция добавляет перехватчик TransactionInterceptor.
метод invokeWithinTransaction:
Четыре шага хорошо видны на рисунке ниже.
- Открытая транзакция: createTransactionIfNecessary
- Метод выполнения: processWithInvocation
- Откат в случае ошибки: completeTransactionAfterThrowing
- Зафиксировать транзакцию: commitTransactionAfterReturning
createTransactionIfNecessary
Ключ PlatformTransactionManager.getTransaction(); Важнейшей операцией является получение соединения с базой данных, а среда jdbc использует DataSourceTransactionManager. Проще говоря, это получение соединения в соответствии с механизмом распространения транзакции, а затем установка свойств соединения в соответствии с readOnly, уровнем изоляции и т. д., а также контроль состояния транзакции.
processedWithInvocation
Выполните метод этого класса и передайте метод в вызов через Функциональный интерфейс java8.
completeTransactionAfterThrowing
doRollback DataSourceTransactionManager, наконец, вызывает connection.rollback() (если исключение Error||RuntimeException). Другие также проверяют состояние транзакции, получают точку сохранения и определяют, является ли она вложенной транзакцией.
commitTransactionAfterReturning
DoCommit DataSourceTransactionManager, который в конечном итоге вызывает connection.commit(), не будет зафиксирован, если это вложенная унаследованная транзакция.
приложение:
Параллелизм транзакций вызывает проблемы
грязное чтение
事务1 update data 1 > 2
事务2 read data 2
事务1 rollback data 2 > 1
事务1 commit
事务1 return data 2
потерянная модификация
事务1 read data 1
事务2 read data 1
事务1 update data 1 + 1 > 2
事务2 update data 1 + 1 > 2
事务1 commit
事务2 commit
неповторяемое чтение
事务1 read data 1
事务2 updata data 1 > 2
事务2 commit
事务1 read data 2
галлюцинации
事务1 read data num 2
事务2 insert delete num 2 > 1
事务2 commit
事务1 commit
事务1 return data num 2
Уровень изоляции транзакции
read uncommitted
一个事务可以读取另一个事务未提交事务的数据。
读未提交会引起脏读,不可重复读、幻读、丢失修改
read committed(Оракул по умолчанию)
一个事务可以读取到另一个事务提交的数据。
读已提交解决了脏读,但还是会引起不可重复读、幻读、丢失修改。
repeatable read(по умолчанию MySQL)
可重复读。一个事务对同一份数据读取到的相同,不在乎其他事务对数据的修改。
解决不可重复读。(实现机制是在事务A读取之后将这些数据添加行级锁,其他事务无法修改:待验证)
会产生幻读,丢失修改。
丢失修改可通过数据库互斥锁(悲观锁)来解决,
或者在字段中加入版本号(乐观锁),也可以代码中自行加锁。如果这个级别自动在查询后添加行级锁,那么其他修改无法执行,就能解决丢失修改问题。
serializable
严格的数据隔离,要求事务序列化执行,事务只能一个接一个执行。
严重影响并发能力。
解决所有并发事务引起问题。
весеннее управление транзакциями
уровень изоляции
TransactionDefinition
Интерфейс определяет пять констант, которые представляют уровень изоляции:
- TransactionDefinition.ISOLATION_DEFAULT:Используйте уровень изоляции по умолчанию для серверной базы данных, уровень изоляции REPEATABLE_READ, принятый Mysql по умолчанию, и уровень изоляции READ_COMMITTED, принятый Oracle по умолчанию.
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED:Самый низкий уровень изоляции, который позволяет читать незафиксированные изменения данных, что может привести к грязным, фантомным или неповторяемым операциям чтения.
- TransactionDefinition.ISOLATION_READ_COMMITTED:Грязные чтения могут быть предотвращены, позволяя прочитать данные, которые были совершены одновременными транзакциями, но фантом чтения или не повторяемые чтения могут произойтиTransactionDefinition.ISOLATION_REPEATABLE_READ:Несколько результатов чтения для одного и того же поля согласуются, если только данные не изменены его собственным бизнесом, это может предотвратить загрязнение и неповторяемое чтение, но иллюзия все еще возможна.
- TransactionDefinition.ISOLATION_SERIALIZABLE:Самый высокий уровень изоляции, полностью соответствующий уровню изоляции ACID. Все транзакции выполняются одна за другой, так что абсолютно исключена возможность интерференции между транзакциями, то есть этот уровень может предотвращать грязные чтения, неповторяемые чтения и фантомные чтения. Но это серьезно повлияет на производительность программы. Обычно этот уровень также не используется.
Механизм распространения транзакций
TransactionDefinition
Интерфейс определяет пять констант, которые представляют уровень изоляции:
Ситуации, поддерживающие текущую транзакцию:
-
TransactionDefinition.PROPAGATION_REQUIRED:
Если текущая транзакция существует, она добавляется к транзакции; если нет транзакции, создайте новый бизнес. -
TransactionDefinition.PROPAGATION_SUPPORTS:
Если в данный момент есть транзакция, присоединиться к транзакции; если текущей транзакции нет, продолжить работу в нетранзакционном режиме. -
TransactionDefinition.PROPAGATION_MANDATORY:
Если в данный момент есть транзакция, присоединиться к транзакции; если текущей транзакции нет, сгенерировать исключение.
Когда текущая транзакция не поддерживается:
-
TransactionDefinition.PROPAGATION_REQUIRES_NEW:
Создайте новую транзакцию и приостановите текущую транзакцию, если есть текущая транзакция. -
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
Запустить в нетранзакционном режиме, приостановить текущую транзакцию, если текущая транзакция есть. -
TransactionDefinition.PROPAGATION_NEVER:
Работает без транзакций и выдает исключение, если транзакция уже существует.
Другие случаи:
-
TransactionDefinition.PROPAGATION_NESTED:
Если в данный момент транзакция существует, транзакция создается для выполнения как вложенная транзакция текущей транзакции; если текущей транзакции нет, значение эквивалентно TransactionDefinition.PROPAGATION_REQUIRED.
Здесь следует отметить, что предыдущие шесть способов распространения транзакций введены Spring из EJB, и они имеют одну и ту же концепцию. И PROPAGATION_NESTED специфичен для Spring. Транзакция, начатая с PROPAGATION_NESTED, встроена во внешнюю транзакцию (если есть внешняя транзакция), в настоящее время встроенная транзакция не является независимой транзакцией, она зависит от существования внешней транзакции, только через фиксацию внешней транзакции, может ли внутренняя транзакция быть вызвана фиксацией транзакций, вложенные подтранзакции не могут быть зафиксированы по отдельности. Если вы знакомы с концепцией точек сохранения в JDBC, вложенные транзакции легко понять. Фактически, вложенные подтранзакции являются приложением точек сохранения. Транзакция может включать несколько точек сохранения, и каждая вложенная подтранзакция является приложением точек сохранения. , субтранзакция. Кроме того, откат внешней транзакции также вызовет откат вложенных подтранзакций.
Свойства времени ожидания транзакции
Так называемый тайм-аут транзакции относится к максимальному времени, в течение которого транзакция может быть выполнена.Если лимит времени превышен, но транзакция не завершена, транзакция будет автоматически отменена. В TransactionDefinition тайм-аут представлен значением int, единицей измерения которого являются секунды.
Свойство транзакции только для чтения
Атрибут транзакции только для чтения относится к операции только для чтения или операции чтения-записи в транзакционном ресурсе. Так называемые транзакционные ресурсы относятся к тем ресурсам, которые управляются транзакциями, например к источникам данных, ресурсам JMS и пользовательским транзакционным ресурсам. Если определено, что над транзакционными ресурсами выполняются только операции только для чтения, то мы можем пометить транзакцию как доступную только для чтения, чтобы повысить производительность обработки транзакций. В TransactionDefinition логический тип указывает, доступна ли транзакция только для чтения.
Правила отката
Эти правила определяют, какие исключения будут вызывать откат транзакции, а какие нет. По умолчанию транзакция будет откатываться только в том случае, если она сталкивается с исключением во время выполнения, и не будет откатываться, если она встречает проверенное исключение (это поведение согласуется с поведением отката EJB). Но вы можете объявить, что транзакция откатывается, когда сталкивается с определенными проверенными исключениями, как если бы это было исключение времени выполнения. Точно так же вы также можете объявить, что транзакция не будет откатываться при определенных исключениях, даже если эти исключения являются исключениями времени выполнения.
Суммировать
Spring инкапсулирует управление транзакциями и источники данных через хороший API, так что мы можем легко использовать транзакции в разных средах, но я думаю, что масштабируемость управления транзакциями Spring все еще немного плоха, например, я изначально хотел поддерживать одну и ту же транзакцию нескольких потоков, я думал, что необходимо только унифицировать сохраненные соединения каждого потока, но потом я обнаружил, что перехватчик должен быть переписан для достижения этого, а перехватчик Spring не модифицирует состояние транзакции и некоторое покрытие обработки.