configurationProperties) {
+ this.configurationProperties = configurationProperties;
+ }
+ }
+
+
+}
diff --git a/microsphere-spring-boot-actuator/src/main/resources/META-INF/config/default/endpoints.properties b/microsphere-spring-boot-actuator/src/main/resources/META-INF/config/default/endpoints.properties
index 091964e..8aa300d 100644
--- a/microsphere-spring-boot-actuator/src/main/resources/META-INF/config/default/endpoints.properties
+++ b/microsphere-spring-boot-actuator/src/main/resources/META-INF/config/default/endpoints.properties
@@ -75,6 +75,12 @@ management.endpoint.artifacts.enabled = true
management.endpoints.web.path-mapping.artifacts = microsphere/artifacts
management.endpoint.artifacts.cache.time-to-live = ${microsphere.cache.long-long-time-to-live}
+### WebEndpoints Endpoint
management.endpoint.webEndpoints.enabled = true
management.endpoints.web.path-mapping.webEndpoints = microsphere/web/endpoints
management.endpoint.webEndpoints.cache.time-to-live = ${microsphere.cache.short-time-to-live}
+
+### ConfigurationMetadata Endpoint
+management.endpoint.configMetadata.enabled = true
+management.endpoints.web.path-mapping.configMetadata = microsphere/config/metadata
+management.endpoint.configMetadata.cache.time-to-live = ${microsphere.cache.long-long-time-to-live}
\ No newline at end of file
diff --git a/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/autoconfigure/ActuatorEndpointsAutoConfigurationTest.java b/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/autoconfigure/ActuatorEndpointsAutoConfigurationTest.java
index 8c8b1cd..7ce1881 100644
--- a/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/autoconfigure/ActuatorEndpointsAutoConfigurationTest.java
+++ b/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/autoconfigure/ActuatorEndpointsAutoConfigurationTest.java
@@ -1,6 +1,7 @@
package io.microsphere.spring.boot.actuate.autoconfigure;
import io.microsphere.spring.boot.actuate.endpoint.ArtifactsEndpoint;
+import io.microsphere.spring.boot.actuate.endpoint.ConfigurationMetadataEndpoint;
import io.microsphere.spring.boot.actuate.endpoint.WebEndpoints;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -9,6 +10,7 @@
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.PropertySource;
+import org.springframework.test.context.TestPropertySource;
import java.util.Map;
@@ -25,10 +27,15 @@
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = {
ActuatorEndpointsAutoConfigurationTest.class,
- })
+ },
+ properties = {
+ "management.endpoint.loggers.enabled=false"
+ }
+)
@PropertySource(value = "classpath:META-INF/config/default/endpoints.properties")
+@TestPropertySource(value = "classpath:META-INF/config/default/endpoints.properties")
@EnableAutoConfiguration
-class ActuatorEndpointsAutoConfigurationTest {
+public class ActuatorEndpointsAutoConfigurationTest {
@Autowired
private ArtifactsEndpoint artifactsEndpoint;
@@ -36,6 +43,9 @@ class ActuatorEndpointsAutoConfigurationTest {
@Autowired
private WebEndpoints webEndpoints;
+ @Autowired
+ private ConfigurationMetadataEndpoint configurationMetadataEndpoint;
+
@Test
void testArtifactsEndpoint() {
assertFalse(artifactsEndpoint.getArtifactMetaInfoList().isEmpty());
@@ -47,6 +57,13 @@ public void testInvokeReadOperations() {
assertFalse(aggregatedResults.isEmpty());
}
+ @Test
+ public void testGetConfigurationMetadata() {
+ ConfigurationMetadataEndpoint.ConfigurationMetadataDescriptor configurationMetadata = configurationMetadataEndpoint.getConfigurationMetadata();
+ assertFalse(configurationMetadata.getGroups().isEmpty());
+ assertFalse(configurationMetadata.getProperties().isEmpty());
+ }
+
public static void main(String[] args) {
new SpringApplicationBuilder(ActuatorEndpointsAutoConfigurationTest.class)
.web(WebApplicationType.SERVLET)
diff --git a/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/env/DefaultPropertiesTest.java b/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/env/DefaultPropertiesTest.java
index 175ba78..8b38131 100644
--- a/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/env/DefaultPropertiesTest.java
+++ b/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/env/DefaultPropertiesTest.java
@@ -57,7 +57,7 @@ public void testEndpointsDefaultProperties() {
assertProperty("management.endpoint.httptrace.enabled", "false");
assertProperty("management.endpoint.info.enabled", "true");
assertProperty("management.endpoint.integrationgraph.enabled", "false");
- assertProperty("management.endpoint.loggers.enabled", "true");
+ assertProperty("management.endpoint.loggers.enabled", "false");
assertProperty("management.endpoint.liquibase.enabled", "false");
assertProperty("management.endpoint.metrics.enabled", "true");
assertProperty("management.endpoint.mappings.enabled", "true");
@@ -74,6 +74,6 @@ public void testEndpointsDefaultProperties() {
}
private void assertProperty(String propertyName, String expectedValue) {
- assertEquals(environment.getRequiredProperty(propertyName), expectedValue);
+ assertEquals(expectedValue, environment.getRequiredProperty(propertyName));
}
}
diff --git a/microsphere-spring-boot-actuator/src/test/resources/application.properties b/microsphere-spring-boot-actuator/src/test/resources/application.properties
new file mode 100644
index 0000000..1636c41
--- /dev/null
+++ b/microsphere-spring-boot-actuator/src/test/resources/application.properties
@@ -0,0 +1 @@
+management.endpoint.loggers.enabled = false
\ No newline at end of file
diff --git a/microsphere-spring-boot-compatible/pom.xml b/microsphere-spring-boot-compatible/pom.xml
new file mode 100644
index 0000000..5391180
--- /dev/null
+++ b/microsphere-spring-boot-compatible/pom.xml
@@ -0,0 +1,78 @@
+
+
+ 4.0.0
+
+ io.github.microsphere-projects
+ microsphere-spring-boot-parent
+ ${revision}
+ ../microsphere-spring-boot-parent/pom.xml
+
+
+ io.github.microsphere-projects
+ microsphere-spring-boot-compatible
+ ${revision}
+ jar
+
+ Microsphere :: Spring Boot :: Compatible
+ Microsphere Spring Boot Compatible
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+ true
+
+
+
+
+ org.springframework
+ spring-core
+ true
+
+
+
+ org.springframework
+ spring-context
+ true
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+
+ true
+
+
+
+
+
+
+
+
+ spring-boot-2.1
+
+
+ org.springframework
+ spring-core
+ 5.2.25.RELEASE
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/BootstrapContext.java b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/BootstrapContext.java
new file mode 100644
index 0000000..f3c4941
--- /dev/null
+++ b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/BootstrapContext.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012-2020 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.env.Environment;
+
+import java.util.function.Supplier;
+
+/**
+ * A simple bootstrap context that is available during startup and {@link Environment}
+ * post-processing up to the point that the {@link ApplicationContext} is prepared.
+ *
+ * Provides lazy access to singletons that may be expensive to create, or need to be
+ * shared before the {@link ApplicationContext} is available.
+ *
+ * @author Phillip Webb
+ * @since 2.4.0
+ */
+public interface BootstrapContext {
+
+ /**
+ * Return an instance from the context if the type has been registered. The instance
+ * will be created it if it hasn't been accessed previously.
+ * @param the instance type
+ * @param type the instance type
+ * @return the instance managed by the context
+ * @throws IllegalStateException if the type has not been registered
+ */
+ T get(Class type) throws IllegalStateException;
+
+ /**
+ * Return an instance from the context if the type has been registered. The instance
+ * will be created it if it hasn't been accessed previously.
+ * @param the instance type
+ * @param type the instance type
+ * @param other the instance to use if the type has not been registered
+ * @return the instance
+ */
+ T getOrElse(Class type, T other);
+
+ /**
+ * Return an instance from the context if the type has been registered. The instance
+ * will be created it if it hasn't been accessed previously.
+ * @param the instance type
+ * @param type the instance type
+ * @param other a supplier for the instance to use if the type has not been registered
+ * @return the instance
+ */
+ T getOrElseSupply(Class type, Supplier other);
+
+ /**
+ * Return an instance from the context if the type has been registered. The instance
+ * will be created it if it hasn't been accessed previously.
+ * @param the instance type
+ * @param the exception to throw if the type is not registered
+ * @param type the instance type
+ * @param exceptionSupplier the supplier which will return the exception to be thrown
+ * @return the instance managed by the context
+ * @throws X if the type has not been registered
+ * @throws IllegalStateException if the type has not been registered
+ */
+ T getOrElseThrow(Class type, Supplier extends X> exceptionSupplier) throws X;
+
+ /**
+ * Return if a registration exists for the given type.
+ * @param the instance type
+ * @param type the instance type
+ * @return {@code true} if the type has already been registered
+ */
+ boolean isRegistered(Class type);
+
+}
diff --git a/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/BootstrapContextClosedEvent.java b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/BootstrapContextClosedEvent.java
new file mode 100644
index 0000000..2be023e
--- /dev/null
+++ b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/BootstrapContextClosedEvent.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012-2020 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * {@link ApplicationEvent} published by a {@link BootstrapContext} when it's closed.
+ *
+ * @author Phillip Webb
+ * @since 2.4.0
+ * @see BootstrapRegistry#addCloseListener(org.springframework.context.ApplicationListener)
+ */
+public class BootstrapContextClosedEvent extends ApplicationEvent {
+
+ private final ConfigurableApplicationContext applicationContext;
+
+ BootstrapContextClosedEvent(BootstrapContext source, ConfigurableApplicationContext applicationContext) {
+ super(source);
+ this.applicationContext = applicationContext;
+ }
+
+ /**
+ * Return the {@link BootstrapContext} that was closed.
+ * @return the bootstrap context
+ */
+ public BootstrapContext getBootstrapContext() {
+ return (BootstrapContext) this.source;
+ }
+
+ /**
+ * Return the prepared application context.
+ * @return the application context
+ */
+ public ConfigurableApplicationContext getApplicationContext() {
+ return this.applicationContext;
+ }
+
+}
diff --git a/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/BootstrapRegistry.java b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/BootstrapRegistry.java
new file mode 100644
index 0000000..74cf06d
--- /dev/null
+++ b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/BootstrapRegistry.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2012-2023 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.env.Environment;
+import org.springframework.util.Assert;
+
+import java.util.function.Supplier;
+
+/**
+ * A simple object registry that is available during startup and {@link Environment}
+ * post-processing up to the point that the {@link ApplicationContext} is prepared.
+ *
+ * Can be used to register instances that may be expensive to create, or need to be shared
+ * before the {@link ApplicationContext} is available.
+ *
+ * The registry uses {@link Class} as a key, meaning that only a single instance of a
+ * given type can be stored.
+ *
+ * The {@link #addCloseListener(ApplicationListener)} method can be used to add a listener
+ * that can perform actions when {@link BootstrapContext} has been closed and the
+ * {@link ApplicationContext} is fully prepared. For example, an instance may choose to
+ * register itself as a regular Spring bean so that it is available for the application to
+ * use.
+ *
+ * @author Phillip Webb
+ * @since 2.4.0
+ * @see BootstrapContext
+ * @see ConfigurableBootstrapContext
+ */
+public interface BootstrapRegistry {
+
+ /**
+ * Register a specific type with the registry. If the specified type has already been
+ * registered and has not been obtained as a {@link Scope#SINGLETON singleton}, it
+ * will be replaced.
+ * @param the instance type
+ * @param type the instance type
+ * @param instanceSupplier the instance supplier
+ */
+ void register(Class type, InstanceSupplier instanceSupplier);
+
+ /**
+ * Register a specific type with the registry if one is not already present.
+ * @param the instance type
+ * @param type the instance type
+ * @param instanceSupplier the instance supplier
+ */
+ void registerIfAbsent(Class type, InstanceSupplier instanceSupplier);
+
+ /**
+ * Return if a registration exists for the given type.
+ * @param the instance type
+ * @param type the instance type
+ * @return {@code true} if the type has already been registered
+ */
+ boolean isRegistered(Class type);
+
+ /**
+ * Return any existing {@link InstanceSupplier} for the given type.
+ * @param the instance type
+ * @param type the instance type
+ * @return the registered {@link InstanceSupplier} or {@code null}
+ */
+ InstanceSupplier getRegisteredInstanceSupplier(Class type);
+
+ /**
+ * Add an {@link ApplicationListener} that will be called with a
+ * {@link BootstrapContextClosedEvent} when the {@link BootstrapContext} is closed and
+ * the {@link ApplicationContext} has been prepared.
+ * @param listener the listener to add
+ */
+ void addCloseListener(ApplicationListener listener);
+
+ /**
+ * Supplier used to provide the actual instance when needed.
+ *
+ * @param the instance type
+ * @see Scope
+ */
+ @FunctionalInterface
+ interface InstanceSupplier {
+
+ /**
+ * Factory method used to create the instance when needed.
+ * @param context the {@link BootstrapContext} which may be used to obtain other
+ * bootstrap instances.
+ * @return the instance
+ */
+ T get(BootstrapContext context);
+
+ /**
+ * Return the scope of the supplied instance.
+ * @return the scope
+ * @since 2.4.2
+ */
+ default Scope getScope() {
+ return Scope.SINGLETON;
+ }
+
+ /**
+ * Return a new {@link InstanceSupplier} with an updated {@link Scope}.
+ * @param scope the new scope
+ * @return a new {@link InstanceSupplier} instance with the new scope
+ * @since 2.4.2
+ */
+ default InstanceSupplier withScope(Scope scope) {
+ Assert.notNull(scope, "Scope must not be null");
+ InstanceSupplier parent = this;
+ return new InstanceSupplier() {
+
+ @Override
+ public T get(BootstrapContext context) {
+ return parent.get(context);
+ }
+
+ @Override
+ public Scope getScope() {
+ return scope;
+ }
+
+ };
+ }
+
+ /**
+ * Factory method that can be used to create an {@link InstanceSupplier} for a
+ * given instance.
+ * @param the instance type
+ * @param instance the instance
+ * @return a new {@link InstanceSupplier}
+ */
+ static InstanceSupplier of(T instance) {
+ return (registry) -> instance;
+ }
+
+ /**
+ * Factory method that can be used to create an {@link InstanceSupplier} from a
+ * {@link Supplier}.
+ * @param the instance type
+ * @param supplier the supplier that will provide the instance
+ * @return a new {@link InstanceSupplier}
+ */
+ static InstanceSupplier from(Supplier supplier) {
+ return (registry) -> (supplier != null) ? supplier.get() : null;
+ }
+
+ }
+
+ /**
+ * The scope of an instance.
+ *
+ * @since 2.4.2
+ */
+ enum Scope {
+
+ /**
+ * A singleton instance. The {@link InstanceSupplier} will be called only once and
+ * the same instance will be returned each time.
+ */
+ SINGLETON,
+
+ /**
+ * A prototype instance. The {@link InstanceSupplier} will be called whenever an
+ * instance is needed.
+ */
+ PROTOTYPE
+
+ }
+
+}
diff --git a/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/ConfigurableBootstrapContext.java b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/ConfigurableBootstrapContext.java
new file mode 100644
index 0000000..28eaa34
--- /dev/null
+++ b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/ConfigurableBootstrapContext.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2012-2023 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot;
+
+/**
+ * A {@link BootstrapContext} that also provides configuration methods through the
+ * {@link BootstrapRegistry} interface.
+ *
+ * @author Phillip Webb
+ * @since 2.4.0
+ * @see BootstrapRegistry
+ * @see BootstrapContext
+ * @see DefaultBootstrapContext
+ */
+public interface ConfigurableBootstrapContext extends BootstrapRegistry, BootstrapContext {
+
+}
diff --git a/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/DefaultBootstrapContext.java b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/DefaultBootstrapContext.java
new file mode 100644
index 0000000..0ba272b
--- /dev/null
+++ b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/DefaultBootstrapContext.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2012-2020 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.event.ApplicationEventMulticaster;
+import org.springframework.context.event.SimpleApplicationEventMulticaster;
+import org.springframework.util.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ * Default {@link ConfigurableBootstrapContext} implementation.
+ *
+ * @author Phillip Webb
+ * @since 2.4.0
+ */
+public class DefaultBootstrapContext implements ConfigurableBootstrapContext {
+
+ private final Map, InstanceSupplier>> instanceSuppliers = new HashMap<>();
+
+ private final Map, Object> instances = new HashMap<>();
+
+ private final ApplicationEventMulticaster events = new SimpleApplicationEventMulticaster();
+
+ @Override
+ public void register(Class type, InstanceSupplier instanceSupplier) {
+ register(type, instanceSupplier, true);
+ }
+
+ @Override
+ public void registerIfAbsent(Class type, InstanceSupplier instanceSupplier) {
+ register(type, instanceSupplier, false);
+ }
+
+ private void register(Class type, InstanceSupplier instanceSupplier, boolean replaceExisting) {
+ Assert.notNull(type, "Type must not be null");
+ Assert.notNull(instanceSupplier, "InstanceSupplier must not be null");
+ synchronized (this.instanceSuppliers) {
+ boolean alreadyRegistered = this.instanceSuppliers.containsKey(type);
+ if (replaceExisting || !alreadyRegistered) {
+ Assert.state(!this.instances.containsKey(type), () -> type.getName() + " has already been created");
+ this.instanceSuppliers.put(type, instanceSupplier);
+ }
+ }
+ }
+
+ @Override
+ public boolean isRegistered(Class type) {
+ synchronized (this.instanceSuppliers) {
+ return this.instanceSuppliers.containsKey(type);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public InstanceSupplier getRegisteredInstanceSupplier(Class type) {
+ synchronized (this.instanceSuppliers) {
+ return (InstanceSupplier) this.instanceSuppliers.get(type);
+ }
+ }
+
+ @Override
+ public void addCloseListener(ApplicationListener listener) {
+ this.events.addApplicationListener(listener);
+ }
+
+ @Override
+ public T get(Class type) throws IllegalStateException {
+ return getOrElseThrow(type, () -> new IllegalStateException(type.getName() + " has not been registered"));
+ }
+
+ @Override
+ public T getOrElse(Class type, T other) {
+ return getOrElseSupply(type, () -> other);
+ }
+
+ @Override
+ public T getOrElseSupply(Class type, Supplier other) {
+ synchronized (this.instanceSuppliers) {
+ InstanceSupplier> instanceSupplier = this.instanceSuppliers.get(type);
+ return (instanceSupplier != null) ? getInstance(type, instanceSupplier) : other.get();
+ }
+ }
+
+ @Override
+ public T getOrElseThrow(Class type, Supplier extends X> exceptionSupplier) throws X {
+ synchronized (this.instanceSuppliers) {
+ InstanceSupplier> instanceSupplier = this.instanceSuppliers.get(type);
+ if (instanceSupplier == null) {
+ throw exceptionSupplier.get();
+ }
+ return getInstance(type, instanceSupplier);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private T getInstance(Class type, InstanceSupplier> instanceSupplier) {
+ T instance = (T) this.instances.get(type);
+ if (instance == null) {
+ instance = (T) instanceSupplier.get(this);
+ if (instanceSupplier.getScope() == Scope.SINGLETON) {
+ this.instances.put(type, instance);
+ }
+ }
+ return instance;
+ }
+
+ /**
+ * Method to be called when {@link BootstrapContext} is closed and the
+ * {@link ApplicationContext} is prepared.
+ * @param applicationContext the prepared context
+ */
+ public void close(ConfigurableApplicationContext applicationContext) {
+ this.events.multicastEvent(new BootstrapContextClosedEvent(this, applicationContext));
+ }
+
+}
diff --git a/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/AbstractEndpointCondition.java b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/AbstractEndpointCondition.java
new file mode 100644
index 0000000..dc25d78
--- /dev/null
+++ b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/AbstractEndpointCondition.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2012-2019 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.actuate.autoconfigure.endpoint.condition;
+
+import org.springframework.boot.actuate.endpoint.EndpointId;
+import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
+import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension;
+import org.springframework.boot.autoconfigure.condition.ConditionMessage;
+import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
+import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.core.annotation.MergedAnnotation;
+import org.springframework.core.annotation.MergedAnnotations;
+import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
+import org.springframework.core.env.Environment;
+import org.springframework.core.type.AnnotatedTypeMetadata;
+import org.springframework.core.type.MethodMetadata;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ConcurrentReferenceHashMap;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Base class for {@link Endpoint @Endpoint} related {@link SpringBootCondition}
+ * implementations.
+ *
+ * @author Stephane Nicoll
+ * @author Andy Wilkinson
+ * @author Phillip Webb
+ */
+abstract class AbstractEndpointCondition extends SpringBootCondition {
+
+ private static final String ENABLED_BY_DEFAULT_KEY = "management.endpoints.enabled-by-default";
+
+ private static final ConcurrentReferenceHashMap> enabledByDefaultCache = new ConcurrentReferenceHashMap<>();
+
+ AnnotationAttributes getEndpointAttributes(Class> annotationClass, ConditionContext context,
+ AnnotatedTypeMetadata metadata) {
+ return getEndpointAttributes(getEndpointType(annotationClass, context, metadata));
+ }
+
+ protected ConditionOutcome getEnablementOutcome(ConditionContext context, AnnotatedTypeMetadata metadata,
+ Class extends Annotation> annotationClass) {
+ Environment environment = context.getEnvironment();
+ AnnotationAttributes attributes = getEndpointAttributes(annotationClass, context, metadata);
+ EndpointId id = EndpointId.of(environment, attributes.getString("id"));
+ String key = "management.endpoint." + id.toLowerCaseString() + ".enabled";
+ Boolean userDefinedEnabled = environment.getProperty(key, Boolean.class);
+ if (userDefinedEnabled != null) {
+ return new ConditionOutcome(userDefinedEnabled, ConditionMessage.forCondition(annotationClass)
+ .because("found property " + key + " with value " + userDefinedEnabled));
+ }
+ Boolean userDefinedDefault = isEnabledByDefault(environment);
+ if (userDefinedDefault != null) {
+ return new ConditionOutcome(userDefinedDefault, ConditionMessage.forCondition(annotationClass).because(
+ "no property " + key + " found so using user defined default from " + ENABLED_BY_DEFAULT_KEY));
+ }
+ boolean endpointDefault = attributes.getBoolean("enableByDefault");
+ return new ConditionOutcome(endpointDefault, ConditionMessage.forCondition(annotationClass)
+ .because("no property " + key + " found so using endpoint default"));
+ }
+
+ protected Class> getEndpointType(Class> annotationClass, ConditionContext context,
+ AnnotatedTypeMetadata metadata) {
+ Map attributes = metadata.getAnnotationAttributes(annotationClass.getName());
+ if (attributes != null && attributes.containsKey("endpoint")) {
+ Class> target = (Class>) attributes.get("endpoint");
+ if (target != Void.class) {
+ return target;
+ }
+ }
+ Assert.state(metadata instanceof MethodMetadata && metadata.isAnnotated(Bean.class.getName()),
+ "EndpointCondition must be used on @Bean methods when the endpoint is not specified");
+ MethodMetadata methodMetadata = (MethodMetadata) metadata;
+ try {
+ return ClassUtils.forName(methodMetadata.getReturnTypeName(), context.getClassLoader());
+ }
+ catch (Throwable ex) {
+ throw new IllegalStateException("Failed to extract endpoint id for "
+ + methodMetadata.getDeclaringClassName() + "." + methodMetadata.getMethodName(), ex);
+ }
+ }
+
+ protected AnnotationAttributes getEndpointAttributes(Class> type) {
+ MergedAnnotations annotations = MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY);
+ MergedAnnotation endpoint = annotations.get(Endpoint.class);
+ if (endpoint.isPresent()) {
+ return endpoint.asAnnotationAttributes();
+ }
+ MergedAnnotation extension = annotations.get(EndpointExtension.class);
+ Assert.state(extension.isPresent(), "No endpoint is specified and the return type of the @Bean method is "
+ + "neither an @Endpoint, nor an @EndpointExtension");
+ return getEndpointAttributes(extension.getClass("endpoint"));
+ }
+
+ private Boolean isEnabledByDefault(Environment environment) {
+ Optional enabledByDefault = enabledByDefaultCache.get(environment);
+ if (enabledByDefault == null) {
+ enabledByDefault = Optional.ofNullable(environment.getProperty(ENABLED_BY_DEFAULT_KEY, Boolean.class));
+ enabledByDefaultCache.put(environment, enabledByDefault);
+ }
+ return enabledByDefault.orElse(null);
+ }
+
+}
diff --git a/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnAvailableEndpoint.java b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnAvailableEndpoint.java
new file mode 100644
index 0000000..bd143c8
--- /dev/null
+++ b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnAvailableEndpoint.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2012-2019 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.actuate.autoconfigure.endpoint.condition;
+
+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;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * {@link Conditional @Conditional} that checks whether an endpoint is available. An
+ * endpoint is considered available if it is both enabled and exposed. Matches enablement
+ * according to the endpoints specific {@link Environment} property, falling back to
+ * {@code management.endpoints.enabled-by-default} or failing that
+ * {@link Endpoint#enableByDefault()}. Matches exposure according to any of the
+ * {@code management.endpoints.web.exposure.} or
+ * {@code management.endpoints.jmx.exposure.} specific properties or failing that to
+ * whether the application runs on
+ * {@link org.springframework.boot.cloud.CloudPlatform#CLOUD_FOUNDRY}. Both those
+ * conditions should match for the endpoint to be considered available.
+ *
+ * When placed on a {@code @Bean} method, the endpoint defaults to the return type of the
+ * factory method:
+ *
+ *
+ * @Configuration
+ * public class MyConfiguration {
+ *
+ * @ConditionalOnAvailableEndpoint
+ * @Bean
+ * public MyEndpoint myEndpoint() {
+ * ...
+ * }
+ *
+ * }
+ *
+ * It is also possible to use the same mechanism for extensions:
+ *
+ *
+ * @Configuration
+ * public class MyConfiguration {
+ *
+ * @ConditionalOnAvailableEndpoint
+ * @Bean
+ * public MyEndpointWebExtension myEndpointWebExtension() {
+ * ...
+ * }
+ *
+ * }
+ *
+ * In the sample above, {@code MyEndpointWebExtension} will be created if the endpoint is
+ * available as defined by the rules above. {@code MyEndpointWebExtension} must be a
+ * regular extension that refers to an endpoint, something like:
+ *
+ *
+ * @EndpointWebExtension(endpoint = MyEndpoint.class)
+ * public class MyEndpointWebExtension {
+ *
+ * }
+ *
+ * Alternatively, the target endpoint can be manually specified for components that should
+ * only be created when a given endpoint is available:
+ *
+ *
+ * @Configuration
+ * public class MyConfiguration {
+ *
+ * @ConditionalOnAvailableEndpoint(endpoint = MyEndpoint.class)
+ * @Bean
+ * public MyComponent myComponent() {
+ * ...
+ * }
+ *
+ * }
+ *
+ * @author Brian Clozel
+ * @author Stephane Nicoll
+ * @since 2.2.0
+ * @see Endpoint
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Documented
+@Conditional(OnAvailableEndpointCondition.class)
+public @interface ConditionalOnAvailableEndpoint {
+
+ /**
+ * The endpoint type that should be checked. Inferred when the return type of the
+ * {@code @Bean} method is either an {@link Endpoint @Endpoint} or an
+ * {@link EndpointExtension @EndpointExtension}.
+ * @return the endpoint type to check
+ */
+ Class> endpoint() default Void.class;
+
+}
diff --git a/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnAvailableEndpointCondition.java b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnAvailableEndpointCondition.java
new file mode 100644
index 0000000..e4d5b6a
--- /dev/null
+++ b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnAvailableEndpointCondition.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2012-2020 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.actuate.autoconfigure.endpoint.condition;
+
+import org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludeExcludeEndpointFilter;
+import org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludeExcludeEndpointFilter.DefaultIncludes;
+import org.springframework.boot.actuate.endpoint.EndpointId;
+import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
+import org.springframework.boot.autoconfigure.condition.ConditionMessage;
+import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
+import org.springframework.boot.cloud.CloudPlatform;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.core.env.Environment;
+import org.springframework.core.type.AnnotatedTypeMetadata;
+import org.springframework.util.ConcurrentReferenceHashMap;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A condition that checks if an endpoint is available (i.e. enabled and exposed).
+ *
+ * @author Brian Clozel
+ * @author Stephane Nicoll
+ * @author Phillip Webb
+ * @see ConditionalOnAvailableEndpoint
+ */
+class OnAvailableEndpointCondition extends AbstractEndpointCondition {
+
+ private static final String JMX_ENABLED_KEY = "spring.jmx.enabled";
+
+ private static final Map> exposuresCache = new ConcurrentReferenceHashMap<>();
+
+ @Override
+ public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
+ ConditionOutcome enablementOutcome = getEnablementOutcome(context, metadata,
+ ConditionalOnAvailableEndpoint.class);
+ if (!enablementOutcome.isMatch()) {
+ return enablementOutcome;
+ }
+ ConditionMessage message = enablementOutcome.getConditionMessage();
+ Environment environment = context.getEnvironment();
+ if (CloudPlatform.CLOUD_FOUNDRY.isActive(environment)) {
+ return new ConditionOutcome(true, message.andCondition(ConditionalOnAvailableEndpoint.class)
+ .because("application is running on Cloud Foundry"));
+ }
+ EndpointId id = EndpointId.of(environment,
+ getEndpointAttributes(ConditionalOnAvailableEndpoint.class, context, metadata).getString("id"));
+ Set exposures = getExposures(environment);
+ for (Exposure exposure : exposures) {
+ if (exposure.isExposed(id)) {
+ return new ConditionOutcome(true,
+ message.andCondition(ConditionalOnAvailableEndpoint.class)
+ .because("marked as exposed by a 'management.endpoints." + exposure.getPrefix()
+ + ".exposure' property"));
+ }
+ }
+ return new ConditionOutcome(false, message.andCondition(ConditionalOnAvailableEndpoint.class)
+ .because("no 'management.endpoints' property marked it as exposed"));
+ }
+
+ private Set getExposures(Environment environment) {
+ Set exposures = exposuresCache.get(environment);
+ if (exposures == null) {
+ exposures = new HashSet<>(2);
+ if (environment.getProperty(JMX_ENABLED_KEY, Boolean.class, false)) {
+ exposures.add(new Exposure(environment, "jmx", DefaultIncludes.JMX));
+ }
+ exposures.add(new Exposure(environment, "web", DefaultIncludes.WEB));
+ exposuresCache.put(environment, exposures);
+ }
+ return exposures;
+ }
+
+ static class Exposure extends IncludeExcludeEndpointFilter> {
+
+ private final String prefix;
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ Exposure(Environment environment, String prefix, DefaultIncludes defaultIncludes) {
+ super((Class) ExposableEndpoint.class, environment, "management.endpoints." + prefix + ".exposure",
+ defaultIncludes);
+ this.prefix = prefix;
+ }
+
+ String getPrefix() {
+ return this.prefix;
+ }
+
+ boolean isExposed(EndpointId id) {
+ return super.match(id);
+ }
+
+ }
+
+}
diff --git a/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/expose/IncludeExcludeEndpointFilter.java b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/expose/IncludeExcludeEndpointFilter.java
new file mode 100644
index 0000000..03df597
--- /dev/null
+++ b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/expose/IncludeExcludeEndpointFilter.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2012-2023 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.actuate.autoconfigure.endpoint.expose;
+
+import org.springframework.boot.actuate.endpoint.EndpointFilter;
+import org.springframework.boot.actuate.endpoint.EndpointId;
+import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
+import org.springframework.boot.context.properties.bind.Bindable;
+import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.core.env.Environment;
+import org.springframework.util.Assert;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * {@link EndpointFilter} that will filter endpoints based on {@code include} and
+ * {@code exclude} patterns.
+ *
+ * @param the endpoint type
+ * @author Phillip Webb
+ * @since 2.2.7
+ */
+public class IncludeExcludeEndpointFilter> implements EndpointFilter {
+
+ private final Class endpointType;
+
+ private final EndpointPatterns include;
+
+ private final EndpointPatterns defaultIncludes;
+
+ private final EndpointPatterns exclude;
+
+ /**
+ * Create a new {@link IncludeExcludeEndpointFilter} with include/exclude rules bound
+ * from the {@link Environment}.
+ * @param endpointType the endpoint type that should be considered (other types always
+ * match)
+ * @param environment the environment containing the properties
+ * @param prefix the property prefix to bind
+ * @param defaultIncludes the default {@code includes} to use when none are specified.
+ * @deprecated since 2.6.0 for removal in 3.0.0 in favor of
+ * {@link #IncludeExcludeEndpointFilter(Class, Environment, String, String[])}
+ */
+ @Deprecated
+ public IncludeExcludeEndpointFilter(Class endpointType, Environment environment, String prefix,
+ DefaultIncludes defaultIncludes) {
+ this(endpointType, environment, prefix, DefaultIncludes.patterns(defaultIncludes));
+ }
+
+ /**
+ * Create a new {@link IncludeExcludeEndpointFilter} with include/exclude rules bound
+ * from the {@link Environment}.
+ * @param endpointType the endpoint type that should be considered (other types always
+ * match)
+ * @param environment the environment containing the properties
+ * @param prefix the property prefix to bind
+ * @param defaultIncludes the default {@code includes} to use when none are specified.
+ */
+ public IncludeExcludeEndpointFilter(Class endpointType, Environment environment, String prefix,
+ String... defaultIncludes) {
+ this(endpointType, environment, prefix, new EndpointPatterns(defaultIncludes));
+ }
+
+ /**
+ * Create a new {@link IncludeExcludeEndpointFilter} with specific include/exclude
+ * rules.
+ * @param endpointType the endpoint type that should be considered (other types always
+ * match)
+ * @param include the include patterns
+ * @param exclude the exclude patterns
+ * @param defaultIncludes the default {@code includes} to use when none are specified.
+ */
+ public IncludeExcludeEndpointFilter(Class endpointType, Collection include, Collection exclude,
+ String... defaultIncludes) {
+ this(endpointType, include, exclude, new EndpointPatterns(defaultIncludes));
+ }
+
+ /**
+ * Create a new {@link IncludeExcludeEndpointFilter} with specific include/exclude
+ * rules.
+ * @param endpointType the endpoint type that should be considered (other types always
+ * match)
+ * @param include the include patterns
+ * @param exclude the exclude patterns
+ * @param defaultIncludes the default {@code includes} to use when none are specified.
+ * @deprecated since 2.6.0 for removal in 3.0.0 in favor of
+ * {@link #IncludeExcludeEndpointFilter(Class, Environment, String, String[])}
+ */
+ @Deprecated
+ public IncludeExcludeEndpointFilter(Class endpointType, Collection include, Collection exclude,
+ DefaultIncludes defaultIncludes) {
+ this(endpointType, include, exclude, DefaultIncludes.patterns(defaultIncludes));
+ }
+
+ private IncludeExcludeEndpointFilter(Class endpointType, Environment environment, String prefix,
+ EndpointPatterns defaultIncludes) {
+ Assert.notNull(endpointType, "EndpointType must not be null");
+ Assert.notNull(environment, "Environment must not be null");
+ Assert.hasText(prefix, "Prefix must not be empty");
+ Assert.notNull(defaultIncludes, "DefaultIncludes must not be null");
+ Binder binder = Binder.get(environment);
+ this.endpointType = endpointType;
+ this.include = new EndpointPatterns(bind(binder, prefix + ".include"));
+ this.defaultIncludes = defaultIncludes;
+ this.exclude = new EndpointPatterns(bind(binder, prefix + ".exclude"));
+ }
+
+ private IncludeExcludeEndpointFilter(Class endpointType, Collection include, Collection exclude,
+ EndpointPatterns defaultIncludes) {
+ Assert.notNull(endpointType, "EndpointType Type must not be null");
+ Assert.notNull(defaultIncludes, "DefaultIncludes must not be null");
+ this.endpointType = endpointType;
+ this.include = new EndpointPatterns(include);
+ this.defaultIncludes = defaultIncludes;
+ this.exclude = new EndpointPatterns(exclude);
+ }
+
+ private List bind(Binder binder, String name) {
+ return binder.bind(name, Bindable.listOf(String.class)).orElseGet(ArrayList::new);
+ }
+
+ @Override
+ public boolean match(E endpoint) {
+ if (!this.endpointType.isInstance(endpoint)) {
+ // Leave non-matching types for other filters
+ return true;
+ }
+ return match(endpoint.getEndpointId());
+ }
+
+ /**
+ * Return {@code true} if the filter matches.
+ * @param endpointId the endpoint ID to check
+ * @return {@code true} if the filter matches
+ * @since 2.6.0
+ */
+ public final boolean match(EndpointId endpointId) {
+ return isIncluded(endpointId) && !isExcluded(endpointId);
+ }
+
+ private boolean isIncluded(EndpointId endpointId) {
+ if (this.include.isEmpty()) {
+ return this.defaultIncludes.matches(endpointId);
+ }
+ return this.include.matches(endpointId);
+ }
+
+ private boolean isExcluded(EndpointId endpointId) {
+ if (this.exclude.isEmpty()) {
+ return false;
+ }
+ return this.exclude.matches(endpointId);
+ }
+
+ /**
+ * Default include patterns that can be used.
+ *
+ * @deprecated since 2.6.0 for removal in 3.0.0 in favor of {@link EndpointExposure}.
+ */
+ @Deprecated
+ public enum DefaultIncludes {
+
+ /**
+ * The default set of include patterns used for JMX.
+ */
+ JMX("*"),
+
+ /**
+ * The default set of include patterns used for web.
+ */
+ WEB("health");
+
+ private final EndpointPatterns patterns;
+
+ DefaultIncludes(String... patterns) {
+ this.patterns = new EndpointPatterns(patterns);
+ }
+
+ static EndpointPatterns patterns(DefaultIncludes defaultIncludes) {
+ return (defaultIncludes != null) ? defaultIncludes.patterns : (EndpointPatterns) null;
+ }
+
+ }
+
+ /**
+ * A set of endpoint patterns used to match IDs.
+ */
+ private static class EndpointPatterns {
+
+ private final boolean empty;
+
+ private final boolean matchesAll;
+
+ private final Set endpointIds;
+
+ EndpointPatterns(String[] patterns) {
+ this((patterns != null) ? Arrays.asList(patterns) : (Collection) null);
+ }
+
+ EndpointPatterns(Collection patterns) {
+ patterns = (patterns != null) ? patterns : Collections.emptySet();
+ boolean matchesAll = false;
+ Set endpointIds = new LinkedHashSet<>();
+ for (String pattern : patterns) {
+ if ("*".equals(pattern)) {
+ matchesAll = true;
+ }
+ else {
+ endpointIds.add(EndpointId.fromPropertyValue(pattern));
+ }
+ }
+ this.empty = patterns.isEmpty();
+ this.matchesAll = matchesAll;
+ this.endpointIds = endpointIds;
+ }
+
+ boolean isEmpty() {
+ return this.empty;
+ }
+
+ boolean matches(EndpointId endpointId) {
+ return this.matchesAll || this.endpointIds.contains(endpointId);
+ }
+
+ }
+
+}
diff --git a/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/endpoint/EndpointId.java b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/endpoint/EndpointId.java
new file mode 100644
index 0000000..38e8725
--- /dev/null
+++ b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/actuate/endpoint/EndpointId.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2012-2020 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.actuate.endpoint;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.core.env.Environment;
+import org.springframework.util.Assert;
+
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * An identifier for an actuator endpoint. Endpoint IDs may contain only letters and
+ * numbers. They must begin with a lower-case letter. Case and syntax characters are
+ * ignored when comparing endpoint IDs.
+ *
+ * @author Phillip Webb
+ * @since 2.0.6
+ */
+public final class EndpointId {
+
+ private static final Log logger = LogFactory.getLog(EndpointId.class);
+
+ private static final Set loggedWarnings = new HashSet<>();
+
+ private static final Pattern VALID_PATTERN = Pattern.compile("[a-zA-Z0-9.-]+");
+
+ private static final Pattern WARNING_PATTERN = Pattern.compile("[.-]+");
+
+ private static final String MIGRATE_LEGACY_NAMES_PROPERTY = "management.endpoints.migrate-legacy-ids";
+
+ private final String value;
+
+ private final String lowerCaseValue;
+
+ private final String lowerCaseAlphaNumeric;
+
+ private EndpointId(String value) {
+ Assert.hasText(value, "Value must not be empty");
+ Assert.isTrue(VALID_PATTERN.matcher(value).matches(), "Value must only contain valid chars");
+ Assert.isTrue(!Character.isDigit(value.charAt(0)), "Value must not start with a number");
+ Assert.isTrue(!Character.isUpperCase(value.charAt(0)), "Value must not start with an uppercase letter");
+ if (WARNING_PATTERN.matcher(value).find()) {
+ logWarning(value);
+ }
+ this.value = value;
+ this.lowerCaseValue = value.toLowerCase(Locale.ENGLISH);
+ this.lowerCaseAlphaNumeric = getAlphaNumerics(this.lowerCaseValue);
+ }
+
+ private String getAlphaNumerics(String value) {
+ StringBuilder result = new StringBuilder(value.length());
+ for (int i = 0; i < value.length(); i++) {
+ char ch = value.charAt(i);
+ if (ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9') {
+ result.append(ch);
+ }
+ }
+ return result.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ return this.lowerCaseAlphaNumeric.equals(((EndpointId) obj).lowerCaseAlphaNumeric);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.lowerCaseAlphaNumeric.hashCode();
+ }
+
+ /**
+ * Return a lower-case version of the endpoint ID.
+ * @return the lower-case endpoint ID
+ */
+ public String toLowerCaseString() {
+ return this.lowerCaseValue;
+ }
+
+ @Override
+ public String toString() {
+ return this.value;
+ }
+
+ /**
+ * Factory method to create a new {@link EndpointId} of the specified value.
+ * @param value the endpoint ID value
+ * @return an {@link EndpointId} instance
+ */
+ public static EndpointId of(String value) {
+ return new EndpointId(value);
+ }
+
+ /**
+ * Factory method to create a new {@link EndpointId} of the specified value. This
+ * variant will respect the {@code management.endpoints.migrate-legacy-names} property
+ * if it has been set in the {@link Environment}.
+ * @param environment the Spring environment
+ * @param value the endpoint ID value
+ * @return an {@link EndpointId} instance
+ * @since 2.2.0
+ */
+ public static EndpointId of(Environment environment, String value) {
+ Assert.notNull(environment, "Environment must not be null");
+ return new EndpointId(migrateLegacyId(environment, value));
+ }
+
+ private static String migrateLegacyId(Environment environment, String value) {
+ if (environment.getProperty(MIGRATE_LEGACY_NAMES_PROPERTY, Boolean.class, false)) {
+ return value.replaceAll("[-.]+", "");
+ }
+ return value;
+ }
+
+ /**
+ * Factory method to create a new {@link EndpointId} from a property value. More
+ * lenient than {@link #of(String)} to allow for common "relaxed" property variants.
+ * @param value the property value to convert
+ * @return an {@link EndpointId} instance
+ */
+ public static EndpointId fromPropertyValue(String value) {
+ return new EndpointId(value.replace("-", ""));
+ }
+
+ static void resetLoggedWarnings() {
+ loggedWarnings.clear();
+ }
+
+ private static void logWarning(String value) {
+ if (logger.isWarnEnabled() && loggedWarnings.add(value)) {
+ logger.warn("Endpoint ID '" + value + "' contains invalid characters, please migrate to a valid format.");
+ }
+ }
+
+}
diff --git a/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/package-info.java b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/package-info.java
new file mode 100644
index 0000000..b36be1e
--- /dev/null
+++ b/microsphere-spring-boot-compatible/src/main/java/org/springframework/boot/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * The classes in this package or sub-packages are forked from the Spring Boot 2.7.x(latest),
+ * which will be used to be compatible with the lower versions of Spring Boot.
+ *
+ * @author Mercy
+ * @since 1.0.0
+ */
+package org.springframework.boot;
\ No newline at end of file
diff --git a/microsphere-spring-boot-parent/pom.xml b/microsphere-spring-boot-parent/pom.xml
index fc41584..c226129 100644
--- a/microsphere-spring-boot-parent/pom.xml
+++ b/microsphere-spring-boot-parent/pom.xml
@@ -19,7 +19,8 @@
Microsphere Spring Boot Parent
- 0.0.3
+ 0.1.0
+ 1.7.2
@@ -45,13 +46,6 @@
-
- spring-boot-2.0
-
- 2.0.9.RELEASE
-
-
-
spring-boot-2.1
@@ -103,6 +97,5 @@
2.7.18
-
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 91002da..3b6ab23 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
io.github.microsphere-projects
microsphere-build
- 0.0.21
+ 0.1.0
4.0.0
@@ -51,12 +51,13 @@
- 0.0.3
+ 0.1.0-SNAPSHOT
microsphere-spring-boot-parent
microsphere-spring-boot-dependencies
+ microsphere-spring-boot-compatible
microsphere-core-spring-boot-starter
microsphere-spring-boot-actuator