2016-03-18 5 views
1

Я сталкиваюсь с NullPointerException в моем приложении Spring при вызове метода на объекте с автоподстановкой. Класс вопрос выглядит следующим образом:Spring @Async @Autowired null только одним методом

@Component 
public class Listener { 

    @Autowired 
    TemplateService templateService; 

    @Async 
    @EventListener 
    private Future<String> listener1(Event1 event) { 
    System.out.println(templateService); 
    return new AsyncResult<>(null); 
    } 

    @Async 
    @EventListener 
    public Future<String> listener2(Event2 event) { 
    System.out.println(templateService); 
    return new AsyncResult<>(null); 
    } 
} 

Когда я публикую событие, которое вызывает listener1, значение null печатается, но когда я публикую событие, которое вызывает listener2, тем toString() метод TemplateService называется (как Я бы ожидал). Возможно, я недопонимаю какой-то аспект того, как @Async влияет на объекты @Autowired, но я не смог определить, что это будет. Я злоупотребляю @Async? Я не понимаю, как использовать объекты @Autowired в многопоточной среде?

+0

Ваш класс не реализует интерфейс, который означает, что он не может рассматриваться для сканирования пути CLASSPATH. Вы не злоупотребляете Async. – JamesENL

+0

@JamesENL Я не уверен, что вы подразумеваете под этим. ШаблонService подключен в listener2, поэтому контекст Spring должен знать класс. Какой интерфейс я бы использовал? – Christopher

+0

У вашего слушателя нет интерфейса, поэтому Spring не знает об этом как о классе и не может создать прокси-сервер, который будет использоваться для создания методов Async. – JamesENL

ответ

2

Изменить видимость вашего метода listener1 как минимум на защиту (видимость пакета, защита или публикация). Это связано с тем, что Spring создает прокси-сервер, который является подклассом вашего компонента. Он переопределяет ваши аннотированные методы @Async, чтобы добавить новую логику для выполнения кода в отдельном потоке. Однако, поскольку он использует наследование, он может только переопределять методы, которые видны подклассу.

Это объясняет, почему метод listener2 является общедоступным.

Измените метод

@Async 
    @EventListener 
    public Future<String> listener1(Event1 event) { 
    System.out.println(templateService); 
    return new AsyncResult<>(null); 
    } 
1

Spring нужен интерфейс для создания класса прокси. Именно этот прокси-класс получает вызов каждый раз, когда вы вызываете метод, и именно благодаря этому методу происходит все асинхронное выполнение. Без интерфейса Spring не может автоматически выполнять аудит, сканировать или делать методы асинхронно.

public interface Listener { 

    public Future<String> listener1(Event1 event); 
    public Future<String> listener2(Event2 event); 
} 

@Component 
public class ListenerImpl { 

    @Autowired 
    private TemplateService templateService; 

    @Async 
    @Override 
    public Future<String> listener1(Event1 event) { 
     System.out.println(templateService); 
     return new AsyncResult<>(null); 
    } 

    @Async 
    @Override 
    public Future<String> listener2(Event2 event) { 
     System.out.println(templateService); 
     return new AsyncResult<>(null); 
    } 
} 

Следует также отметить, что Spring не может запускать частные методы асинхронно.

+0

Не требуется, чтобы компонент реализовал интерфейс, чтобы Spring мог проксировать его. Рекомендованная практика, но Spring все еще может использовать прокси-сервер класса CGLIB, если компонент не реализует хотя бы один интерфейс. Однако вы находитесь в точке зрения видимости метода. Метод @Async должен быть видимым для подклассов, чтобы Spring мог переопределить его в прокси-классе. Чтобы перехватить ваш метод для запуска в отдельном потоке, Spring должен переопределить ваш метод, чтобы переплетаться в новом поведении –