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