Annotation-based event listeners
Add support for annotation-based event listeners. Enabled automatically
when using Java configuration or can be enabled explicitly via the
regular <context:annotation-driven/> XML element. Detect methods of
managed beans annotated with @EventListener, either directly or through
a meta-annotation.
Annotated methods must define the event type they listen to as a single
parameter argument. Events are automatically filtered out according to
the method signature. When additional runtime filtering is required, one
can specify the `condition` attribute of the annotation that defines a
SpEL expression that should match to actually invoke the method for a
particular event. The root context exposes the actual `event`
(`#root.event`) and method arguments (`#root.args`). Individual method
arguments are also exposed via either the `a` or `p` alias (`#a0` refers
to the first method argument). Finally, methods arguments are exposed via
their names if that information can be discovered.
Events can be either an ApplicationEvent or any arbitrary payload. Such
payload is wrapped automatically in a PayloadApplicationEvent and managed
explicitly internally. As a result, users can now publish and listen
for arbitrary objects.
If an annotated method has a return value, an non null result is actually
published as a new event, something like:
@EventListener
public FooEvent handle(BarEvent event) { ... }
Events can be handled in an aynchronous manner by adding `@Async` to the
event method declaration and enabling such infrastructure. Events can
also be ordered by adding an `@Order` annotation to the event method.
Issue: SPR-11622
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -55,7 +55,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
assertEquals(10, beanCount);
|
||||
assertEquals(11, beanCount);
|
||||
assertTrue(context.containsBean("serviceInvocationCounter"));
|
||||
assertTrue(context.containsBean("fooServiceImpl"));
|
||||
assertTrue(context.containsBean("stubFooDao"));
|
||||
@@ -66,6 +66,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.EVENT_LISTENER_PROCESSOR_BEAN_NAME));
|
||||
context.refresh();
|
||||
FooServiceImpl service = context.getBean("fooServiceImpl", FooServiceImpl.class);
|
||||
assertTrue(context.getDefaultListableBeanFactory().containsSingleton("myNamedComponent"));
|
||||
@@ -98,7 +99,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
assertEquals(10, beanCount);
|
||||
assertEquals(11, beanCount);
|
||||
scanner.scan(BASE_PACKAGE);
|
||||
assertTrue(context.containsBean("serviceInvocationCounter"));
|
||||
assertTrue(context.containsBean("fooServiceImpl"));
|
||||
@@ -218,11 +219,12 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, false);
|
||||
scanner.addIncludeFilter(new AnnotationTypeFilter(CustomComponent.class));
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
assertEquals(5, beanCount);
|
||||
assertEquals(6, beanCount);
|
||||
assertTrue(context.containsBean("messageBean"));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.EVENT_LISTENER_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -231,7 +233,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, false);
|
||||
scanner.addIncludeFilter(new AnnotationTypeFilter(CustomComponent.class));
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
assertEquals(5, beanCount);
|
||||
assertEquals(6, beanCount);
|
||||
assertTrue(context.containsBean("messageBean"));
|
||||
assertFalse(context.containsBean("serviceInvocationCounter"));
|
||||
assertFalse(context.containsBean("fooServiceImpl"));
|
||||
@@ -241,6 +243,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.EVENT_LISTENER_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -249,7 +252,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, true);
|
||||
scanner.addIncludeFilter(new AnnotationTypeFilter(CustomComponent.class));
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
assertEquals(11, beanCount);
|
||||
assertEquals(12, beanCount);
|
||||
assertTrue(context.containsBean("messageBean"));
|
||||
assertTrue(context.containsBean("serviceInvocationCounter"));
|
||||
assertTrue(context.containsBean("fooServiceImpl"));
|
||||
@@ -259,6 +262,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.EVENT_LISTENER_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -267,7 +271,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, true);
|
||||
scanner.addExcludeFilter(new AnnotationTypeFilter(Aspect.class));
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
assertEquals(9, beanCount);
|
||||
assertEquals(10, beanCount);
|
||||
assertFalse(context.containsBean("serviceInvocationCounter"));
|
||||
assertTrue(context.containsBean("fooServiceImpl"));
|
||||
assertTrue(context.containsBean("stubFooDao"));
|
||||
@@ -276,6 +280,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.EVENT_LISTENER_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -284,7 +289,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, true);
|
||||
scanner.addExcludeFilter(new AssignableTypeFilter(FooService.class));
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
assertEquals(9, beanCount);
|
||||
assertEquals(10, beanCount);
|
||||
assertFalse(context.containsBean("fooServiceImpl"));
|
||||
assertTrue(context.containsBean("serviceInvocationCounter"));
|
||||
assertTrue(context.containsBean("stubFooDao"));
|
||||
@@ -293,6 +298,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.EVENT_LISTENER_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -320,7 +326,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
scanner.addExcludeFilter(new AssignableTypeFilter(FooService.class));
|
||||
scanner.addExcludeFilter(new AnnotationTypeFilter(Aspect.class));
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
assertEquals(8, beanCount);
|
||||
assertEquals(9, beanCount);
|
||||
assertFalse(context.containsBean("fooServiceImpl"));
|
||||
assertFalse(context.containsBean("serviceInvocationCounter"));
|
||||
assertTrue(context.containsBean("stubFooDao"));
|
||||
@@ -329,6 +335,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.EVENT_LISTENER_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -337,7 +344,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
scanner.setBeanNameGenerator(new TestBeanNameGenerator());
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
assertEquals(10, beanCount);
|
||||
assertEquals(11, beanCount);
|
||||
assertFalse(context.containsBean("fooServiceImpl"));
|
||||
assertTrue(context.containsBean("fooService"));
|
||||
assertTrue(context.containsBean("serviceInvocationCounter"));
|
||||
@@ -347,6 +354,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
assertTrue(context.containsBean(AnnotationConfigUtils.EVENT_LISTENER_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -356,7 +364,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
GenericApplicationContext multiPackageContext = new GenericApplicationContext();
|
||||
ClassPathBeanDefinitionScanner multiPackageScanner = new ClassPathBeanDefinitionScanner(multiPackageContext);
|
||||
int singlePackageBeanCount = singlePackageScanner.scan(BASE_PACKAGE);
|
||||
assertEquals(10, singlePackageBeanCount);
|
||||
assertEquals(11, singlePackageBeanCount);
|
||||
multiPackageScanner.scan(BASE_PACKAGE, "org.springframework.dao.annotation");
|
||||
// assertTrue(multiPackageBeanCount > singlePackageBeanCount);
|
||||
}
|
||||
@@ -367,7 +375,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
int initialBeanCount = context.getBeanDefinitionCount();
|
||||
int scannedBeanCount = scanner.scan(BASE_PACKAGE);
|
||||
assertEquals(10, scannedBeanCount);
|
||||
assertEquals(11, scannedBeanCount);
|
||||
assertEquals(scannedBeanCount, context.getBeanDefinitionCount() - initialBeanCount);
|
||||
int addedBeanCount = scanner.scan("org.springframework.aop.aspectj.annotation");
|
||||
assertEquals(initialBeanCount + scannedBeanCount + addedBeanCount, context.getBeanDefinitionCount());
|
||||
@@ -380,7 +388,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
scanner.setBeanNameGenerator(new TestBeanNameGenerator());
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
assertEquals(10, beanCount);
|
||||
assertEquals(11, beanCount);
|
||||
context.refresh();
|
||||
|
||||
FooServiceImpl fooService = context.getBean("fooService", FooServiceImpl.class);
|
||||
|
||||
@@ -30,19 +30,19 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
|
||||
protected ResolvableType getGenericApplicationEventType(String fieldName) {
|
||||
try {
|
||||
return ResolvableType.forField(GenericApplicationEvents.class.getField(fieldName));
|
||||
return ResolvableType.forField(TestEvents.class.getField(fieldName));
|
||||
}
|
||||
catch (NoSuchFieldException e) {
|
||||
throw new IllegalStateException("No such field on Events '" + fieldName + "'");
|
||||
}
|
||||
}
|
||||
|
||||
protected static class GenericApplicationEvent<T>
|
||||
protected static class GenericTestEvent<T>
|
||||
extends ApplicationEvent {
|
||||
|
||||
private final T payload;
|
||||
|
||||
public GenericApplicationEvent(Object source, T payload) {
|
||||
public GenericTestEvent(Object source, T payload) {
|
||||
super(source);
|
||||
this.payload = payload;
|
||||
}
|
||||
@@ -53,76 +53,72 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
|
||||
}
|
||||
|
||||
protected static class StringEvent extends GenericApplicationEvent<String> {
|
||||
protected static class StringEvent extends GenericTestEvent<String> {
|
||||
|
||||
public StringEvent(Object source, String payload) {
|
||||
super(source, payload);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class LongEvent extends GenericApplicationEvent<Long> {
|
||||
protected static class LongEvent extends GenericTestEvent<Long> {
|
||||
|
||||
public LongEvent(Object source, Long payload) {
|
||||
super(source, payload);
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> GenericApplicationEvent<T> createGenericEvent(T payload) {
|
||||
return new GenericApplicationEvent<>(this, payload);
|
||||
protected <T> GenericTestEvent<T> createGenericTestEvent(T payload) {
|
||||
return new GenericTestEvent<>(this, payload);
|
||||
}
|
||||
|
||||
|
||||
static class GenericEventListener implements ApplicationListener<GenericApplicationEvent<?>> {
|
||||
static class GenericEventListener implements ApplicationListener<GenericTestEvent<?>> {
|
||||
@Override
|
||||
public void onApplicationEvent(GenericApplicationEvent<?> event) {
|
||||
|
||||
public void onApplicationEvent(GenericTestEvent<?> event) {
|
||||
}
|
||||
}
|
||||
|
||||
static class ObjectEventListener implements ApplicationListener<GenericApplicationEvent<Object>> {
|
||||
static class ObjectEventListener implements ApplicationListener<GenericTestEvent<Object>> {
|
||||
@Override
|
||||
public void onApplicationEvent(GenericApplicationEvent<Object> event) {
|
||||
|
||||
public void onApplicationEvent(GenericTestEvent<Object> event) {
|
||||
}
|
||||
}
|
||||
|
||||
static class UpperBoundEventListener
|
||||
implements ApplicationListener<GenericApplicationEvent<? extends RuntimeException>> {
|
||||
implements ApplicationListener<GenericTestEvent<? extends RuntimeException>> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(GenericApplicationEvent<? extends RuntimeException> event) {
|
||||
|
||||
public void onApplicationEvent(GenericTestEvent<? extends RuntimeException> event) {
|
||||
}
|
||||
}
|
||||
|
||||
static class StringEventListener implements ApplicationListener<GenericApplicationEvent<String>> {
|
||||
static class StringEventListener implements ApplicationListener<GenericTestEvent<String>> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(GenericApplicationEvent<String> event) {
|
||||
|
||||
public void onApplicationEvent(GenericTestEvent<String> event) {
|
||||
}
|
||||
}
|
||||
|
||||
static class RawApplicationListener implements ApplicationListener {
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class GenericApplicationEvents {
|
||||
static class TestEvents {
|
||||
|
||||
public GenericApplicationEvent<?> wildcardEvent;
|
||||
public ApplicationEvent applicationEvent;
|
||||
|
||||
public GenericApplicationEvent<String> stringEvent;
|
||||
public GenericTestEvent<?> wildcardEvent;
|
||||
|
||||
public GenericApplicationEvent<Long> longEvent;
|
||||
public GenericTestEvent<String> stringEvent;
|
||||
|
||||
public GenericApplicationEvent<IllegalStateException> illegalStateExceptionEvent;
|
||||
public GenericTestEvent<Long> longEvent;
|
||||
|
||||
public GenericApplicationEvent<IOException> ioExceptionEvent;
|
||||
public GenericTestEvent<IllegalStateException> illegalStateExceptionEvent;
|
||||
|
||||
public GenericTestEvent<IOException> ioExceptionEvent;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,619 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.event;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.PayloadApplicationEvent;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.context.event.test.AbstractIdentifiable;
|
||||
import org.springframework.context.event.test.AnotherTestEvent;
|
||||
import org.springframework.context.event.test.EventCollector;
|
||||
import org.springframework.context.event.test.Identifiable;
|
||||
import org.springframework.context.event.test.TestEvent;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class AnnotationDrivenEventListenerTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
private EventCollector eventCollector;
|
||||
|
||||
private CountDownLatch countDownLatch; // 1 call by default
|
||||
|
||||
@After
|
||||
public void closeContext() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleEventJavaConfig() {
|
||||
load(TestEventListener.class);
|
||||
TestEvent event = new TestEvent(this, "test");
|
||||
TestEventListener listener = this.context.getBean(TestEventListener.class);
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertEvent(listener, event);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleEventXmlConfig() {
|
||||
this.context = new ClassPathXmlApplicationContext(
|
||||
"org/springframework/context/event/simple-event-configuration.xml");
|
||||
TestEvent event = new TestEvent(this, "test");
|
||||
TestEventListener listener = this.context.getBean(TestEventListener.class);
|
||||
this.eventCollector = getEventCollector(this.context);
|
||||
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertEvent(listener, event);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metaAnnotationIsDiscovered() {
|
||||
load(MetaAnnotationListenerTestBean.class);
|
||||
|
||||
MetaAnnotationListenerTestBean bean = context.getBean(MetaAnnotationListenerTestBean.class);
|
||||
this.eventCollector.assertNoEventReceived(bean);
|
||||
|
||||
TestEvent event = new TestEvent();
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertEvent(bean, event);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextEventsAreReceived() {
|
||||
load(ContextEventListener.class);
|
||||
ContextEventListener listener = this.context.getBean(ContextEventListener.class);
|
||||
|
||||
List<Object> events = this.eventCollector.getEvents(listener);
|
||||
assertEquals("Wrong number of initial context events", 1, events.size());
|
||||
assertEquals(ContextRefreshedEvent.class, events.get(0).getClass());
|
||||
|
||||
this.context.stop();
|
||||
List<Object> eventsAfterStop = this.eventCollector.getEvents(listener);
|
||||
assertEquals("Wrong number of context events on shutdown", 2, eventsAfterStop.size());
|
||||
assertEquals(ContextStoppedEvent.class, eventsAfterStop.get(1).getClass());
|
||||
this.eventCollector.assertTotalEventsCount(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodSignatureNoEvent() {
|
||||
AnnotationConfigApplicationContext failingContext =
|
||||
new AnnotationConfigApplicationContext();
|
||||
failingContext.register(BasicConfiguration.class,
|
||||
InvalidMethodSignatureEventListener.class);
|
||||
|
||||
thrown.expect(BeanInitializationException.class);
|
||||
thrown.expectMessage(InvalidMethodSignatureEventListener.class.getName());
|
||||
thrown.expectMessage("cannotBeCalled");
|
||||
failingContext.refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleReply() {
|
||||
load(TestEventListener.class, ReplyEventListener.class);
|
||||
AnotherTestEvent event = new AnotherTestEvent(this, "dummy");
|
||||
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
|
||||
TestEventListener listener = this.context.getBean(TestEventListener.class);
|
||||
|
||||
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.eventCollector.assertNoEventReceived(replyEventListener);
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertEvent(replyEventListener, event);
|
||||
this.eventCollector.assertEvent(listener, new TestEvent(replyEventListener, event.getId(), event.msg)); // reply
|
||||
this.eventCollector.assertTotalEventsCount(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullReplyIgnored() {
|
||||
load(TestEventListener.class, ReplyEventListener.class);
|
||||
AnotherTestEvent event = new AnotherTestEvent(this, null); // No response
|
||||
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
|
||||
TestEventListener listener = this.context.getBean(TestEventListener.class);
|
||||
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.eventCollector.assertNoEventReceived(replyEventListener);
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertEvent(replyEventListener, event);
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eventListenerWorksWithInterfaceProxy() throws Exception {
|
||||
load(ProxyTestBean.class);
|
||||
|
||||
SimpleService proxy = this.context.getBean(SimpleService.class);
|
||||
assertTrue("bean should be a proxy", proxy instanceof Advised);
|
||||
this.eventCollector.assertNoEventReceived(proxy.getId());
|
||||
|
||||
TestEvent event = new TestEvent();
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertEvent(proxy.getId(), event);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodNotAvailableOnProxyIsDetected() throws Exception {
|
||||
thrown.expect(BeanInitializationException.class);
|
||||
thrown.expectMessage("handleIt2");
|
||||
load(InvalidProxyTestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eventListenerWorksWithCglibProxy() throws Exception {
|
||||
load(CglibProxyTestBean.class);
|
||||
|
||||
CglibProxyTestBean proxy = this.context.getBean(CglibProxyTestBean.class);
|
||||
assertTrue("bean should be a cglib proxy", AopUtils.isCglibProxy(proxy));
|
||||
this.eventCollector.assertNoEventReceived(proxy.getId());
|
||||
|
||||
TestEvent event = new TestEvent();
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertEvent(proxy.getId(), event);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asyncProcessingApplied() throws InterruptedException {
|
||||
loadAsync(AsyncEventListener.class);
|
||||
String threadName = Thread.currentThread().getName();
|
||||
AnotherTestEvent event = new AnotherTestEvent(this, threadName);
|
||||
AsyncEventListener listener = this.context.getBean(AsyncEventListener.class);
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
|
||||
this.context.publishEvent(event);
|
||||
|
||||
countDownLatch.await(2, TimeUnit.SECONDS);
|
||||
this.eventCollector.assertEvent(listener, event);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exceptionPropagated() {
|
||||
load(ExceptionEventListener.class);
|
||||
TestEvent event = new TestEvent(this, "fail");
|
||||
ExceptionEventListener listener = this.context.getBean(ExceptionEventListener.class);
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
try {
|
||||
this.context.publishEvent(event);
|
||||
fail("An exception should have thrown");
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
assertEquals("Wrong exception", "Test exception", e.getMessage());
|
||||
this.eventCollector.assertEvent(listener, event);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exceptionNotPropagatedWithAsync() throws InterruptedException {
|
||||
loadAsync(ExceptionEventListener.class);
|
||||
AnotherTestEvent event = new AnotherTestEvent(this, "fail");
|
||||
ExceptionEventListener listener = this.context.getBean(ExceptionEventListener.class);
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
|
||||
this.context.publishEvent(event);
|
||||
countDownLatch.await(2, TimeUnit.SECONDS);
|
||||
|
||||
this.eventCollector.assertEvent(listener, event);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerWithSimplePayload() {
|
||||
load(TestEventListener.class);
|
||||
TestEventListener listener = this.context.getBean(TestEventListener.class);
|
||||
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.context.publishEvent("test");
|
||||
this.eventCollector.assertEvent(listener, "test");
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerWithNonMatchingPayload() {
|
||||
load(TestEventListener.class);
|
||||
TestEventListener listener = this.context.getBean(TestEventListener.class);
|
||||
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.context.publishEvent(123L);
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.eventCollector.assertTotalEventsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replyWithPayload() {
|
||||
load(TestEventListener.class, ReplyEventListener.class);
|
||||
AnotherTestEvent event = new AnotherTestEvent(this, "String");
|
||||
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
|
||||
TestEventListener listener = this.context.getBean(TestEventListener.class);
|
||||
|
||||
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.eventCollector.assertNoEventReceived(replyEventListener);
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertEvent(replyEventListener, event);
|
||||
this.eventCollector.assertEvent(listener, "String"); // reply
|
||||
this.eventCollector.assertTotalEventsCount(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerWithGenericApplicationEvent() {
|
||||
load(GenericEventListener.class);
|
||||
GenericEventListener listener = this.context.getBean(GenericEventListener.class);
|
||||
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.context.publishEvent("TEST");
|
||||
this.eventCollector.assertEvent(listener, "TEST");
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void conditionMatch() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
load(ConditionalEventListener.class);
|
||||
TestEvent event = new TestEvent(this, "OK");
|
||||
TestEventListener listener = this.context.getBean(ConditionalEventListener.class);
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertEvent(listener, event);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
|
||||
this.context.publishEvent("OK");
|
||||
this.eventCollector.assertEvent(listener, event, "OK");
|
||||
this.eventCollector.assertTotalEventsCount(2);
|
||||
|
||||
this.context.publishEvent(timestamp);
|
||||
this.eventCollector.assertEvent(listener, event, "OK", timestamp);
|
||||
this.eventCollector.assertTotalEventsCount(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void conditionDoesNotMatch() {
|
||||
long maxLong = Long.MAX_VALUE;
|
||||
load(ConditionalEventListener.class);
|
||||
TestEvent event = new TestEvent(this, "KO");
|
||||
TestEventListener listener = this.context.getBean(ConditionalEventListener.class);
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.eventCollector.assertTotalEventsCount(0);
|
||||
|
||||
this.context.publishEvent("KO");
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.eventCollector.assertTotalEventsCount(0);
|
||||
|
||||
this.context.publishEvent(maxLong);
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
this.eventCollector.assertTotalEventsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderedListeners() {
|
||||
load(OrderedTestListener.class);
|
||||
OrderedTestListener listener = this.context.getBean(OrderedTestListener.class);
|
||||
|
||||
assertTrue(listener.order.isEmpty());
|
||||
this.context.publishEvent("whatever");
|
||||
assertThat(listener.order, contains("first", "second", "third"));
|
||||
}
|
||||
|
||||
private void load(Class<?>... classes) {
|
||||
List<Class<?>> allClasses = new ArrayList<>();
|
||||
allClasses.add(BasicConfiguration.class);
|
||||
allClasses.addAll(Arrays.asList(classes));
|
||||
doLoad(allClasses.toArray(new Class<?>[allClasses.size()]));
|
||||
}
|
||||
|
||||
private void loadAsync(Class<?>... classes) {
|
||||
List<Class<?>> allClasses = new ArrayList<>();
|
||||
allClasses.add(AsyncConfiguration.class);
|
||||
allClasses.addAll(Arrays.asList(classes));
|
||||
doLoad(allClasses.toArray(new Class<?>[allClasses.size()]));
|
||||
}
|
||||
|
||||
private void doLoad(Class<?>... classes) {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(classes);
|
||||
this.eventCollector = ctx.getBean(EventCollector.class);
|
||||
this.countDownLatch = ctx.getBean(CountDownLatch.class);
|
||||
this.context = ctx;
|
||||
}
|
||||
|
||||
private EventCollector getEventCollector(ConfigurableApplicationContext context) {
|
||||
return context.getBean(EventCollector.class);
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class BasicConfiguration {
|
||||
|
||||
@Bean
|
||||
public EventCollector eventCollector() {
|
||||
return new EventCollector();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CountDownLatch testCountDownLatch() {
|
||||
return new CountDownLatch(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static abstract class AbstractTestEventListener extends AbstractIdentifiable {
|
||||
|
||||
@Autowired
|
||||
private EventCollector eventCollector;
|
||||
|
||||
protected void collectEvent(Object content) {
|
||||
this.eventCollector.addEvent(this, content);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Component
|
||||
static class TestEventListener extends AbstractTestEventListener {
|
||||
|
||||
@EventListener
|
||||
public void handle(TestEvent event) {
|
||||
collectEvent(event);
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void handleString(String content) {
|
||||
collectEvent(content);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventListener
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface FooListener {
|
||||
}
|
||||
|
||||
@Component
|
||||
static class MetaAnnotationListenerTestBean extends AbstractTestEventListener {
|
||||
|
||||
@FooListener
|
||||
public void handleIt(TestEvent event) {
|
||||
collectEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
static class ContextEventListener extends AbstractTestEventListener {
|
||||
|
||||
@EventListener
|
||||
public void handleContextEvent(ApplicationContextEvent event) {
|
||||
collectEvent(event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Component
|
||||
static class InvalidMethodSignatureEventListener {
|
||||
|
||||
@EventListener
|
||||
public void cannotBeCalled(String s, Integer what) {
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
static class ReplyEventListener extends AbstractTestEventListener {
|
||||
|
||||
@EventListener
|
||||
public Object handle(AnotherTestEvent event) {
|
||||
collectEvent(event);
|
||||
if (event.msg == null) {
|
||||
return null;
|
||||
}
|
||||
else if (event.msg.equals("String")) {
|
||||
return event.msg;
|
||||
}
|
||||
else {
|
||||
return new TestEvent(this, event.getId(), event.msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Component
|
||||
static class ExceptionEventListener extends AbstractTestEventListener {
|
||||
|
||||
@Autowired
|
||||
private CountDownLatch countDownLatch;
|
||||
|
||||
@EventListener
|
||||
public void handle(TestEvent event) {
|
||||
collectEvent(event);
|
||||
if ("fail".equals(event.msg)) {
|
||||
throw new IllegalStateException("Test exception");
|
||||
}
|
||||
}
|
||||
|
||||
@EventListener
|
||||
@Async
|
||||
public void handleAsync(AnotherTestEvent event) {
|
||||
collectEvent(event);
|
||||
if ("fail".equals(event.msg)) {
|
||||
countDownLatch.countDown();
|
||||
throw new IllegalStateException("Test exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BasicConfiguration.class)
|
||||
@EnableAsync(proxyTargetClass = true)
|
||||
static class AsyncConfiguration {
|
||||
}
|
||||
|
||||
@Component
|
||||
static class AsyncEventListener extends AbstractTestEventListener {
|
||||
|
||||
@Autowired
|
||||
private CountDownLatch countDownLatch;
|
||||
|
||||
@EventListener
|
||||
@Async
|
||||
public void handleAsync(AnotherTestEvent event) {
|
||||
assertTrue(!Thread.currentThread().getName().equals(event.msg));
|
||||
collectEvent(event);
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
interface SimpleService extends Identifiable {
|
||||
|
||||
@EventListener
|
||||
void handleIt(TestEvent event);
|
||||
|
||||
}
|
||||
|
||||
@Component
|
||||
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
|
||||
static class ProxyTestBean extends AbstractIdentifiable implements SimpleService {
|
||||
|
||||
@Autowired
|
||||
private EventCollector eventCollector;
|
||||
|
||||
@Override
|
||||
public void handleIt(TestEvent event) {
|
||||
eventCollector.addEvent(this, event);
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
|
||||
static class InvalidProxyTestBean extends ProxyTestBean {
|
||||
|
||||
@EventListener // does not exist on any interface so it should fail
|
||||
public void handleIt2(TestEvent event) {
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||
static class CglibProxyTestBean extends AbstractTestEventListener {
|
||||
|
||||
@EventListener
|
||||
public void handleIt(TestEvent event) {
|
||||
collectEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
static class GenericEventListener extends AbstractTestEventListener {
|
||||
|
||||
@EventListener
|
||||
public void handleString(PayloadApplicationEvent<String> event) {
|
||||
collectEvent(event.getPayload());
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
static class ConditionalEventListener extends TestEventListener {
|
||||
|
||||
@EventListener(condition = "'OK'.equals(#root.event.msg)")
|
||||
@Override
|
||||
public void handle(TestEvent event) {
|
||||
super.handle(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventListener(condition = "'OK'.equals(#content)")
|
||||
public void handleString(String content) {
|
||||
super.handleString(content);
|
||||
}
|
||||
|
||||
@EventListener(condition = "#root.event.timestamp > #p0")
|
||||
public void handleTimestamp(Long timestamp) {
|
||||
collectEvent(timestamp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Component
|
||||
static class OrderedTestListener extends TestEventListener {
|
||||
|
||||
public final List<String> order = new ArrayList<>();
|
||||
|
||||
@EventListener
|
||||
@Order(50)
|
||||
public void handleThird(String payload) {
|
||||
order.add("third");
|
||||
}
|
||||
|
||||
@EventListener
|
||||
@Order(-50)
|
||||
public void handleFirst(String payload) {
|
||||
order.add("first");
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void handleSecond(String payload) {
|
||||
order.add("second");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -58,19 +58,19 @@ public class ApplicationContextEventTests extends AbstractApplicationEventListen
|
||||
|
||||
@Test
|
||||
public void multicastGenericEvent() {
|
||||
multicastEvent(true, StringEventListener.class, createGenericEvent("test"),
|
||||
multicastEvent(true, StringEventListener.class, createGenericTestEvent("test"),
|
||||
getGenericApplicationEventType("stringEvent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multicastGenericEventWrongType() {
|
||||
multicastEvent(false, StringEventListener.class, createGenericEvent(123L),
|
||||
multicastEvent(false, StringEventListener.class, createGenericTestEvent(123L),
|
||||
getGenericApplicationEventType("longEvent"));
|
||||
}
|
||||
|
||||
@Test // Unfortunate - this should work as well
|
||||
public void multicastGenericEventWildcardSubType() {
|
||||
multicastEvent(false, StringEventListener.class, createGenericEvent("test"),
|
||||
multicastEvent(false, StringEventListener.class, createGenericTestEvent("test"),
|
||||
getGenericApplicationEventType("wildcardEvent"));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.event;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.PayloadApplicationEvent;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ApplicationListenerMethodAdapterTests extends AbstractApplicationEventListenerTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private final SampleEvents sampleEvents = spy(new SampleEvents());
|
||||
|
||||
private final ApplicationContext context = mock(ApplicationContext.class);
|
||||
|
||||
@Test
|
||||
public void rawListener() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleRaw", ApplicationEvent.class);
|
||||
supportsEventType(true, method, getGenericApplicationEventType("applicationEvent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rawListenerWithGenericEvent() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleRaw", ApplicationEvent.class);
|
||||
supportsEventType(true, method, getGenericApplicationEventType("stringEvent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericListener() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleGenericString", GenericTestEvent.class);
|
||||
supportsEventType(true, method, getGenericApplicationEventType("stringEvent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericListenerWrongParameterizedType() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleGenericString", GenericTestEvent.class);
|
||||
supportsEventType(false, method, getGenericApplicationEventType("longEvent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerWithPayloadAndGenericInformation() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleString", String.class);
|
||||
supportsEventType(true, method, createGenericEventType(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerWithInvalidPayloadAndGenericInformation() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleString", String.class);
|
||||
supportsEventType(false, method, createGenericEventType(Integer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerWithPayloadTypeErasure() { // Always accept such event when the type is unknown
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleString", String.class);
|
||||
supportsEventType(true, method, ResolvableType.forClass(PayloadApplicationEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerWithSubTypeSeveralGenerics() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleString", String.class);
|
||||
supportsEventType(true, method, ResolvableType.forClass(PayloadTestEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerWithSubTypeSeveralGenericsResolved() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleString", String.class);
|
||||
supportsEventType(true, method, ResolvableType.forClass(PayloadStringTestEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerWithTooManyParameters() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"tooManyParameters", String.class, String.class);
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
createTestInstance(method);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerWithNoParameter() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"noParameter");
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
createTestInstance(method);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultOrder() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleGenericString", GenericTestEvent.class);
|
||||
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
|
||||
assertEquals(0, adapter.getOrder());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specifiedOrder() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleRaw", ApplicationEvent.class);
|
||||
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
|
||||
assertEquals(42, adapter.getOrder());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeListener() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleGenericString", GenericTestEvent.class);
|
||||
GenericTestEvent<String> event = createGenericTestEvent("test");
|
||||
invokeListener(method, event);
|
||||
verify(this.sampleEvents, times(1)).handleGenericString(event);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeListenerRuntimeException() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"generateRuntimeException", GenericTestEvent.class);
|
||||
GenericTestEvent<String> event = createGenericTestEvent("fail");
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("Test exception");
|
||||
thrown.expectCause(is(isNull(Throwable.class)));
|
||||
invokeListener(method, event);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeListenerCheckedException() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"generateCheckedException", GenericTestEvent.class);
|
||||
GenericTestEvent<String> event = createGenericTestEvent("fail");
|
||||
|
||||
thrown.expect(UndeclaredThrowableException.class);
|
||||
thrown.expectCause(is(instanceOf(IOException.class)));
|
||||
invokeListener(method, event);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeListenerInvalidProxy() {
|
||||
Object target = new InvalidProxyTestBean();
|
||||
ProxyFactory proxyFactory = new ProxyFactory();
|
||||
proxyFactory.setTarget(target);
|
||||
proxyFactory.addInterface(SimpleService.class);
|
||||
Object bean = proxyFactory.getProxy(getClass().getClassLoader());
|
||||
|
||||
Method method = ReflectionUtils.findMethod(InvalidProxyTestBean.class, "handleIt2", ApplicationEvent.class);
|
||||
StaticApplicationListenerMethodAdapter listener =
|
||||
new StaticApplicationListenerMethodAdapter(method, bean);
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("handleIt2");
|
||||
listener.onApplicationEvent(createGenericTestEvent("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeListenerWithPayload() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleString", String.class);
|
||||
PayloadApplicationEvent<String> event = new PayloadApplicationEvent<>(this, "test");
|
||||
invokeListener(method, event);
|
||||
verify(this.sampleEvents, times(1)).handleString("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeListenerWithPayloadWrongType() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleString", String.class);
|
||||
PayloadApplicationEvent<Long> event = new PayloadApplicationEvent<>(this, 123L);
|
||||
invokeListener(method, event);
|
||||
verify(this.sampleEvents, never()).handleString(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanInstanceRetrievedAtEveryInvocation() {
|
||||
Method method = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"handleGenericString", GenericTestEvent.class);
|
||||
when(this.context.getBean("testBean")).thenReturn(this.sampleEvents);
|
||||
ApplicationListenerMethodAdapter listener = new ApplicationListenerMethodAdapter(
|
||||
"testBean", GenericTestEvent.class, method);
|
||||
listener.init(this.context, new EventExpressionEvaluator());
|
||||
GenericTestEvent<String> event = createGenericTestEvent("test");
|
||||
|
||||
|
||||
listener.onApplicationEvent(event);
|
||||
verify(this.sampleEvents, times(1)).handleGenericString(event);
|
||||
verify(this.context, times(1)).getBean("testBean");
|
||||
|
||||
listener.onApplicationEvent(event);
|
||||
verify(this.sampleEvents, times(2)).handleGenericString(event);
|
||||
verify(this.context, times(2)).getBean("testBean");
|
||||
}
|
||||
|
||||
private void supportsEventType(boolean match, Method method, ResolvableType eventType) {
|
||||
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
|
||||
assertEquals("Wrong match for event '" + eventType + "' on " + method,
|
||||
match, adapter.supportsEventType(eventType));
|
||||
}
|
||||
|
||||
private void invokeListener(Method method, ApplicationEvent event) {
|
||||
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
|
||||
adapter.onApplicationEvent(event);
|
||||
}
|
||||
|
||||
private ApplicationListenerMethodAdapter createTestInstance(Method method) {
|
||||
return new StaticApplicationListenerMethodAdapter(method, this.sampleEvents);
|
||||
}
|
||||
|
||||
private ResolvableType createGenericEventType(Class<?> payloadType) {
|
||||
return ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, payloadType);
|
||||
}
|
||||
|
||||
private static class StaticApplicationListenerMethodAdapter
|
||||
extends ApplicationListenerMethodAdapter {
|
||||
|
||||
private final Object targetBean;
|
||||
|
||||
public StaticApplicationListenerMethodAdapter(Method method, Object targetBean) {
|
||||
super("unused", targetBean.getClass(), method);
|
||||
this.targetBean = targetBean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTargetBean() {
|
||||
return targetBean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class SampleEvents {
|
||||
|
||||
|
||||
@EventListener
|
||||
@Order(42)
|
||||
public void handleRaw(ApplicationEvent event) {
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void handleGenericString(GenericTestEvent<String> event) {
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void handleString(String payload) {
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void tooManyParameters(String event, String whatIsThis) {
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void noParameter() {
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void generateRuntimeException(GenericTestEvent<String> event) {
|
||||
if ("fail".equals(event.getPayload())) {
|
||||
throw new IllegalStateException("Test exception");
|
||||
}
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void generateCheckedException(GenericTestEvent<String> event) throws IOException {
|
||||
if ("fail".equals(event.getPayload())) {
|
||||
throw new IOException("Test exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface SimpleService {
|
||||
|
||||
void handleIt(ApplicationEvent event);
|
||||
|
||||
}
|
||||
|
||||
static class InvalidProxyTestBean implements SimpleService {
|
||||
|
||||
@Override
|
||||
public void handleIt(ApplicationEvent event) {
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void handleIt2(ApplicationEvent event) {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unused", "serial"})
|
||||
static class PayloadTestEvent<V, T> extends PayloadApplicationEvent<T> {
|
||||
|
||||
private final V something;
|
||||
|
||||
public PayloadTestEvent(Object source, T payload, V something) {
|
||||
super(source, payload);
|
||||
this.something = something;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unused", "serial"})
|
||||
static class PayloadStringTestEvent extends PayloadTestEvent<Long, String> {
|
||||
public PayloadStringTestEvent(Object source, String payload, Long something) {
|
||||
super(source, payload, something);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.TestListener;
|
||||
import org.springframework.context.event.test.TestEvent;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.tests.sample.beans.ITestBean;
|
||||
import org.springframework.tests.sample.beans.TestBean;
|
||||
@@ -116,15 +117,6 @@ public class EventPublicationInterceptorTests {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class TestEvent extends ApplicationEvent {
|
||||
|
||||
public TestEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static final class TestEventWithNoValidOneArgObjectCtor extends ApplicationEvent {
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public class GenericApplicationListenerAdapterTests extends AbstractApplicationE
|
||||
|
||||
@Test // Demonstrates we can't inject that event because the generic type is lost
|
||||
public void genericListenerStrictTypeTypeErasure() {
|
||||
GenericApplicationEvent<String> stringEvent = createGenericEvent("test");
|
||||
GenericTestEvent<String> stringEvent = createGenericTestEvent("test");
|
||||
ResolvableType eventType = ResolvableType.forType(stringEvent.getClass());
|
||||
supportsEventType(false, StringEventListener.class, eventType);
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public class GenericApplicationListenerAdapterTests extends AbstractApplicationE
|
||||
@Test // But it works if we specify the type properly
|
||||
public void genericListenerStrictTypeAndResolvableType() {
|
||||
ResolvableType eventType = ResolvableType
|
||||
.forClassWithGenerics(GenericApplicationEvent.class, String.class);
|
||||
.forClassWithGenerics(GenericTestEvent.class, String.class);
|
||||
supportsEventType(true, StringEventListener.class, eventType);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public class GenericApplicationListenerAdapterTests extends AbstractApplicationE
|
||||
|
||||
@Test
|
||||
public void genericListenerStrictTypeNotMatchTypeErasure() {
|
||||
GenericApplicationEvent<Long> longEvent = createGenericEvent(123L);
|
||||
GenericTestEvent<Long> longEvent = createGenericTestEvent(123L);
|
||||
ResolvableType eventType = ResolvableType.forType(longEvent.getClass());
|
||||
supportsEventType(false, StringEventListener.class, eventType);
|
||||
}
|
||||
@@ -118,7 +118,7 @@ public class GenericApplicationListenerAdapterTests extends AbstractApplicationE
|
||||
|
||||
@Test // Demonstrates we cant inject that event because the listener has a wildcard
|
||||
public void genericListenerWildcardTypeTypeErasure() {
|
||||
GenericApplicationEvent<String> stringEvent = createGenericEvent("test");
|
||||
GenericTestEvent<String> stringEvent = createGenericTestEvent("test");
|
||||
ResolvableType eventType = ResolvableType.forType(stringEvent.getClass());
|
||||
supportsEventType(true, GenericEventListener.class, eventType);
|
||||
}
|
||||
@@ -131,7 +131,7 @@ public class GenericApplicationListenerAdapterTests extends AbstractApplicationE
|
||||
|
||||
@Test // Demonstrates we cant inject that event because the listener has a raw type
|
||||
public void genericListenerRawTypeTypeErasure() {
|
||||
GenericApplicationEvent<String> stringEvent = createGenericEvent("test");
|
||||
GenericTestEvent<String> stringEvent = createGenericTestEvent("test");
|
||||
ResolvableType eventType = ResolvableType.forType(stringEvent.getClass());
|
||||
supportsEventType(true, RawApplicationListener.class, eventType);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.event.test;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public abstract class AbstractIdentifiable implements Identifiable {
|
||||
|
||||
private final String id;
|
||||
|
||||
public AbstractIdentifiable() {
|
||||
this.id = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
AbstractIdentifiable that = (AbstractIdentifiable) o;
|
||||
|
||||
return id.equals(that.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.event.test;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class AnotherTestEvent extends IdentifiableApplicationEvent {
|
||||
|
||||
public final String msg;
|
||||
|
||||
public AnotherTestEvent(Object source, String msg) {
|
||||
super(source);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.event.test;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test utility to collect and assert events.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Component
|
||||
public class EventCollector {
|
||||
|
||||
private final MultiValueMap<String, Object> content = new LinkedMultiValueMap<>();
|
||||
|
||||
|
||||
/**
|
||||
* Register an event for the specified listener.
|
||||
*/
|
||||
public void addEvent(Identifiable listener, Object event) {
|
||||
this.content.add(listener.getId(), event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the events that the specified listener has received. The list of events
|
||||
* is ordered according to their reception order.
|
||||
*/
|
||||
public List<Object> getEvents(Identifiable listener) {
|
||||
return this.content.get(listener.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the listener identified by the specified id has not received any event.
|
||||
*/
|
||||
public void assertNoEventReceived(String listenerId) {
|
||||
List<Object> events = content.getOrDefault(listenerId, Collections.emptyList());
|
||||
assertEquals("Expected no events but got " + events, 0, events.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the specified listener has not received any event.
|
||||
*/
|
||||
public void assertNoEventReceived(Identifiable listener) {
|
||||
assertNoEventReceived(listener.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the listener identified by the specified id has received the
|
||||
* specified events, in that specific order.
|
||||
*/
|
||||
public void assertEvent(String listenerId, Object... events) {
|
||||
List<Object> actual = content.getOrDefault(listenerId, Collections.emptyList());
|
||||
assertEquals("wrong number of events", events.length, actual.size());
|
||||
for (int i = 0; i < events.length; i++) {
|
||||
assertEquals("Wrong event at index " + i, events[i], actual.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the specified listener has received the specified events, in
|
||||
* that specific order.
|
||||
*/
|
||||
public void assertEvent(Identifiable listener, Object... events) {
|
||||
assertEvent(listener.getId(), events);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the number of events received by this instance. Checks that
|
||||
* unexpected events have not been received. If an event is handled by
|
||||
* several listeners, each instance will be registered.
|
||||
*/
|
||||
public void assertTotalEventsCount(int number) {
|
||||
int actual = 0;
|
||||
for (Map.Entry<String, List<Object>> entry : this.content.entrySet()) {
|
||||
actual += entry.getValue().size();
|
||||
}
|
||||
assertEquals("Wrong number of total events (" + this.content.size() + ") " +
|
||||
"registered listener(s)", number, actual);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.event.test;
|
||||
|
||||
/**
|
||||
* A simple marker interface used to identify an event or an event listener
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public interface Identifiable {
|
||||
|
||||
/**
|
||||
* Return a unique global id used to identify this instance.
|
||||
*/
|
||||
String getId();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.event.test;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* A basic test event that can be uniquely identified easily.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class IdentifiableApplicationEvent extends ApplicationEvent implements Identifiable {
|
||||
|
||||
private final String id;
|
||||
|
||||
protected IdentifiableApplicationEvent(Object source, String id) {
|
||||
super(source);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
protected IdentifiableApplicationEvent(Object source) {
|
||||
this(source, UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
protected IdentifiableApplicationEvent() {
|
||||
this(new Object());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
IdentifiableApplicationEvent that = (IdentifiableApplicationEvent) o;
|
||||
|
||||
return id.equals(that.id);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.event.test;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class TestEvent extends IdentifiableApplicationEvent {
|
||||
|
||||
public final String msg;
|
||||
|
||||
public TestEvent(Object source, String id, String msg) {
|
||||
super(source, id);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public TestEvent(Object source, String msg) {
|
||||
super(source);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public TestEvent(Object source) {
|
||||
this(source, "test");
|
||||
}
|
||||
|
||||
public TestEvent() {
|
||||
this(new Object());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.expression;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class MethodBasedEvaluationContextTest {
|
||||
|
||||
private final ParameterNameDiscoverer paramDiscover = new DefaultParameterNameDiscoverer();
|
||||
|
||||
@Test
|
||||
public void simpleArguments() {
|
||||
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello",
|
||||
String.class, Boolean.class);
|
||||
MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {"test", true});
|
||||
|
||||
assertEquals("test", context.lookupVariable("a0"));
|
||||
assertEquals("test", context.lookupVariable("p0"));
|
||||
assertEquals("test", context.lookupVariable("foo"));
|
||||
|
||||
assertEquals(true, context.lookupVariable("a1"));
|
||||
assertEquals(true, context.lookupVariable("p1"));
|
||||
assertEquals(true, context.lookupVariable("flag"));
|
||||
|
||||
assertNull(context.lookupVariable("a2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullArgument() {
|
||||
Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello",
|
||||
String.class, Boolean.class);
|
||||
MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, null});
|
||||
|
||||
assertNull(context.lookupVariable("a0"));
|
||||
assertNull(context.lookupVariable("p0"));
|
||||
}
|
||||
|
||||
private MethodBasedEvaluationContext createEvaluationContext(Method method, Object[] args) {
|
||||
return new MethodBasedEvaluationContext(this, method, args, this.paramDiscover);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class SampleMethods {
|
||||
|
||||
private void hello(String foo, Boolean flag) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user