Suppose that I have multiple ExecutorService beans to apply them within @Async annotation. Here is the pools configuration.

@Configuration
@EnableAsync
class AsyncConfig {
    @Bean
    public ExecutorService fixedPool(@Value("${fixed-pool-size}") int poolSize) {
        return Executors.newFixedThreadPool(poolSize);
    }

    @Bean
    public ExecutorService cachedPool() {
        return Executors.newCachedThreadPool();
    }
}

If I want to use one of these of pools as an async executor, I have several options.

  1. Specifiying pool bean name directly
@Async("fixedPool")
public void myAsyncMethod() { ... }

In this case, I have to repeat "fixedPool" literal over and over again. Any typo would result to an error in runtime. Though I could move it to public String constant, I don't like this approach. Because constants do not provide any context. Besides, its not the object-oriented way.

  1. Declaring custom annotations for each pool
@Target({TYPE, METHOD})
@Retention(RUNTIME)
@Async("fixedPool")
public @interface AsyncFixedPool {
}
...
@Target({TYPE, METHOD})
@Retention(RUNTIME)
@Async("cachedPool")
public @interface AsyncCachedPool {
}

Now I can use either @AsyncFixedPool or @AsyncCachedPool annotation. On the other hand, I have to declare a separate annotation for each new pool that can be added lately.

And here is my proposal. It would be much convenient, If Spring allowed to specify @AliasFor enum attributes that can be converted to String. Here is my idea.

@Getter
@RequiredArgsConstructor
public enum PoolType {
    FIXED_POOL("fixedPool"),
    CACHED_POOL("cachedPool");

    private final beanName;
}

@Target({TYPE, METHOD})
@Retention(RUNTIME)
@Async
public @interface CustomAsync {
    // here we're specifying PoolTypeConverter to transform PoolType enum to String value
    @AliasFor(annotation = Async.class, attribute = "value", converter = PoolTypeConverter.class)
    PoolType value();
}

// <Input Value, Output Value>, Functional interface
public class PoolTypeConverter implements AliasForConverter<PoolType, String> {
    @Override
    public String convert(PoolType poolType) {
        return poolType.getBeanName();
    }
}

Now I can apply @CustomAsync and specify the required executor by enum.

Such feature would open so many possibilities in custom annotations reusing.

Comment From: sbrannen

Hi @SimonHarmonicMinor,

Thanks for making your first suggestion for the Spring Framework. 👍

That's certainly an interesting proposal, and it would be rather powerful.

Unfortunately, the MergedAnnotation model and annotation search algorithms in Spring are already extremely complex. Introducing additional features on top of the @AliasFor support would therefore further complicate matters, and we have no intentions to introduce additional attributes (or features) for @AliasFor.

As you discovered, the use of custom composed annotations like @AsyncFixedPool and @AsyncCachedPool is the best way to reduce code duplication and avoid magic strings in your code base.

In light of the above, I am closing this issue.