在抽象超类中定义spring @EventListener

时间:2016-01-08 01:41:26

标签: java spring generics

我已经开始使用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)

当我将@EventListener注释向下移动到每个具体的侦听器时,异常消失,除了testSendingEventWithGenericsWithExtendedUniquePayload之外,事情的行为与预期一致。

问题

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

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

春天: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 {

    }


}

0 个答案:

没有答案