DATAREST-970 - AnnotationEventHandlerInvoker now considers order of event handler methods.
We now make sure that an @Order annotation on annotated event handler methods are considered and the methods are invoked in the defined order. Non-annotation-based event handlers don't suffer from the same problem as they're ApplicationListener instances directly so that the container will enforce the correct ordering in case @Order is used or Ordered is implemented. Some cleanup in EventHandlerMethod. Original pull request: #248.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2017 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.
|
||||
@@ -15,9 +15,14 @@
|
||||
*/
|
||||
package org.springframework.data.rest.core.event;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -26,6 +31,7 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.rest.core.annotation.HandleAfterCreate;
|
||||
import org.springframework.data.rest.core.annotation.HandleAfterDelete;
|
||||
@@ -165,37 +171,50 @@ public class AnnotatedEventHandlerInvoker implements ApplicationListener<Reposit
|
||||
throw new IllegalStateException(String.format(PARAMETER_MISSING, method));
|
||||
}
|
||||
|
||||
EventHandlerMethod handlerMethod = new EventHandlerMethod(parameterTypes[0], handler, method);
|
||||
EventHandlerMethod handlerMethod = EventHandlerMethod.of(parameterTypes[0], handler, method);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Annotated handler method found: {}", handlerMethod);
|
||||
}
|
||||
|
||||
handlerMethods.add(eventType, handlerMethod);
|
||||
List<EventHandlerMethod> events = handlerMethods.get(eventType);
|
||||
|
||||
if (events == null) {
|
||||
events = new ArrayList<EventHandlerMethod>();
|
||||
}
|
||||
|
||||
if (events.isEmpty()) {
|
||||
handlerMethods.add(eventType, handlerMethod);
|
||||
return;
|
||||
}
|
||||
|
||||
events.add(handlerMethod);
|
||||
Collections.sort(events);
|
||||
handlerMethods.put(eventType, events);
|
||||
}
|
||||
|
||||
static class EventHandlerMethod {
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@RequiredArgsConstructor
|
||||
static class EventHandlerMethod implements Comparable<EventHandlerMethod> {
|
||||
|
||||
final Class<?> targetType;
|
||||
final Method method;
|
||||
final Object handler;
|
||||
|
||||
private EventHandlerMethod(Class<?> targetType, Object handler, Method method) {
|
||||
public static EventHandlerMethod of(Class<?> targetType, Object handler, Method method) {
|
||||
|
||||
this.targetType = targetType;
|
||||
this.method = method;
|
||||
this.handler = handler;
|
||||
|
||||
ReflectionUtils.makeAccessible(this.method);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return new EventHandlerMethod(targetType, method, handler);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
* @see java.lang.Comparable#compareTo(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("EventHandlerMethod{ targetType=%s, method=%s, handler=%s }", targetType, method, handler);
|
||||
public int compareTo(EventHandlerMethod o) {
|
||||
return AnnotationAwareOrderComparator.INSTANCE.compare(this.method, o.method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.data.rest.core.annotation.HandleBeforeCreate;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
|
||||
import org.springframework.data.rest.core.domain.Person;
|
||||
@@ -32,6 +33,7 @@ import org.springframework.util.MultiValueMap;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Fabian Trampusch
|
||||
* @author Joseph Valerio
|
||||
*/
|
||||
public class AnnotatedEventHandlerInvokerUnitTests {
|
||||
|
||||
@@ -71,6 +73,24 @@ public class AnnotatedEventHandlerInvokerUnitTests {
|
||||
assertThat(sampleHandler.wasCalled, is(true));
|
||||
}
|
||||
|
||||
@Test // DATAREST-970
|
||||
public void invokesEventHandlerInOrderMethods() {
|
||||
|
||||
SampleOrderEventHandler1 orderHandler1 = new SampleOrderEventHandler1();
|
||||
SampleOrderEventHandler2 orderHandler2 = new SampleOrderEventHandler2();
|
||||
|
||||
AnnotatedEventHandlerInvoker invoker = new AnnotatedEventHandlerInvoker();
|
||||
invoker.postProcessAfterInitialization(orderHandler1, "orderHandler1");
|
||||
invoker.postProcessAfterInitialization(orderHandler2, "orderHandler2");
|
||||
|
||||
invoker.onApplicationEvent(new BeforeCreateEvent(new Person("Dave", "Matthews")));
|
||||
|
||||
assertThat(orderHandler1.wasCalled, is(true));
|
||||
assertThat(orderHandler2.wasCalled, is(true));
|
||||
|
||||
assertThat(orderHandler1.timestamp, is(greaterThan(orderHandler2.timestamp)));
|
||||
}
|
||||
|
||||
@RepositoryEventHandler
|
||||
static class Sample {
|
||||
|
||||
@@ -88,4 +108,32 @@ public class AnnotatedEventHandlerInvokerUnitTests {
|
||||
wasCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@RepositoryEventHandler
|
||||
static class SampleOrderEventHandler1 {
|
||||
|
||||
boolean wasCalled = false;
|
||||
long timestamp;
|
||||
|
||||
@Order(2)
|
||||
@HandleBeforeCreate
|
||||
private void method(Person sample) {
|
||||
wasCalled = true;
|
||||
timestamp = System.nanoTime();
|
||||
}
|
||||
}
|
||||
|
||||
@RepositoryEventHandler
|
||||
static class SampleOrderEventHandler2 {
|
||||
|
||||
boolean wasCalled = false;
|
||||
long timestamp;
|
||||
|
||||
@Order(1)
|
||||
@HandleBeforeCreate
|
||||
private void method(Person sample) {
|
||||
wasCalled = true;
|
||||
timestamp = System.nanoTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user