mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Ensure AvailabilityChangeEvent carries generics
Update `AvailabilityChangeEvent` to be a `PayloadEvent` and ensure that the `getResolvableType` method returns a generic compatible result. Prior to this commit, a ClassCastExeption would be thrown if the following event listener was declared: @EventListener void onEvent(AvailabilityChangeEvent<ReadinessState> event) { ... } Closes gh-21898
This commit is contained in:
parent
a65ca7ac2f
commit
1604545727
@ -19,6 +19,8 @@ package org.springframework.boot.availability;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.PayloadApplicationEvent;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@ -32,9 +34,7 @@ import org.springframework.util.Assert;
|
||||
* @author Phillip Webb
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public class AvailabilityChangeEvent<S extends AvailabilityState> extends ApplicationEvent {
|
||||
|
||||
private final S state;
|
||||
public class AvailabilityChangeEvent<S extends AvailabilityState> extends PayloadApplicationEvent<S> {
|
||||
|
||||
/**
|
||||
* Create a new {@link AvailabilityChangeEvent} instance.
|
||||
@ -42,9 +42,7 @@ public class AvailabilityChangeEvent<S extends AvailabilityState> extends Applic
|
||||
* @param state the availability state (never {@code null})
|
||||
*/
|
||||
public AvailabilityChangeEvent(Object source, S state) {
|
||||
super(source);
|
||||
Assert.notNull(state, "State must not be null");
|
||||
this.state = state;
|
||||
super(source, state);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,7 +50,20 @@ public class AvailabilityChangeEvent<S extends AvailabilityState> extends Applic
|
||||
* @return the availability state
|
||||
*/
|
||||
public S getState() {
|
||||
return this.state;
|
||||
return getPayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResolvableType getResolvableType() {
|
||||
return ResolvableType.forClassWithGenerics(getClass(), getStateType());
|
||||
}
|
||||
|
||||
private Class<?> getStateType() {
|
||||
S state = getState();
|
||||
if (state instanceof Enum) {
|
||||
return ((Enum<?>) state).getDeclaringClass();
|
||||
}
|
||||
return state.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,6 +21,10 @@ import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
@ -39,7 +43,7 @@ class AvailabilityChangeEventTests {
|
||||
@Test
|
||||
void createWhenStateIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new AvailabilityChangeEvent<>(this.source, null))
|
||||
.withMessage("State must not be null");
|
||||
.withMessage("Payload must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -49,6 +53,24 @@ class AvailabilityChangeEventTests {
|
||||
assertThat(event.getState()).isEqualTo(state);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResolvableType() {
|
||||
LivenessState state = LivenessState.CORRECT;
|
||||
AvailabilityChangeEvent<LivenessState> event = new AvailabilityChangeEvent<>(this.source, state);
|
||||
ResolvableType type = event.getResolvableType();
|
||||
assertThat(type.resolve()).isEqualTo(AvailabilityChangeEvent.class);
|
||||
assertThat(type.resolveGeneric()).isEqualTo(LivenessState.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResolvableTypeWhenSubclassedEnum() {
|
||||
SubClassedEnum state = SubClassedEnum.TWO;
|
||||
AvailabilityChangeEvent<SubClassedEnum> event = new AvailabilityChangeEvent<>(this.source, state);
|
||||
ResolvableType type = event.getResolvableType();
|
||||
assertThat(type.resolve()).isEqualTo(AvailabilityChangeEvent.class);
|
||||
assertThat(type.resolveGeneric()).isEqualTo(SubClassedEnum.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void publishPublishesEvent() {
|
||||
ApplicationContext context = mock(ApplicationContext.class);
|
||||
@ -61,4 +83,45 @@ class AvailabilityChangeEventTests {
|
||||
assertThat(event.getState()).isEqualTo(state);
|
||||
}
|
||||
|
||||
@Test
|
||||
void publishEvenToContextConsidersGenericType() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
|
||||
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
|
||||
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
|
||||
}
|
||||
|
||||
enum SubClassedEnum implements AvailabilityState {
|
||||
|
||||
ONE {
|
||||
|
||||
@Override
|
||||
String getDescription() {
|
||||
return "I have been overridden";
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
TWO {
|
||||
|
||||
@Override
|
||||
String getDescription() {
|
||||
return "I have aslo been overridden";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
abstract String getDescription();
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@EventListener
|
||||
void onLivenessAvailabilityChange(AvailabilityChangeEvent<LivenessState> event) {
|
||||
assertThat(event.getState()).isInstanceOf(LivenessState.class).isEqualTo(LivenessState.CORRECT);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user