Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve generics matching of ApplicationEvent subtypes with generic payload parameter [SPR-13069] #17661

Closed
spring-projects-issues opened this issue May 25, 2015 · 4 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

Oliver Drotbohm opened SPR-13069 and commented

Spring 4.2 introduces better matching of generic types for ApplicationEvent instances. The matching now requires a complete generic match for an event being delivered to an ApplicationListener. Assume the following scenario:

class EventWithPayload<T> {

  public EventWithPayload(T source) {
    super(source);
  }
}

The main purpose of a generic type introduced in an ApplicationEvent is to allow ApplicationListeners to be declared to match that particular type:

class PersonEventListener implements ApplicationListener<EventWithPayload<Person>> { … }

The delivery of instances of such an event now only works if a dedicated sub-type that fills the generic parameter is used (e.g. a new EventWithPayload<Person>(person) {} is thrown). However, if the generic type is used as is, delivery fails as the event type is basically interpreted as EventWithPayload<?> and thus (understandably) doesn't match the generics matching.

I suggest to introduce an annotation that can be used on EventWithPayload to let Spring Framework inspect the event's payload type and include that in the matching algorithm. It basically expresses: "in this event type the generic type is used to bind the payload".

@PayloadGenerics(0) // index of the generic parameter in case multiple ones are use, default to 0
class EventWithPayload<T> { … }

Alternatively, an idea might be to analyze the return type of getSource() and inspect the payload if that method has been overridden to return any of the generic parameters of the type.

class EventWithPayload<T> {

  public EventWithPayload(T source) {
    super(source);
  }

  @Override
  public T getSource() {
    return (T) super.getSource();
  }
}

Affects: 4.2 RC1

Referenced from: commits 06a0dfa, b87816e

@spring-projects-issues
Copy link
Collaborator Author

Stéphane Nicoll commented

Ollie and I are brainstorming on this. It's an interesting problem that we tried to tackle pre-RC1. Some facts:

  • Ollie wants this to work against 4.1 ideally
  • I suggested to pass the ResolvableType but that puts more pressure on the sender. It's also (IMO) the cleanest solution for this type erasure problem
  • If we introduce an annotation to describe the mapping between the generics parameter and the content of the payload, we may do that in such a way that it works for any object (ApplicationEvent and arbitrary event wrapped in PayloadApplicationEvent)

@spring-projects-issues
Copy link
Collaborator Author

Stéphane Nicoll commented

A candidate fix is available on my fork

@spring-projects-issues
Copy link
Collaborator Author

Oliver Drotbohm commented

Looks good to me!

@spring-projects-issues
Copy link
Collaborator Author

Stéphane Nicoll commented

Added ResolvableType.forInstance(Object that uses ResolvableTypeProvider behind the scenes if the instance implements it. Upgraded the event infrastructure to use that if need to be.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants