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.
- 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.
- 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.