Асинхронный метод Spring вызывается из другого асинхронного метода

Я использую Spring 4 и заметил странное поведение... если я несколько раз вызываю асинхронный метод из обычного метода экземпляра, то все они вызываются в разных потоках и заканчиваются в случайное время. Но если я несколько раз вызываю асинхронный метод из другого асинхронного метода, они заканчиваются по порядку. У меня есть что-то вроде этого:

@Async
public void nonAsyncMethod() {
  for (int i = 0; i < 30; i++) {
     asyncMethod();
  }
}

@Async
public void asyncMethod() {
   ... something here
}

Я использую асинхронный исполнитель по умолчанию. Должен ли я использовать другой? Однако этот исполнитель не использует повторно ни один поток и каждый раз запускает новый, так что все должно быть в порядке... Может ли это быть просто совпадением? Но я пробовал более 10 раз, и если я вернусь к неасинхронному для первого метода, они закончатся случайным образом.


person spauny    schedule 22.07.2014    source источник
comment
что, если первый асинхронный метод вызывает неасинхронный метод, который вызывает второй асинхронный метод (несколько раз)?   -  person ZhongYu    schedule 23.07.2014


Ответы (1)


То, что вы описываете, является классической ошибкой Spring AOP.

Короче говоря, чтобы Spring мог обеспечить асинхронное поведение, ему необходимо создать прокси для вашего класса во время выполнения. Затем прокси делает все, что ему нужно, до и/или после вызова вашего кода. Но в вашем случае механизм прокси не применяется для второго метода.

Когда bean-компонент вашего класса внедряется через Spring в какой-либо другой компонент, Spring вместо этого действительно внедряет прокси. Поэтому вызывается соответствующий метод прокси. Однако, когда вы вызываете метод внутри класса, ограничения Spring AOP означают, что прокси никогда не вступает в игру, а вместо этого вызывается обычный метод — без дополнительных функций.

Вот почему asyncMethod всегда выполняется в том же потоке, что и другой метод того же класса, который его вызвал.

Ознакомьтесь с этой отличной записью в блоге, а также эта часть документации Spring.

Есть несколько способов обойти эту проблему (см. это), которые не не требует от вас рефакторинга вашего кода, но если вы хотите, чтобы асинхронность работала с обоими методами, несмотря ни на что, самое простое, что нужно сделать, это реорганизовать второй метод в другой класс.

person geoand    schedule 23.07.2014
comment
@KarthikR Рад, что вам понравилось! Спасибо! - person geoand; 08.09.2015
comment
Это отличный ответ! Любые советы, чтобы легко преодолеть? - person Amanuel Nega; 08.01.2017
comment
@AmanuelNega Самым простым решением обычно является рефакторинг метода в другой класс (который, конечно, также должен быть bean-компонентом Spring) - person geoand; 09.01.2017