Используется ли атрибут Spring @Transactional для частного метода?
Если у меня есть @Transactional -нотация частного метода в Spring bean, имеет ли аннотация какое-либо действие?
Если аннотация @Transactional
находится в открытом методе, она работает и открывает транзакцию.
public class Bean {
public void doStuff() {
doPrivateStuff();
}
@Transactional
private void doPrivateStuff() {
}
}
...
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
Ответы
Ответ 1
Вопрос не является частным или публичным, возникает вопрос: как он вызывается и какая реализация АОП вы используете?
Если вы используете (по умолчанию) Spring прокси-сервер AOP, то все функции AOP, предоставляемые Spring (например, @Transational
), будут учитываться только в том случае, если вызов проходит через прокси-сервер. - Обычно это происходит, если вызываемый метод вызывается из другого bean.
Это имеет два значения:
- Поскольку частные методы не должны вызываться из другого bean (исключение - это отражение), их аннотация
@Transactional
не учитывается.
- Если этот метод является общедоступным, но он вызывается из того же bean, он не будет учитываться (этот оператор является правильным только в том случае, если используется (по умолчанию) Spring используется прокси-сервер AOP).
@See Spring Ссылка: Глава 9.6 9.6 Механизмы проксирования
ИМХО, вы должны использовать режим aspectJ, а не Spring Proxies, который преодолеет проблему. Аспекты AspectJ Transactional Aspects сплетены даже в частные методы (проверены на Spring 3.0).
Ответ 2
Ответ на ваш вопрос: no - @Transactional
не будет иметь эффекта, если будет использоваться для аннотирования частных методов. Прокси-генератор будет игнорировать их.
Это описано в Spring Глава 10.5.6:
Видимость метода и @Transactional
При использовании прокси следует применять только аннотация @Transactional
к методам с общественной видимостью. Если вы аннотируете защищенные, частные или методы, видимые в пакете, с @Transactional
аннотация, без ошибок, но аннотированный метод не отображает сконфигурированные транзакционные настройки. Рассмотрим использование AspectJ (см. ниже), если вам нужно для аннотирования непубличных методов.
Ответ 3
По умолчанию атрибут @Transactional
работает только при вызове аннотированного метода в ссылке, полученной из applicationContext.
public class Bean {
public void doStuff() {
doTransactionStuff();
}
@Transactional
public void doTransactionStuff() {
}
}
Откроется транзакция:
Bean bean = (Bean)appContext.getBean("bean");
bean.doTransactionStuff();
Это не будет:
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
Spring Справка: Использование @Transactional
Примечание. В режиме прокси (который является значением по умолчанию) будут перехвачены только "внешние" вызовы методов, поступающие через прокси. Это означает, что "self-invocation", то есть метод внутри целевого объекта, вызывающий какой-либо другой метод целевого объекта, не приведет к фактической транзакции во время выполнения, даже если вызываемый метод помечен @Transactional
!
Рассмотрите возможность использования режима AspectJ (см. ниже), если вы ожидаете, что самозавершения будут также обернуты транзакциями. В этом случае в первую очередь не будет прокси-сервера; вместо этого целевой класс будет "переплетаться" (т.е. его байтовый код будет изменен), чтобы превратить @Transactional
в поведение во время выполнения любого метода.
Ответ 4
Да, можно использовать @Transactional для частных методов, но, как говорили другие, это не сработает. Вам нужно использовать AspectJ. Мне потребовалось некоторое время, чтобы понять, как заставить его работать. Я поделюсь своими результатами.
Я предпочел использовать плетение во время компиляции вместо ткачества с загрузкой, потому что я думаю, что это лучший вариант. Кроме того, я использую Java 8, поэтому вам может потребоваться настроить некоторые параметры.
Сначала добавьте зависимость для aspectjrt.
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
Затем добавьте плагин AspectJ, чтобы выполнить фактическое перекодирование байт-кода в Maven (это может быть не минимальный пример).
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.8</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
Наконец добавьте это в свой класс конфигурации
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
Теперь вы можете использовать @Transactional для частных методов.
Остерегайтесь такого подхода: вам нужно настроить вашу среду IDE, чтобы быть в курсе AspectJ, иначе, если вы запустите приложение через Eclipse, например, оно может не работать. Удостоверьтесь, что вы тестируете прямую сборку Maven как проверку работоспособности.
Ответ 5
Ответ - нет. См. Spring Справка: Использование @Transactional
Аннотация @Transactional
может быть помещена перед определением интерфейса, методом на интерфейсе, определением класса или общедоступным методом в классе
Ответ 6
Весенние Документы объясняют это
В режиме прокси (который используется по умолчанию) перехватываются только внешние вызовы методов, поступающие через прокси. Это означает, что самовывоз, по сути, метод в целевом объекте, вызывающий другой метод целевого объекта, не приведет к реальной транзакции во время выполнения, даже если вызванный метод помечен @Transactional.
Подумайте об использовании режима AspectJ (см. Атрибут mode в таблице ниже), если вы ожидаете, что самовыводы также будут обернуты транзакциями. В этом случае, во-первых, не будет прокси; вместо этого целевой класс будет соткан (то есть его байтовый код будет изменен), чтобы превратить @Transactional в поведение во время выполнения любого метода.
Другим способом является пользователь BeanSelfAware
Ответ 7
Если вам нужно обернуть закрытый метод внутри транзакции и не хотите использовать aspectj, вы можете использовать TransactionTemplate.
@Service
public class MyService {
@Autowired
private TransactionTemplate transactionTemplate;
private void process(){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
processInTransaction();
}
});
}
private void processInTransaction(){
//...
}
}
Ответ 8
Так же, как @loonis предложил использовать TransactionTemplate, можно использовать этот вспомогательный компонент (Kotlin):
@Component
class TransactionalUtils {
/**
* Execute any [block] of code (even private methods)
* as if it was effectively [Transactional]
*/
@Transactional
fun <R> executeAsTransactional(block: () -> R): R {
return block()
}
}
Использование:
@Service
class SomeService(private val transactionalUtils: TransactionalUtils) {
fun foo() {
transactionalUtils.executeAsTransactional { transactionalFoo() }
}
private fun transactionalFoo() {
println("This method is executed within transaction")
}
}
Не знаю, будет ли TransactionTemplate
повторно использовать существующую транзакцию или нет, но этот код определенно делает.