在抽象超类中定义spring @EventListener

Oli*_*ich 6 java generics spring

我盯着使用spring的@EventListener注释来创建处理我的非春季特定事件的事件处理程序.最初一切都很顺利.我使用测试来验证我可以将@EventListener注释放在抽象类的方法上,一切都按预期工作.

然而,一旦我开始添加泛型,我就开始NullPointerExceptions从中获取ApplicationListenerMethodAdapter.java:337.

我已经创建了一个测试用例来说明问题.目前所有测试方法都失败,例外情况如下:

java.lang.NullPointerException
at java.lang.Class.isAssignableFrom(Native Method)
at org.springframework.context.event.ApplicationListenerMethodAdapter.getResolvableType(ApplicationListenerMethodAdapter.java:337)
at org.springframework.context.event.ApplicationListenerMethodAdapter.resolveArguments(ApplicationListenerMethodAdapter.java:161)
at org.springframework.context.event.ApplicationListenerMethodAdapter.processEvent(ApplicationListenerMethodAdapter.java:142)
at org.springframework.context.event.ApplicationListenerMethodAdapter.onApplicationEvent(ApplicationListenerMethodAdapter.java:106)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:163)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:136)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:381)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:348)
Run Code Online (Sandbox Code Playgroud)

当我将@EventListener注释向下移动到每个具体的侦听器时,异常消失,并且事物的行为与预期的一样,但是除外testSendingEventWithGenericsWithExtendedUniquePayload.

问题

Q1)使用@EventListener抽象超类的方法是一种有效的使用模式吗?我希望在那里实施共同的行为.

Q2)我读到了关于ResolvableTypeProvider在春季文档中实现我的活动的内容.我的理解是,这将允许我避免为每个有效负载类型创建许多具体的子类.这就是我试图测试的内容testSendingEventWithGenericsWithExtendedUniquePayload.我期待在这个测试中被触发的事件被处理,TestEventWithGenericsExtendedUniquePayloadListener但事实并非如此.我在这里误解了什么吗?

Spring:4.2.4.RELEASE Java:1.8.0_65

谢谢你的帮助奥利弗

测试代码

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.core.ResolvableType;
import org.springframework.core.ResolvableTypeProvider;
import org.springframework.stereotype.Component;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.Collections;
import java.util.List;
import java.util.UUID;

import static org.springframework.core.ResolvableType.*;

/**
 * @author Oliver Henlich
 */
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class EventListenerTest {
    private static final Logger log = LoggerFactory.getLogger(EventListenerTest.class);

    @Autowired
    protected transient ApplicationEventPublisher applicationEventPublisher;

    @Test
    public void testSendingEvent1() {
        log.info("testSendingEvent1");

        // this should go to TestEvent1Listener
        applicationEventPublisher.publishEvent(new TestEvent1(new UniquePayload()));

    }

    @Test
    public void testSendingEventWithGenerics() {
        log.info("testSendingEventWithGenerics");

        // this should go to TestEventWithGenericsListener
        applicationEventPublisher.publishEvent(new TestEventWithGenerics<>(new UniquePayload()));
    }

    @Test
    public void testSendingEventWithGenericsWithExtendedUniquePayload() {
        log.info("testSendingEventWithGenerics");

        // I was expecting this to go to TestEventWithGenericsExtendedUniquePayloadListener
        applicationEventPublisher.publishEvent(new TestEventWithGenerics<>(new ExtendedUniquePayload()));
    }

    @Test
    public void testSendingEvent2() {
        log.info("testSendingEvent2");

        // there is no listener for this one
        applicationEventPublisher.publishEvent(new TestEvent2(new UniquePayload()));
    }


    // LISTENERS --------------------------------------------------------------
    interface TestDataEventListener<E extends TestDataEvent> {
        @SuppressWarnings("unused")
        List<String> handleEvent(E event);
    }

    abstract static class AbstractTestDataEventListener<E extends TestDataEvent> implements TestDataEventListener<E> {
        @Override
        @EventListener
        public final List<String> handleEvent(E event) {
            return onEvent(event);
        }

        public abstract List<String> onEvent(E event);
    }

    @Component
    static final class TestEvent1Listener extends AbstractTestDataEventListener<TestEvent1> {

        @Override
        public List<String> onEvent(TestEvent1 event) {
            log.info("Listener {} handled {}", this, event);
            return Collections.emptyList();
        }
    }

    @Component
    static final class TestEventWithGenericsListener extends AbstractTestDataEventListener<TestEventWithGenerics> {

        @Override
        public List<String> onEvent(TestEventWithGenerics event) {
            log.info("Listener {} handled {}", this, event);
            return Collections.emptyList();
        }
    }

    @Component
    static final class TestEventWithGenericsExtendedUniquePayloadListener extends AbstractTestDataEventListener<TestEventWithGenerics<ExtendedUniquePayload>> {

        @Override
        public List<String> onEvent(TestEventWithGenerics<ExtendedUniquePayload> event) {
            log.info("Listener {} handled {}", this, event);
            return Collections.emptyList();
        }
    }


    // EVENTS -----------------------------------------------------------------
    interface TestDataEvent<T extends Unique> extends ResolvableTypeProvider {
        T load();
    }

    abstract static class AbstractTestDataEvent<T extends Unique> implements TestDataEvent<T> {
        protected final UUID uuid;

        private final ResolvableType resolvableType;

        public AbstractTestDataEvent(T uniqueObject) {
            uuid = uniqueObject.getUuid();

            ResolvableType temp = ResolvableType.forClass(getClass());
            if (temp.hasGenerics()) {
                temp = forClassWithGenerics(getClass(), forInstance(uniqueObject));
            }
            resolvableType = temp;
            log.info("class = {} resolvableType = {}", getClass(), resolvableType);
        }


        @Override
        public ResolvableType getResolvableType() {
            return resolvableType;
        }
    }


    static final class TestEvent1 extends AbstractTestDataEvent<UniquePayload> {
        public TestEvent1(UniquePayload uniqueObject) {
            super(uniqueObject);
        }

        @Override
        public UniquePayload load() {
            return new UniquePayload(uuid);
        }
    }

    static final class TestEvent2 extends AbstractTestDataEvent<UniquePayload> {
        public TestEvent2(UniquePayload uniqueObject) {
            super(uniqueObject);
        }

        @Override
        public UniquePayload load() {
            return new UniquePayload(uuid);
        }
    }

    static final class TestEventWithGenerics<T extends UniquePayload> extends AbstractTestDataEvent<T> {
        public TestEventWithGenerics(T uniqueObject) {
            super(uniqueObject);
        }

        @Override
        public T load() {
            return (T) new UniquePayload(uuid);
        }
    }


    static class UniquePayload implements Unique {
        private final UUID uuid;

        public UniquePayload() {
            this(UUID.randomUUID());
        }

        public UniquePayload(UUID uuid) {
            this.uuid = uuid;
        }


        @Override
        public UUID getUuid() {
            return uuid;
        }
    }

    static class ExtendedUniquePayload extends UniquePayload {

    }

    interface Unique {
        UUID getUuid();
    }

    @Configuration
    @ComponentScan(basePackageClasses = EventListenerTest.class)
    public static class ContextConfiguration {

    }


}
Run Code Online (Sandbox Code Playgroud)