12

I defined a class with annotation Configuration

    @Configuration
    @AutoConfigureAfter(EndpointAutoConfiguration.class)
    public class EndpointConfiguration {
        @Resource
        private MetricsEndpoint metricsEndpoint;

        @Bean
        public MetricsFormatEndpoint metricsFormatEndpoint() {
            return new MetricsFormatEndpoint(metricsEndpoint);
        }
    }

the MetricsFormatEndpoint works well.

but I use the annotation conditionalOnBean, it doesn't work at all.

    @Bean
    @ConditionalOnBean(MetricsEndpoint.class)
    public MetricsFormatEndpoint metricsFormatEndpoint() {
        return new MetricsFormatEndpoint(metricsEndpoint);
    }

see the localhost:8080/beans,the spring applicationContext has the bean 'metricsEndpoint',

    {"bean":"metricsEndpoint","scope":"singleton",
     "type":"org.springframework.boot.actuate.endpoint.MetricsEndpoint",
     "resource":"class path resource 
    [org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.class]",
    "dependencies":[]}

I read the document of the annotation @ConditionalOnBean, it says The class type of bean that should be checked. The condition matches when any of the classes specified is contained in the {@link ApplicationContext}.

who can tell me why

ederribeiro
  • 388
  • 1
  • 3
  • 15
zhangzhide
  • 145
  • 1
  • 2
  • 8

2 Answers2

24

The javadoc for @ConditionalOnBean describes it as:

Conditional that only matches when the specified bean classes and/or names are already contained in the BeanFactory.

In this case, the key part is "already contained in the BeanFactory". Your own configuration classes are considered before any auto-configuration classes. This means that the auto-configuration of the MetricsEndpoint bean hasn't happened by the time that your own configuration is checking for its existence and, as a result, your MetricsFormatEndpoint bean isn't created.

One approach to take would be to create your own auto-configuration class for your MetricsFormatEndpoint bean and annotate it with @AutoConfigureAfter(EndpointAutoConfiguration.class). That will ensure that its conditions are evaluated after the MetricsEndpoint bean has been defined.

Andy Wilkinson
  • 108,729
  • 24
  • 257
  • 242
  • 2
    i also try this,@AutoConfigureAfter(EndpointAutoConfiguration.class) ,it doesn't work either. – zhangzhide Aug 05 '15 at 10:09
  • `@AutoConfigureAfter` will only work on an auto-configuration class. Did you create your own as I suggested above? – Andy Wilkinson Aug 05 '15 at 12:18
  • i add the annotation @AutoConfigureAfter(EndpointAutoConfiguration.class) on the class EndpointConfiguration,it still doesn't work. – zhangzhide Aug 06 '15 at 03:00
  • 3
    Could you please provide a concrete example of what you describe. I added the FlywayAutoConfiguration class to the spring.factories so the flyway migration is ran before my configuration class but it doesn't when I use AutoConfigureAfter. I have to autowire FlywayMigrationInitializer to ensure it does which I don't like before I do not actually use the bean. – Olayinka Mar 16 '17 at 13:57
  • 2
    @AndyWilkinson "Your own configuration classes are considered before any auto-configuration classes''. But then, how is it possible we can inject starter beans into our app's configuration class? – Whimusical Feb 16 '19 at 01:54
  • 2
    @Whimusical The answer is in the context of defining bean, not dependency injection. When beans are being defined, your own configuration classes are considered before any auto-configuration classes. When beans are being created and dependencies are being injected, all of the defined beans are considered equally. This allows a bean from auto-configuration to be injected into a an app's own bean or configuration class. – Andy Wilkinson Feb 18 '19 at 08:51
  • @AndyWilkinson Ahh nice, it makes all sense. Thanks – Whimusical Feb 18 '19 at 16:12
  • If you are trying `@AutoConfigureAfter(EndpointAutoConfiguration.class)` and it does not work, that's because at least in the recent versions, this auto-configuration is not tied to any specific endpoint auto-configurations and does not guarantee that endpoints will be auto-configured after it's done. I faced this problem with `StartupEndpoint` and had to add specifically `@AutoConfigureAfter(StartupEndpointAutoConfiguration.class)` to get it going. And if you have multiple endpoints, yeah, there will be a long list of configs in there. – eduard.dudar Apr 29 '21 at 01:15
0

ConditionalOnClass worked as well.

Javadoc said that AutoConfigureAfter should be applied after other specified auto-configuration classes.

And ConditionalOnClass matches when the specified classes are on the classpath. I think it's properer

yiming xie
  • 31
  • 5