Подтвердить что ты не робот

Spring @Async не работает

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

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

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

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

Любые идеи, что могло пойти не так?

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

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

приложение-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"/>
    ...

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

}

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

Ответ 3

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

Ответ 4

Jiří Vypědřík ответ решил мою проблему. В частности,

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

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

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

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

Т.е., если у вас есть аннотированная панель методов в классе 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.

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

Ответ 5

Сначала создайте конфигурацию .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 общедоступны.

Ответ 6

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

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

Вы можете увидеть здесь ответы об этой проблеме и ее в основном, что BeanPostProcessors требуются каждым Bean, поэтому каждый bean, введенный здесь и его зависимостями, будет исключается для обработки позже другими BeanPostProcessors, потому что это испортило жизненный цикл beans. Поэтому укажите, что является BeanPostProcessor, которое вызывает это и не использует или не создает beans внутри него.

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

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

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

Выводы:

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

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

Ответ 7

@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();
  }
}

источник

Ответ 8

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

@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;
    }
}

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