Using a custom ThreadPoolTaskExecutor bean I'd expect it to be picked up by TaskExecutorMetricsAutoConfiguration to provide me with the metrics.

But when using the spring.main.lazy-initialization=true it doesn't bind the metrics. Also marking the bean with @Lazy(false) doesn't help.

Using Spring Boot 3.1.4 (but same issue on 3.0.x)

Code to reproduce:

@SpringBootApplication
public class DemoApplication {
    @Bean(name = "custom")
    ThreadPoolTaskExecutor webhookTaskExecutor(TaskExecutorBuilder builder) {
        builder.awaitTermination(false);
        return builder.build();
    }
    @Bean
    ApplicationRunner applicationRunner(ThreadPoolTaskExecutor executor) {
        return args -> {
            executor.submit(() -> System.out.println("Hello World")).get();
        };
    }
    public static void main(String[] args) {
        var app = new SpringApplication(DemoApplication.class);
        app.setDefaultProperties(Map.of("spring.main.lazy-initialization","true"));
        var ctx = app.run(args);

        System.out.println(ctx.getBean(MeterRegistry.class).get("executor.completed").tags("name", "custom").meters().size());
    }
}

It fails with

Exception in thread "main" io.micrometer.core.instrument.search.MeterNotFoundException: Unable to find a meter that matches all the requirements at once. Here's what was found:
   FAIL: No meter with name 'executor.completed' was found.
   FAIL: No meters have the required tag 'name'.
    at io.micrometer.core.instrument.search.MeterNotFoundException$FromRequiredSearch.build(MeterNotFoundException.java:245)
    at io.micrometer.core.instrument.search.MeterNotFoundException$FromRequiredSearch.access$100(MeterNotFoundException.java:49)
    at io.micrometer.core.instrument.search.MeterNotFoundException.forSearch(MeterNotFoundException.java:46)
    at io.micrometer.core.instrument.search.RequiredSearch.meters(RequiredSearch.java:220)
    at com.example.demo.DemoApplication.main(DemoApplication.java:37)

Comment From: wilkinsona

Thanks for the report. As far as I can tell, this isn't specific to custom executors and affects the auto-configured executor as well.

You can work around the problem by excluding TaskExecutorMetricsAutoConfiguration from lazy initialization:

@Bean
static LazyInitializationExcludeFilter eagerTaskExecutorMetrics() {
    return LazyInitializationExcludeFilter.forBeanTypes(TaskExecutorMetricsAutoConfiguration.class);
}