Allow @ConditionalOnEnabledEndpoint to be used on any component
Closes gh-14787
This commit is contained in:
parent
21ebb94d49
commit
861587ec78
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -23,6 +23,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
|
|
@ -31,6 +32,60 @@ import org.springframework.core.env.Environment;
|
|||
* according to the endpoints specific {@link Environment} property, falling back to
|
||||
* {@code management.endpoints.enabled-by-default} or failing that
|
||||
* {@link Endpoint#enableByDefault()}.
|
||||
* <p>
|
||||
* When placed on a {@code @Bean} method, the endpoint defaults to the return type of the
|
||||
* factory method:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class MyConfiguration {
|
||||
*
|
||||
* @ConditionalOnEnableEndpoint
|
||||
* @Bean
|
||||
* public MyEndpoint myEndpoint() {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* It is also possible to use the same mechanism for extensions:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class MyConfiguration {
|
||||
*
|
||||
* @ConditionalOnEnableEndpoint
|
||||
* @Bean
|
||||
* public MyEndpointWebExtension myEndpointWebExtension() {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* In the sample above, {@code MyEndpointWebExtension} will be created if the endpoint is
|
||||
* enabled as defined by the rules above. {@code MyEndpointWebExtension} must be a regular
|
||||
* extension that refers to an endpoint, something like:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @EndpointWebExtension(endpoint = MyEndpoint.class)
|
||||
* public class MyEndpointWebExtension {
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Alternatively, the target endpoint can be manually specified for components that should
|
||||
* only be created when a given endpoint is enabled:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class MyConfiguration {
|
||||
*
|
||||
* @ConditionalOnEnableEndpoint(endpoint = MyEndpoint.class)
|
||||
* @Bean
|
||||
* public MyComponent myComponent() {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
|
|
@ -42,4 +97,12 @@ import org.springframework.core.env.Environment;
|
|||
@Conditional(OnEnabledEndpointCondition.class)
|
||||
public @interface ConditionalOnEnabledEndpoint {
|
||||
|
||||
/**
|
||||
* The endpoint type that should be checked. Inferred when the return type of the
|
||||
* {@code @Bean} method is either an {@link Endpoint} or an {@link EndpointExtension}.
|
||||
* @return the endpoint type to check
|
||||
* @since 2.0.6
|
||||
*/
|
||||
Class<?> endpoint() default Void.class;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.condition;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
|
|
@ -92,16 +93,23 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
|
|||
metadata instanceof MethodMetadata
|
||||
&& metadata.isAnnotated(Bean.class.getName()),
|
||||
"OnEnabledEndpointCondition may only be used on @Bean methods");
|
||||
return getEndpointAttributes(context, (MethodMetadata) metadata);
|
||||
Class<?> endpointType = getEndpointType(context, (MethodMetadata) metadata);
|
||||
return getEndpointAttributes(endpointType);
|
||||
}
|
||||
|
||||
private AnnotationAttributes getEndpointAttributes(ConditionContext context,
|
||||
MethodMetadata metadata) {
|
||||
private Class<?> getEndpointType(ConditionContext context, MethodMetadata metadata) {
|
||||
Map<String, Object> attributes = metadata
|
||||
.getAnnotationAttributes(ConditionalOnEnabledEndpoint.class.getName());
|
||||
if (attributes != null && attributes.containsKey("endpoint")) {
|
||||
Class<?> target = (Class<?>) attributes.get("endpoint");
|
||||
if (target != Void.class) {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
// We should be safe to load at this point since we are in the REGISTER_BEAN phase
|
||||
try {
|
||||
Class<?> returnType = ClassUtils.forName(metadata.getReturnTypeName(),
|
||||
return ClassUtils.forName(metadata.getReturnTypeName(),
|
||||
context.getClassLoader());
|
||||
return getEndpointAttributes(returnType);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Failed to extract endpoint id for "
|
||||
|
|
@ -119,8 +127,8 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
|
|||
attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(type,
|
||||
EndpointExtension.class, false, true);
|
||||
Assert.state(attributes != null,
|
||||
"OnEnabledEndpointCondition may only be used on @Bean methods that "
|
||||
+ "return an @Endpoint or @EndpointExtension");
|
||||
"No endpoint is specified and the return type of the @Bean method is "
|
||||
+ "neither an @Endpoint, nor an @EndpointExtension");
|
||||
return getEndpointAttributes(attributes.getClass("endpoint"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,44 @@ public class ConditionalOnEnabledEndpointTests {
|
|||
.doesNotHaveBean("fooExt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeWithReferenceWhenNoPropertiesShouldMatch() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(FooEndpointEnabledByDefaultTrue.class,
|
||||
ComponentEnabledIfEndpointIsEnabledConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasBean("fooComponent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeWithReferenceWhenEndpointEnabledPropertyIsTrueShouldMatch() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.foo.enabled=true")
|
||||
.withUserConfiguration(FooEndpointEnabledByDefaultTrue.class,
|
||||
ComponentEnabledIfEndpointIsEnabledConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasBean("fooComponent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeWithReferenceWhenEndpointEnabledPropertyIsFalseShouldNotMatch() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.foo.enabled=false")
|
||||
.withUserConfiguration(FooEndpointEnabledByDefaultTrue.class,
|
||||
ComponentEnabledIfEndpointIsEnabledConfiguration.class)
|
||||
.run((context) -> assertThat(context).doesNotHaveBean("fooComponent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeWithNoReferenceShouldFail() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(
|
||||
ComponentWithNoEndpointReferenceConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasFailed();
|
||||
assertThat(context.getStartupFailure().getCause().getMessage())
|
||||
.contains(
|
||||
"No endpoint is specified and the return type of the @Bean method "
|
||||
+ "is neither an @Endpoint, nor an @EndpointExtension");
|
||||
});
|
||||
}
|
||||
|
||||
@Endpoint(id = "foo", enableByDefault = true)
|
||||
static class FooEndpointEnabledByDefaultTrue {
|
||||
|
||||
|
|
@ -187,4 +225,26 @@ public class ConditionalOnEnabledEndpointTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ComponentEnabledIfEndpointIsEnabledConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledEndpoint(endpoint = FooEndpointEnabledByDefaultTrue.class)
|
||||
public String fooComponent() {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ComponentWithNoEndpointReferenceConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public String fooComponent() {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue