We're now at a stage where we need to port a number of hints in a sustainable way and avoid having to write dedicated code for common patterns we have in the portfolio.

On such simple pattern is the following:

@EventListener
public void onContextRefresh(ContextRefreshedEvent event) { ... }

The internal implementation processing @EventListener is creating a wrapper that invokes the method via reflection so that needs an invocation hint to work in restricted environment (native image).

A more complex pattern could be

@GetMapping("/dtos/{id}")
public MyDto findById(String id) { ... }

In this case, not only the findById method is invoked by reflection but the chances are high that MyDto is going to be serialized using some sort of converter and therefore require additional hints.

Rather than having dedicated BeanRegistrationAotProcessor or BeanFactoryInitializationAotProcessor we could build a generic one that checks for the presence of a certain annotation. We could then annotate the annotations that we know require reflection so that they are processed semi-automatically.

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Reflective
public @interface EventListener { ... }

@Reflective could take the strategy to use (a bit like @Conditional does). This would help us implement a more fine-grained algorithm like the MVC use case.

Comment From: snicoll

One thing to mention is that adding this meta-annotation can be temporary. Once the underlying infrastructure is fully resolved ahead-of-time, and therefore the need for reflection for AOT-based context is no longer necessary, the meta-annotation can be simply removed.