Spring @Async Не Работает

An @Async метод @Service-аннотированный класс не вызывается асинхронно - он блокирует поток.

у меня есть <task: annotation-driven /> в моей конфигурации, и вызов метода поступает извне класса, поэтому прокси-сервер должен быть поражен. Когда я прохожу через код, прокси действительно попадает, но он, похоже, не приближается к классам, связанным с запуском в исполнителе задач.

Я поставил точки останова в AsyncExecutionInterceptor и они никогда не попасть. Я отладил в AsyncAnnotationBeanPostProcessor и может видеть, как Совет применяется.

служба определяется как интерфейс (с аннотированным методом @Async там для хорошей меры) с аннотированным методом реализации @Async тоже. Ни помечаются @Transactional.

есть идеи, что могло пойти не так?

- =UPDATE= -

Любопытно, что он работает только когда у меня task XML-элементы в моем приложении-сервлете.XML-файл, а не в моем app-services.xml-файл, и если я выполняю сканирование компонентов через службы оттуда тоже. Обычно у меня есть один XML-файл с только контроллерами в нем (и ограничить компонент-сканирование соответственно), а другой с сервисами в нем (опять же с компонентом-сканирование ограничено таким образом, что он не повторно сканирует контроллеры, загруженные в другой файл).

приложение-сервлет.в XML

<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:jee="http://www.springframework.org/schema/jee" 
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:webflow="http://www.springframework.org/schema/webflow-config" 
xmlns:task="http://www.springframework.org/schema/task"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/task
    http://www.springframework.org/schema/task/spring-task-3.0.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
    http://www.springframework.org/schema/jee 
    http://www.springframework.org/schema/jee/spring-jee-3.0.xsd"
>
<task:annotation-driven executor="executor" />
<task:executor id="executor" pool-size="7"/>

<!-- Enable controller annotations -->
<context:component-scan base-package="com.package.store">
    <!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> -->
</context:component-scan>

<tx:annotation-driven/>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

<mvc:annotation-driven conversion-service="conversionService" />

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
</bean>

app-services.в XML (не работает, когда указано здесь)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/task
        http://www.springframework.org/schema/task/spring-task-3.0.xsd">

    <!-- Set up Spring to scan through various packages to find annotated classes -->
    <context:component-scan base-package="com.package.store">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <task:annotation-driven executor="han" />
    <task:executor id="han" pool-size="6"/>
    ...

я упускаю что-то вопиюще очевидное в моей конфигурации, или есть какое-то тонкое взаимодействие между элементами конфигурации?

9 ответов


С помощью отличный ответ Райана Стюарта, я смог понять это (по крайней мере, для моей конкретной проблемы).

короче, контекст загружается ContextLoaderListener (Как правило, из applicationContext.xml) является родителем контекста, загружаемого DispatcherServlet (как правило, от *-servlet.xml). Если у вас есть боб с @Async метод объявлен / компонент-отсканирован в обоих контекстах, версия из дочернего контекста (DispatcherServlet) переопределит один в Родительский контекст (ContextLoaderListener). Я проверил это, исключив этот компонент из сканирования компонентов в *-servlet.xml -- теперь он работает так, как ожидалось.


для меня решением было добавить @EnableAsync на @Configuration аннотированный класс:

@Configuration
@ComponentScan("bla.package")
@EnableAsync
public class BlaConfiguration {

}

теперь класс в пакет bla.package имеющего @Async аннотированные методы действительно могут вызывать их асинхронно.


  1. попробуйте добавить proxy-target-class="true" для всех <*:annotation-driven/> элементы, которые поддерживают этот атрибут.
  2. Проверьте, аннотирован ли ваш метод с помощью @Async общественный.

ответ Иржи Выпедржика решил мою проблему. В частности,

  1. проверьте, является ли ваш метод с аннотацией @Async общедоступным.

еще одна полезная информация из Spring tutorials https://spring.io/guides/gs/async-method/:

создание локального экземпляра класса FacebookLookupService не выполняется разрешить метод findPage работать асинхронно. Он должен быть создан внутри ля @Класс конфигурации или подобран @ComponentScan.

это означает, что если у вас был статический метод Foo.bar (), вызывая его таким образом, не будет выполнять его в асинхронном режиме, даже если он был аннотирован с помощью @Async. Вам нужно будет аннотировать Foo с помощью @Component, а в вызывающем классе получить @ Autowired экземпляр Foo.

Ie, если у вас есть аннотированная панель методов в классе Foo:

@Component
class Foo { 
   @Async
   public static void bar(){ /* ... */ }

   @Async
   public void bar2(){ /* ... */ }
}

An в классе вызывающего абонента:

class Test {

  @Autowired Foo foo;

  public test(){
     Foo.bar(); // Not async
     foo.bar(); // Not async
     foo.bar2(); // Async
  }

}

изменить: Похоже, что вызов его статически также не выполняет его в async.

надеюсь, что это помогает.


сначала сделайте свой .xml конфиг выглядит так:

<task:scheduler id="myScheduler" pool-size="10" />
<task:executor id="myExecutor" pool-size="10" />
<task:annotation-driven executor="myExecutor" scheduler="myScheduler" proxy-target-class="true" />

(Да, количество планировщиков и размер пула потоков исполнителя настраиваются)

или просто используйте default:

<!-- enable task annotation to support @Async, @Scheduled, ... -->
<task:annotation-driven />

во-вторых, убедитесь, что @Async методы государственного.


я понял, следуя учебнику асинхронный метод учебник код что мой источник проблемы был: Боб с аннотированным @Async метод не был создан заворачивается в прокси. Я начал копать и понял, что было сообщение, говорящее

Bean 'NameOfTheBean' не имеет права на обработку всеми BeanPostProcessors (например: не подходит для автоматического проксирования)

вы можете ознакомиться здесь ответы об этой проблеме и ее в основном, что BeanPostProcessors требуются каждым Бобом, поэтому каждый боб, введенный здесь, и его зависимости будут исключены для последующей обработки другими BeanPostProcessors, потому что это повредило жизненный цикл бобов. Итак, определите, что такое BeanPostProcessor это вызывает это и не использовать или создавать бобы внутри него.

в моем случае у меня была такая конфигурация

@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {

    @Autowired
    private Wss4jSecurityInterceptor securityInterceptor;

    @Autowired
    private DefaultPayloadLoggingInterceptor payloadLoggingInterceptor;

    @Override
    public void addInterceptors(List<EndpointInterceptor> interceptors) {
        interceptors.add(securityInterceptor);
        interceptors.add(payloadLoggingInterceptor);
    }
}

WsConfigurerAdapter на самом деле BeanPostProcessor и вы поймете это потому что всегда есть закономерность:@Configuration это расширяет классы и переопределяет некоторые из его функций для установки или настройки компонентов, участвующих в некоторых нефункциональных функциях, таких как веб-служба или безопасность.

в вышеупомянутом примере вы должны переопределить addInterceptors и добавлены перехватчики бобов, так что если вы используете некоторые аннотации, как @Async внутри DefaultPayloadLoggingInterceptor это не будет работать. Каково решение? Получить поездку WsConfigurerAdapter начать. После копания немного я понял класс под названием PayloadRootAnnotationMethodEndpointMapping в конце был, который имел все допустимые перехватчики, поэтому я сделал это вручную, чтобы переопределить функцию.

@EnableWs
@Configuration
public class WebServiceConfig {

    @Autowired
    private Wss4jSecurityInterceptor securityInterceptor;

    @Autowired
    private DefaultPayloadLoggingInterceptor payloadLoggingInterceptor;

    @Autowired
    public void setupInterceptors(PayloadRootAnnotationMethodEndpointMapping endpointMapping) {
        EndpointInterceptor[] interceptors = {
                securityInterceptor,
                payloadLoggingInterceptor
        };

        endpointMapping.setInterceptors(interceptors);
    }
}

так это будет работать в конце концов BeanPostProcessor сделали свое дело. The setupInterceptors функция будет работать, когда эта сторона закончена и установить перехватчики бобов. Этот вариант использования может быть экстраполирован на такие случаи, как безопасность.

выводы:

  • если вы используете @Configuration, расширяющуюся из некоторого класса, который выполняет некоторые заданные функции автоматически и вы переопределяете их, вы, вероятно, находитесь внутри BeanPostProcessor, поэтому не вводите туда бобы и попробуйте использовать поведение AOP, потому что оно не будет работать, и вы увидите, что весна говорит вам об этом с вышеупомянутым сообщением в консоли. В этих случаях не используйте бобы, но объекты (используя new предложения).
  • Если вам нужно использовать бобы digg о том, какой класс несет бобы, которые вы хотите настроить в конце,@Autowired и добавить эти бобы, как я сделал до.

надеюсь, это сэкономит вам время.


@Async нельзя использовать в сочетании с обратными вызовами жизненного цикла, такими как @PostConstruct. Для асинхронной инициализации Spring beans в настоящее время необходимо использовать отдельный инициализирующий Spring bean, который вызывает аннотированный метод @Async на цели.

public class SampleBeanImpl implements SampleBean {

  @Async
  void doSomething() { … }
}


public class SampleBeanInititalizer {

  private final SampleBean bean;

  public SampleBeanInitializer(SampleBean bean) {
    this.bean = bean;
  }

  @PostConstruct
  public void initialize() {
    bean.doSomething();
  }
}

источник


напишите независимую конфигурацию Spring для асинхронного компонента.
например:

@Configuration
@ComponentScan(basePackages="xxxxxxxxxxxxxxxxxxxxx")
@EnableAsync
public class AsyncConfig {

    /**
     *  used by  asynchronous event listener.
     * @return
     */
    @Bean(name = "asynchronousListenerExecutor")
    public Executor createAsynchronousListenerExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setMaxPoolSize(100);
        executor.initialize();
        return executor;
    }
}

я преодолеваю эту проблему с этой ситуацией.


вам нужно 3 строки кода для асинхронной работы

  1. в applicationContext.в XML
  2. на уровне класса @EnableAsync
  3. @Async на уровне метода

@Service @EnableAsync public myClass {

@Async публичная пустота myMethod () {

}