Spring @Async не работает - PullRequest
41 голосов
/ 07 июля 2011

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

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

Я установил контрольные точки в AsyncExecutionInterceptor, и они никогда не получают удар.Я выполнил отладку в AsyncAnnotationBeanPostProcessor и вижу, что совет будет применен.

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

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

- = ОБНОВЛЕНИЕ = -

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

app-servlet.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"/>
    ...

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

Ответы [ 10 ]

28 голосов
/ 08 июня 2012

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

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

26 голосов
/ 06 сентября 2014

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

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

}

Теперь класс в пакете bla.package, который имеет @Async аннотированные методы, может действительно вызывать ихасинхронно.

11 голосов
/ 13 января 2015

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

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

Другая полезная информация из учебных пособий Spring https://spring.io/guides/gs/async-method/:

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

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

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

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

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

Ваш класс вызывающего:

class Test {

  @Autowired Foo foo;

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

}

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

Надеюсь, это поможет.

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

Во-первых, ваша .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" />

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

Или просто используйте значение по умолчанию:

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

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

1 голос
/ 02 мая 2018

Для работы Async необходимо 3 строки кода

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

@ Служба @EnableAsync public myClass {

@ Async public void myMethod () {

}

1 голос
/ 03 марта 2017

@ Async нельзя использовать вместе с обратными вызовами жизненного цикла, такими как @PostConstruct. Для асинхронной инициализации bean-компонентов Spring в настоящее время вы должны использовать отдельный инициализирующий bean-компонент Spring, который затем вызывает аннотированный метод @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();
  }
}

источник

1 голос
/ 05 января 2017

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

Bean 'NameOfTheBean' не может обрабатываться всеми BeanPostProcessors (например, не имеет права на авто-проксирование)

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

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

@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, который расширяет классы и переопределяет некоторые из его функций для установки или настройки bean-компонентов, участвующих в некоторых нефункциональных функциях, таких как веб-служба или защита.

В вышеупомянутом примере вы должны переопределить addInterceptors и добавленные bean-объекты-перехватчики, поэтому, если вы используете какую-то аннотацию, например @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 сделали свою работу.Функция setupInterceptors запустится, когда эта сторона закончится, и установит компоненты-перехватчики.Этот вариант использования может быть экстраполирован на такие случаи, как Security.

Выводы:

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

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

0 голосов
/ 20 февраля 2019

Попробуйте ниже: 1. В конфиге создайте бин для ThreadPoolTaskExecutor

@Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        return new ThreadPoolTaskExecutor();
    }

2. В методе обслуживания, где используется @Async, добавьте

@Async("threadPoolTaskExecutor")
    public void asyncMethod(){
    //do something
    }

Это должно заставить работать @Async.

0 голосов
/ 23 марта 2017

написать независимую конфигурацию 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;
    }
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...