diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/ConfigurationPropertyHasFeaturesAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/ConfigurationPropertyHasFeaturesAutoConfiguration.java
index d28cb5af..53f446e1 100644
--- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/ConfigurationPropertyHasFeaturesAutoConfiguration.java
+++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/ConfigurationPropertyHasFeaturesAutoConfiguration.java
@@ -18,44 +18,47 @@
package io.microsphere.spring.cloud.client.actuator;
import io.microsphere.logging.Logger;
+import io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants;
import io.microsphere.spring.cloud.client.condition.ConditionalOnFeaturesAvailable;
import io.microsphere.spring.context.config.AutoRegistrationBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
-import org.springframework.beans.factory.config.SingletonBeanRegistry;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.actuator.NamedFeature;
-import org.springframework.context.EnvironmentAware;
-import org.springframework.core.env.ConfigurableEnvironment;
-import org.springframework.core.env.Environment;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
-import static io.microsphere.collection.ListUtils.newLinkedList;
+import static io.microsphere.collection.ListUtils.ofList;
import static io.microsphere.collection.MapUtils.newLinkedHashMap;
-import static io.microsphere.constants.PropertyConstants.MICROSPHERE_PROPERTY_NAME_PREFIX;
-import static io.microsphere.constants.SymbolConstants.COMMA_CHAR;
-import static io.microsphere.constants.SymbolConstants.DOT;
-import static io.microsphere.constants.SymbolConstants.DOT_CHAR;
+import static io.microsphere.collection.SetUtils.newLinkedHashSet;
+import static io.microsphere.collection.SetUtils.newTreeSet;
import static io.microsphere.logging.LoggerFactory.getLogger;
-import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX;
-import static io.microsphere.spring.core.env.EnvironmentUtils.asConfigurableEnvironment;
-import static io.microsphere.spring.core.env.PropertySourcesUtils.getSubProperties;
+import static io.microsphere.spring.beans.BeanSource.BEAN_FACTORY;
+import static io.microsphere.spring.beans.factory.BeanFactoryUtils.asDefaultListableBeanFactory;
+import static io.microsphere.spring.cloud.client.actuator.FeaturesUtils.getAbstractFeaturePropertyName;
+import static io.microsphere.spring.cloud.client.actuator.FeaturesUtils.getHasFeaturesBeanName;
+import static io.microsphere.spring.cloud.client.actuator.FeaturesUtils.getNamedFeaturePropertyName;
+import static io.microsphere.spring.cloud.client.actuator.FeaturesUtils.getQualifierFeatureName;
+import static io.microsphere.spring.cloud.client.actuator.NamedFeatureComparator.INSTANCE;
import static io.microsphere.text.FormatUtils.format;
import static io.microsphere.util.ClassLoaderUtils.resolveClass;
-import static io.microsphere.util.StringUtils.split;
-import static java.lang.String.valueOf;
+import static io.microsphere.util.ClassUtils.getSimpleName;
/**
* Auto-registrar for Spring Cloud Client Actuator's {@link HasFeatures} based on configuration properties.
*
- * This class scans configuration properties under the prefix {@value #PROPERTY_PREFIX} to automatically register
- * {@link HasFeatures} beans. It supports two types of feature definitions:
+ * This class scans configuration properties under the prefix {@value FeaturesConstants#PROPERTY_NAME_PREFIX} to
+ * automatically register {@link HasFeatures} beans. It supports two types of feature definitions:
*
* - Abstract Features: Defined by listing feature classes directly under a module name.
* - Named Features: Defined by mapping a specific feature name to a feature class under a module name.
@@ -66,13 +69,13 @@
* 1. Abstract Features
*
{@code
* # Defines abstract features for the 'jdbc' module
- * microsphere.spring.cloud.features.jdbc=org.springframework.jdbc.core.JdbcTemplate,org.springframework.transaction.PlatformTransactionManager
+ * microsphere.spring.cloud.features.abstract.jdbc=org.springframework.jdbc.core.JdbcTemplate,org.springframework.transaction.PlatformTransactionManager
* }
*
* 2. Named Features
* {@code
* # Defines a named feature 'rest-template' for the 'web' module
- * microsphere.spring.cloud.features.web.rest-template=org.springframework.web.client.RestTemplate
+ * microsphere.spring.cloud.features.named.web.rest-template=org.springframework.web.client.RestTemplate
* }
*
* Resulting Beans
@@ -87,38 +90,17 @@
*/
@ConditionalOnFeaturesAvailable
@AutoConfigureBefore(name = "org.springframework.cloud.client.CommonsClientAutoConfiguration")
-public class ConfigurationPropertyHasFeaturesAutoConfiguration implements BeanFactoryAware, BeanClassLoaderAware,
- EnvironmentAware {
+@EnableConfigurationProperties(FeaturesProperties.class)
+public class ConfigurationPropertyHasFeaturesAutoConfiguration implements BeanFactoryAware, BeanClassLoaderAware, InitializingBean {
private static final Logger logger = getLogger(ConfigurationPropertyHasFeaturesAutoConfiguration.class);
- static final String NAME = "features";
-
- /**
- * The prefix of the configuration properties for {@link HasFeatures} : "microsphere.spring.cloud.features."
- */
- public static final String PROPERTY_PREFIX = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + NAME + DOT;
-
- /**
- * The pattern of the configuration properties for abstract features: "microsphere.spring.cloud.features.{module-name}"
- */
- public static final String ABSTRACT_FEATURE_PROPERTY_NAME_PATTERN = PROPERTY_PREFIX + "{}";
-
- /**
- * The pattern of the configuration properties for named features: "microsphere.spring.cloud.features.{module-name}.{feature-name}"
- */
- public static final String NAMED_FEATURE_PROPERTY_NAME_PATTERN = ABSTRACT_FEATURE_PROPERTY_NAME_PATTERN + DOT + "{}";
-
- /**
- * The suffix of the bean name for {@link HasFeatures} : ".features"
- */
- public static final String BEAN_NAME_SUFFIX = DOT + NAME;
-
private ClassLoader classLoader;
- private SingletonBeanRegistry singletonBeanRegistry;
+ private DefaultListableBeanFactory beanFactory;
- private final Map moduleFeaturesMap = newLinkedHashMap();
+ @Autowired
+ private FeaturesProperties featuresProperties;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
@@ -127,141 +109,112 @@ public void setBeanClassLoader(ClassLoader classLoader) {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
- this.singletonBeanRegistry = (SingletonBeanRegistry) beanFactory;
+ this.beanFactory = asDefaultListableBeanFactory(beanFactory);
}
@Override
- public void setEnvironment(Environment environment) {
- ConfigurableEnvironment env = asConfigurableEnvironment(environment);
- Map subProperties = getSubProperties(env, PROPERTY_PREFIX);
- for (Entry entry : subProperties.entrySet()) {
- String key = entry.getKey();
- String value = valueOf(entry.getValue());
- int index = key.indexOf(DOT_CHAR);
- boolean isAbstract = index == -1;
- if (isAbstract) {
- String[] featureClassNames = split(value, COMMA_CHAR);
- String moduleName = key;
- addAbstractFeatureClassNames(moduleName, featureClassNames);
- } else {
- String moduleName = key.substring(0, index);
- String featureName = key.substring(index + 1);
- String featureClassName = value;
- addNamedFeatureClassName(moduleName, featureName, featureClassName);
- }
- }
- registerHasFeaturesBeans();
- }
+ public void afterPropertiesSet() {
+ FeaturesProperties featuresProperties = this.featuresProperties;
- /**
- * Gets the configuration property name for abstract features of the specified module.
- * Example Usage
- * {@code
- * // For module name "jdbc"
- * String propertyName = getAbstractFeaturePropertyName("jdbc");
- * // Result: "microsphere.spring.cloud.features.jdbc"
- * }
- *
- * @param moduleName the name of the module
- * @return the configuration property name for abstract features
- */
- public static String getAbstractFeaturePropertyName(String moduleName) {
- return format(ABSTRACT_FEATURE_PROPERTY_NAME_PATTERN, moduleName);
- }
+ Map moduleFeaturesMap = newLinkedHashMap();
+ Map> abstractProperties = featuresProperties.getAbstract();
- /**
- * Gets the configuration property name for a named feature of the specified module.
- * Example Usage
- * {@code
- * // For module name "web" and feature name "rest-template"
- * String propertyName = getNamedFeaturePropertyName("web", "rest-template");
- * // Result: "microsphere.spring.cloud.features.web.rest-template"
- * }
- *
- * @param moduleName the name of the module
- * @param featureName the name of the feature
- * @return the configuration property name for the named feature
- */
- public static String getNamedFeaturePropertyName(String moduleName, String featureName) {
- return format(NAMED_FEATURE_PROPERTY_NAME_PATTERN, moduleName, featureName);
- }
+ abstractProperties.forEach((moduleName, featureClassNames) -> {
+ addAbstractFeatureClassNames(moduleName, featureClassNames, moduleFeaturesMap);
+ });
- /**
- * Gets the bean name for {@link HasFeatures} of the specified module.
- * Example Usage
- * {@code
- * // For module name "jdbc"
- * String beanName = getBeanName("jdbc");
- * // Result: "jdbc.features"
- * }
- *
- * @param moduleName the name of the module
- * @return the bean name for {@link HasFeatures}
- */
- public static String getBeanName(String moduleName) {
- return moduleName + BEAN_NAME_SUFFIX;
- }
+ Map> namedProperties = featuresProperties.getNamed();
+
+ namedProperties.forEach((moduleName, namedFeatures) -> {
+ for (Entry entry : namedFeatures.entrySet()) {
+ String featureName = entry.getKey();
+ String featureClassName = entry.getValue();
+ addNamedFeatureClassName(moduleName, featureName, featureClassName, moduleFeaturesMap);
+ }
+ });
- /**
- * Gets the qualified feature name for a named feature of the specified module.
- * Example Usage
- * {@code
- * // For module name "web" and feature name "rest-template"
- * String qualifiedName = getQualifierFeatureName("web", "rest-template");
- * // Result: "microsphere.web.rest-template"
- * }
- *
- * @param moduleName the name of the module
- * @param featureName the name of the feature
- * @return the qualified feature name
- */
- public static String getQualifierFeatureName(String moduleName, String featureName) {
- return MICROSPHERE_PROPERTY_NAME_PREFIX + moduleName + DOT_CHAR + featureName;
+ registerHasFeaturesBeans(moduleFeaturesMap);
+ moduleFeaturesMap.clear();
}
- private void addAbstractFeatureClassNames(String moduleName, String[] featureClassNames) {
- ModuleFeatures moduleFeatures = getModuleFeatures(moduleName);
+ private void addAbstractFeatureClassNames(String moduleName, List featureClassNames, Map moduleFeaturesMap) {
for (String featureClassName : featureClassNames) {
- Class> featureClass = loadClass(featureClassName);
- if (featureClass == null) {
- String propertyName = getAbstractFeaturePropertyName(moduleName);
- logger.warn("The class of abstract feature[class : '{}'] is not found in classpath, please check the configuration property : '{}'",
- featureClassName, propertyName);
- continue;
+ addAbstractFeatureClassName(moduleName, featureClassName, moduleFeaturesMap);
+ }
+ }
+
+ private void addAbstractFeatureClassName(String moduleName, String featureClassName, Map moduleFeaturesMap) {
+ Class> featureClass = loadClass(featureClassName);
+ if (featureClass == null) {
+ logger.warn("The class of AbstractFeature[class : '{}'] is not found in classpath, please check the configuration property : '{}'",
+ featureClassName, getAbstractFeaturePropertyName(moduleName));
+ return;
+ }
+
+ Set> beanTypes = getBeanTypes(featureClass);
+ if (beanTypes.isEmpty()) { // No bean type found
+ addAbstractFeatureClass(moduleName, featureClass, moduleFeaturesMap);
+ } else {
+ for (Class> beanType : beanTypes) {
+ addNamedFeatureClass(moduleName, beanType, moduleFeaturesMap);
}
- moduleFeatures.abstractFeatures.add(featureClass);
}
}
- private void addNamedFeatureClassName(String moduleName, String featureName, String featureClassName) {
- ModuleFeatures moduleFeatures = getModuleFeatures(moduleName);
+ private void addNamedFeatureClassName(String moduleName, String featureName, String featureClassName, Map moduleFeaturesMap) {
String name = getQualifierFeatureName(moduleName, featureName);
Class> featureClass = loadClass(featureClassName);
+ String propertyName = getNamedFeaturePropertyName(moduleName, featureName);
if (featureClass == null) {
- String propertyName = getNamedFeaturePropertyName(moduleName, featureName);
logger.warn("The class of named feature[name : '{}' , class : '{}'] is not found in classpath, please check the configuration property : '{}'",
name, featureClassName, propertyName);
return;
}
+
+ addNamedFeatureClass(moduleName, featureName, featureClass, moduleFeaturesMap);
+ }
+
+ private Set> getBeanTypes(Class> featureClass) {
+ return (Set) BEAN_FACTORY.getBeanTypes(this.beanFactory, featureClass);
+ }
+
+ private void addAbstractFeatureClass(String moduleName, Class> featureClass, Map moduleFeaturesMap) {
+ ModuleFeatures moduleFeatures = getModuleFeatures(moduleFeaturesMap, moduleName);
+ logger.trace("The AbstractFeature[module : '{}' , class : '{}'] will be added in the HasFeatures.", moduleName,
+ featureClass.getName());
+ moduleFeatures.abstractFeatures.add(featureClass);
+ }
+
+ private void addNamedFeatureClass(String moduleName, Class> featureClass, Map moduleFeaturesMap) {
+ String featureName = getSimpleName(featureClass);
+ addNamedFeatureClass(moduleName, featureName, featureClass, moduleFeaturesMap);
+ }
+
+ private void addNamedFeatureClass(String moduleName, String featureName, Class> featureClass, Map moduleFeaturesMap) {
+ String name = getQualifierFeatureName(moduleName, featureName);
NamedFeature namedFeature = new NamedFeature(name, featureClass);
+ ModuleFeatures moduleFeatures = getModuleFeatures(moduleFeaturesMap, moduleName);
+ logger.trace("The NamedFeature[module : '{}' , name : '{}' , class : '{}'] will be added in the HasFeatures.",
+ moduleName, name, featureClass.getName());
moduleFeatures.namedFeatures.add(namedFeature);
}
+
private Class> loadClass(String className) {
return resolveClass(className, this.classLoader);
}
- private ModuleFeatures getModuleFeatures(String moduleName) {
- return this.moduleFeaturesMap.computeIfAbsent(moduleName, ModuleFeatures::new);
+ private ModuleFeatures getModuleFeatures(Map moduleFeaturesMap, String moduleName) {
+ return moduleFeaturesMap.computeIfAbsent(moduleName, ModuleFeatures::new);
}
- private void registerHasFeaturesBeans() {
- for (Entry entry : this.moduleFeaturesMap.entrySet()) {
+ private void registerHasFeaturesBeans(Map moduleFeaturesMap) {
+ for (Entry entry : moduleFeaturesMap.entrySet()) {
String moduleName = entry.getKey();
ModuleFeatures moduleFeatures = entry.getValue();
HasFeatures hasFeatures = moduleFeatures.toHasFeatures();
- String beanName = getBeanName(moduleName);
- this.singletonBeanRegistry.registerSingleton(beanName, hasFeatures);
+ String beanName = getHasFeaturesBeanName(moduleName);
+ this.beanFactory.registerSingleton(beanName, hasFeatures);
}
}
@@ -269,9 +222,9 @@ static class ModuleFeatures {
private final String moduleName;
- private final List> abstractFeatures = newLinkedList();
+ private final Set> abstractFeatures = newLinkedHashSet();
- private final List namedFeatures = newLinkedList();
+ private final Set namedFeatures = newTreeSet(INSTANCE);
ModuleFeatures(String moduleName) {
this.moduleName = moduleName;
@@ -279,7 +232,7 @@ static class ModuleFeatures {
HasFeatures toHasFeatures() {
logger.info(toString());
- return new HasFeatures(this.abstractFeatures, this.namedFeatures);
+ return new HasFeatures(ofList(this.abstractFeatures), ofList(this.namedFeatures));
}
@Override
diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/FeaturesProperties.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/FeaturesProperties.java
new file mode 100644
index 00000000..822a7a04
--- /dev/null
+++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/FeaturesProperties.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package io.microsphere.spring.cloud.client.actuator;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.client.actuator.FeaturesEndpoint;
+
+import java.util.List;
+import java.util.Map;
+
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.PROPERTY_NAME_PREFIX;
+import static java.util.Collections.emptyMap;
+
+/**
+ * The {@link ConfigurationProperties} for {@link FeaturesEndpoint}
+ *
+ * @author Mercy
+ * @see ConfigurationProperties
+ * @since 1.0.0
+ */
+@ConfigurationProperties(prefix = PROPERTY_NAME_PREFIX)
+public class FeaturesProperties {
+
+ private Map> _abstract;
+
+ private Map> named;
+
+ public void setAbstract(Map> _abstract) {
+ this._abstract = _abstract;
+ }
+
+ public Map> getAbstract() {
+ if (_abstract == null) {
+ return emptyMap();
+ }
+ return _abstract;
+ }
+
+ public void setNamed(Map> named) {
+ this.named = named;
+ }
+
+ public Map> getNamed() {
+ if (named == null) {
+ return emptyMap();
+ }
+ return named;
+ }
+}
diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/FeaturesUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/FeaturesUtils.java
new file mode 100644
index 00000000..46577c68
--- /dev/null
+++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/FeaturesUtils.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+package io.microsphere.spring.cloud.client.actuator;
+
+import io.microsphere.util.Utils;
+import org.springframework.cloud.client.actuator.HasFeatures;
+
+import static io.microsphere.constants.SymbolConstants.COLON_CHAR;
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.ABSTRACT_FEATURE_PROPERTY_NAME_PATTERN;
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.BEAN_NAME_SUFFIX;
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.NAMED_FEATURE_PROPERTY_NAME_PATTERN;
+import static io.microsphere.text.FormatUtils.format;
+
+/**
+ * The {@link Utils utilities} class for Spring Cloud {@link HasFeatures features}
+ *
+ * @author Mercy
+ * @see HasFeatures
+ * @see Utils
+ * @since 1.0.0
+ */
+public abstract class FeaturesUtils implements Utils {
+
+ /**
+ * Gets the configuration property name for abstract features of the specified module.
+ * Example Usage
+ * {@code
+ * // For module name "jdbc"
+ * String propertyName = getAbstractFeaturePropertyName("jdbc");
+ * // Result: "microsphere.spring.cloud.features.abstract.jdbc"
+ * }
+ *
+ * @param moduleName the name of the module
+ * @return the configuration property name for abstract features
+ */
+ public static String getAbstractFeaturePropertyName(String moduleName) {
+ return format(ABSTRACT_FEATURE_PROPERTY_NAME_PATTERN, moduleName);
+ }
+
+ /**
+ * Gets the configuration property name for a named feature of the specified module.
+ * Example Usage
+ * {@code
+ * // For module name "web" and feature name "rest-template"
+ * String propertyName = getNamedFeaturePropertyName("web", "rest-template");
+ * // Result: "microsphere.spring.cloud.features.named.web.rest-template"
+ * }
+ *
+ * @param moduleName the name of the module
+ * @param featureName the name of the feature
+ * @return the configuration property name for the named feature
+ */
+ public static String getNamedFeaturePropertyName(String moduleName, String featureName) {
+ return format(NAMED_FEATURE_PROPERTY_NAME_PATTERN, moduleName, featureName);
+ }
+
+ /**
+ * Gets the bean name for {@link HasFeatures} of the specified module.
+ * Example Usage
+ * {@code
+ * // For module name "jdbc"
+ * String beanName = getHasFeaturesBeanName("jdbc");
+ * // Result: "jdbc.features"
+ * }
+ *
+ * @param moduleName the name of the module
+ * @return the bean name for {@link HasFeatures}
+ */
+ public static String getHasFeaturesBeanName(String moduleName) {
+ return moduleName + BEAN_NAME_SUFFIX;
+ }
+
+ /**
+ * Gets the qualified feature name for a named feature of the specified module.
+ * Example Usage
+ * {@code
+ * // For module name "web" and feature name "rest-template"
+ * String qualifiedName = getQualifierFeatureName("web", "rest-template");
+ * // Result: "web:rest-template"
+ * }
+ *
+ * @param moduleName the name of the module
+ * @param featureName the name of the feature
+ * @return the qualified feature name
+ */
+ public static String getQualifierFeatureName(String moduleName, String featureName) {
+ return moduleName + COLON_CHAR + featureName;
+ }
+
+ private FeaturesUtils() {
+ }
+}
diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/NamedFeatureComparator.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/NamedFeatureComparator.java
new file mode 100644
index 00000000..e9381ac0
--- /dev/null
+++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/NamedFeatureComparator.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package io.microsphere.spring.cloud.client.actuator;
+
+import org.springframework.cloud.client.actuator.NamedFeature;
+
+import java.util.Comparator;
+
+/**
+ * The {@link Comparator} class for {@link NamedFeature}
+ *
+ * @author Mercy
+ * @see Comparator
+ * @see NamedFeature
+ * @since 1.0.0
+ */
+public class NamedFeatureComparator implements Comparator {
+
+ public static final NamedFeatureComparator INSTANCE = new NamedFeatureComparator();
+
+ @Override
+ public int compare(NamedFeature namedFeature1, NamedFeature namedFeature2) {
+ int c = namedFeature1.getName().compareTo(namedFeature2.getName());
+ if (c == 0) {
+ c = compare(namedFeature1.getType(), namedFeature2.getType());
+ }
+ return c;
+ }
+
+ private int compare(Class> type1, Class> type2) {
+ return type1.getName().compareTo(type2.getName());
+ }
+}
diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/constants/FeaturesConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/constants/FeaturesConstants.java
new file mode 100644
index 00000000..0fb71bde
--- /dev/null
+++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/actuator/constants/FeaturesConstants.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package io.microsphere.spring.cloud.client.actuator.constants;
+
+import org.springframework.cloud.client.actuator.HasFeatures;
+
+import static io.microsphere.constants.SymbolConstants.DOT;
+import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX;
+
+/**
+ * The constants class for Spring Cloud {@link HasFeatures features}
+ *
+ * @author Mercy
+ * @see HasFeatures
+ * @see org.springframework.cloud.client.actuator.FeaturesEndpoint
+ * @since 1.0.0
+ */
+public interface FeaturesConstants {
+
+ String FEATURES = "features";
+
+ String ABSTRACT = "abstract";
+
+ String NAMED = "named";
+
+ String PLACEHOLDER = "{}";
+
+ /**
+ * The prefix of the configuration properties for {@link HasFeatures} : "microsphere.spring.cloud.features"
+ */
+ String PROPERTY_NAME_PREFIX = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + FEATURES;
+
+ /**
+ * The prefix of the configuration properties for abstract features: "microsphere.spring.cloud.features.abstract."
+ */
+ String ABSTRACT_FEATURE_PROPERTY_NAME_PREFIX = PROPERTY_NAME_PREFIX + DOT + ABSTRACT + DOT;
+
+ /**
+ * The prefix of the configuration properties for named features: "microsphere.spring.cloud.features.named."
+ */
+ String NAMED_FEATURE_PROPERTY_NAME_PREFIX = PROPERTY_NAME_PREFIX + DOT + NAMED + DOT;
+
+ /**
+ * The pattern of the configuration properties for abstract features: "microsphere.spring.cloud.features.abstract.{module-name}"
+ */
+ String ABSTRACT_FEATURE_PROPERTY_NAME_PATTERN = ABSTRACT_FEATURE_PROPERTY_NAME_PREFIX + PLACEHOLDER;
+
+ /**
+ * The pattern of the configuration properties for named features: "microsphere.spring.cloud.features.named.{module-name}.{feature-name}"
+ */
+ String NAMED_FEATURE_PROPERTY_NAME_PATTERN = NAMED_FEATURE_PROPERTY_NAME_PREFIX + PLACEHOLDER + DOT + PLACEHOLDER;
+
+ /**
+ * The suffix of the bean name for {@link HasFeatures} : ".features"
+ */
+ String BEAN_NAME_SUFFIX = DOT + FEATURES;
+}
diff --git a/microsphere-spring-cloud-commons/src/main/resources/META-INF/config/default/features.yaml b/microsphere-spring-cloud-commons/src/main/resources/META-INF/config/default/features.yaml
new file mode 100644
index 00000000..a3d5a9d0
--- /dev/null
+++ b/microsphere-spring-cloud-commons/src/main/resources/META-INF/config/default/features.yaml
@@ -0,0 +1,121 @@
+microsphere:
+ spring:
+ cloud:
+ features:
+ abstract:
+ microsphere-spring-context:
+ - io.microsphere.spring.beans.factory.annotation.AnnotatedInjectionBeanPostProcessor
+ - io.microsphere.spring.beans.factory.config.GenericBeanPostProcessorAdapter
+ - io.microsphere.spring.context.event.GenericApplicationListenerAdapter
+ - io.microsphere.spring.context.event.OnceApplicationContextEventListener
+ - io.microsphere.spring.context.lifecycle.AbstractSmartLifecycle
+ ## ListenableAutowireCandidateResolverInitializer
+ - io.microsphere.spring.beans.factory.support.ListenableAutowireCandidateResolver
+ - io.microsphere.spring.beans.factory.support.AutowireCandidateResolvingListener
+ ## @EnableTTLCaching
+ - io.microsphere.spring.cache.intereptor.TTLCacheResolver
+ ## @Enable*
+ - io.microsphere.spring.context.annotation.BeanCapableImportCandidate
+ ## @OverrideAnnotationAttributes
+ - io.microsphere.spring.context.annotation.OverrideAnnotationAttributesStrategy
+ ## @EnableAutoRegistrationBean
+ - io.microsphere.spring.context.config.AutoRegistrationBean
+ ## @EnableConfigurationBeanBinding
+ - io.microsphere.spring.context.config.ConfigurationBeanBinder
+ - io.microsphere.spring.context.config.ConfigurationBeanCustomizer
+ - io.microsphere.spring.beans.factory.annotation.ConfigurationBeanBindingPostProcessor
+ ## @EnableEventExtension
+ - io.microsphere.spring.context.event.ApplicationEventInterceptor
+ - io.microsphere.spring.context.event.ApplicationListenerInterceptor
+ - io.microsphere.spring.context.event.InterceptingApplicationEventMulticaster
+ - io.microsphere.spring.context.event.InterceptingApplicationEventMulticasterProxy
+ ## EventPublishingBean*
+ - io.microsphere.spring.context.event.BeanFactoryListener
+ - io.microsphere.spring.context.event.BeanListener
+ - io.microsphere.spring.context.event.EventPublishingBeanBeforeProcessor
+ - io.microsphere.spring.context.event.EventPublishingBeanAfterProcessor
+ ## ListenableConfigurableEnvironment
+ - io.microsphere.spring.core.env.EnvironmentListener
+ - io.microsphere.spring.core.env.ProfileListener
+ - io.microsphere.spring.core.env.PropertyResolverListener
+ ## SpringProtocolURLStreamHandler
+ - io.microsphere.spring.net.SpringProtocolURLStreamHandler
+ microsphere-spring-guice:
+ ## @EnableGuice
+ - io.microsphere.spring.guice.annotation.GuiceInjectAnnotationBeanPostProcessor
+ microsphere-spring-jdbc:
+ ## @EnableP6DataSource
+ - io.microsphere.spring.jdbc.p6spy.beans.factory.config.P6DataSourceBeanPostProcessor
+ microsphere-spring-web:
+ ## @EnableWebExtension
+ - io.microsphere.spring.web.metadata.WebEndpointMappingResolver
+ - io.microsphere.spring.web.metadata.WebEndpointMappingRegistry
+ - io.microsphere.spring.web.metadata.WebEndpointMappingFactory
+ - io.microsphere.spring.web.metadata.WebEndpointMappingFilter
+ - io.microsphere.spring.web.metadata.WebEndpointMappingRegistrar
+ - io.microsphere.spring.web.method.support.HandlerMethodAdvice
+ - io.microsphere.spring.web.method.support.HandlerMethodArgumentInterceptor
+ - io.microsphere.spring.web.method.support.HandlerMethodInterceptor
+ - io.microsphere.spring.web.event.WebEventPublisher
+ microsphere-spring-webflux:
+ - io.microsphere.spring.webflux.server.filter.DelegatingWebFilter
+ - io.microsphere.spring.webflux.server.filter.RequestContextWebFilter
+ ## @EnableWebFluxExtension
+ - io.microsphere.spring.webflux.metadata.HandlerMappingWebEndpointMappingResolver
+ - io.microsphere.spring.webflux.method.InterceptingHandlerMethodProcessor
+ - io.microsphere.spring.webflux.server.filter.RequestHandledEventPublishingWebFilter
+ - io.microsphere.spring.webflux.server.filter.RequestContextWebFilter
+ - io.microsphere.spring.webflux.method.StoringRequestBodyArgumentInterceptor
+ - io.microsphere.spring.webflux.method.StoringResponseBodyReturnValueInterceptor
+ - io.microsphere.spring.webflux.handler.ReversedProxyHandlerMapping
+ microsphere-spring-webmvc:
+ - io.microsphere.spring.web.servlet.filter.ContentCachingFilter
+ ## @EnableWebMvcExtension
+ - io.microsphere.spring.webmvc.metadata.HandlerMappingWebEndpointMappingResolver
+ - io.microsphere.spring.webmvc.method.support.InterceptingHandlerMethodProcessor
+ - io.microsphere.spring.webmvc.interceptor.LazyCompositeHandlerInterceptor
+ - io.microsphere.spring.webmvc.advice.StoringRequestBodyArgumentAdvice
+ - io.microsphere.spring.webmvc.advice.StoringResponseBodyReturnValueAdvice
+ - io.microsphere.spring.webmvc.handler.ReversedProxyHandlerMapping
+ - io.microsphere.spring.web.metadata.ServletWebEndpointMappingResolver
+ microsphere-spring-boot:
+ - io.microsphere.spring.boot.context.config.BindableConfigurationBeanBinder
+ - io.microsphere.spring.boot.context.properties.bind.BindListener
+ - io.microsphere.spring.boot.context.properties.ListenableConfigurationPropertiesBindHandlerAdvisor
+ - io.microsphere.spring.boot.context.OnceApplicationPreparedEventListener
+ - io.microsphere.spring.boot.context.OnceMainApplicationPreparedEventListener
+
+ named:
+ microsphere-spring-context:
+ # InjectionPointDependencyResolver
+ ConstructionInjectionPointDependencyResolver: io.microsphere.spring.beans.factory.ConstructionInjectionPointDependencyResolver
+ AutowiredInjectionPointDependencyResolver: io.microsphere.spring.beans.factory.annotation.AutowiredInjectionPointDependencyResolver
+ ResourceInjectionPointDependencyResolver: io.microsphere.spring.beans.factory.annotation.ResourceInjectionPointDependencyResolver
+ # ApplicationContextInitializer
+ EventPublishingBeanInitializer: io.microsphere.spring.context.event.EventPublishingBeanInitializer
+ ListenableAutowireCandidateResolverInitializer: io.microsphere.spring.beans.factory.support.ListenableAutowireCandidateResolverInitializer
+ ListenableConfigurableEnvironmentInitializer: io.microsphere.spring.core.env.ListenableConfigurableEnvironmentInitializer
+ AutoRegistrationBeanInitializer: io.microsphere.spring.boot.context.config.AutoRegistrationBeanInitializer
+ microsphere-spring-webflux:
+ SpringWebFluxHelper: io.microsphere.spring.webflux.util.SpringWebFluxHelper
+ microsphere-spring-webmvc:
+ SpringWebMvcHelper: io.microsphere.spring.webmvc.util.SpringWebMvcHelper
+ microsphere-spring-boot:
+ # ApplicationContextInitializer
+ ConditionEvaluationReportInitializer: io.microsphere.spring.boot.report.ConditionEvaluationReportInitializer
+ OriginTrackedConfigurationPropertyInitializer: io.microsphere.spring.boot.env.config.OriginTrackedConfigurationPropertyInitializer
+ # SpringApplicationRunListener
+ BannedArtifactClassLoadingListener: io.microsphere.spring.boot.classloading.BannedArtifactClassLoadingListener
+ FailureReportSpringApplicationRunListener: io.microsphere.spring.boot.listener.FailureReportSpringApplicationRunListener
+ # ApplicationListener
+ ArtifactsCollisionDiagnosisListener: io.microsphere.spring.boot.diagnostics.ArtifactsCollisionDiagnosisListener
+ ConditionEvaluationReportListener: io.microsphere.spring.boot.report.ConditionEvaluationReportListener
+ DefaultPropertiesApplicationListener: io.microsphere.spring.boot.env.DefaultPropertiesApplicationListener
+ # SpringBootExceptionReporter
+ ConditionEvaluationSpringBootExceptionReporter: io.microsphere.spring.boot.report.ConditionEvaluationSpringBootExceptionReporter
+ # FailureAnalyzer
+ ArtifactsCollisionFailureAnalyzer: io.microsphere.spring.boot.diagnostics.ArtifactsCollisionFailureAnalyzer
+ # AutoConfigurationImportFilter
+ ConfigurableAutoConfigurationImportFilter: io.microsphere.spring.boot.autoconfigure.ConfigurableAutoConfigurationImportFilter
+ # DefaultPropertiesPostProcessor
+ SpringApplicationDefaultPropertiesPostProcessor: io.microsphere.spring.boot.env.SpringApplicationDefaultPropertiesPostProcessor
diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/actuator/ConfigurationPropertyHasFeaturesAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/actuator/ConfigurationPropertyHasFeaturesAutoConfigurationTest.java
index 67807fd0..a7aca84f 100644
--- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/actuator/ConfigurationPropertyHasFeaturesAutoConfigurationTest.java
+++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/actuator/ConfigurationPropertyHasFeaturesAutoConfigurationTest.java
@@ -22,23 +22,18 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.client.actuator.FeaturesEndpoint;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.actuator.NamedFeature;
-import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Map;
-import static io.microsphere.spring.cloud.client.actuator.ConfigurationPropertyHasFeaturesAutoConfiguration.ABSTRACT_FEATURE_PROPERTY_NAME_PATTERN;
-import static io.microsphere.spring.cloud.client.actuator.ConfigurationPropertyHasFeaturesAutoConfiguration.BEAN_NAME_SUFFIX;
-import static io.microsphere.spring.cloud.client.actuator.ConfigurationPropertyHasFeaturesAutoConfiguration.NAME;
-import static io.microsphere.spring.cloud.client.actuator.ConfigurationPropertyHasFeaturesAutoConfiguration.NAMED_FEATURE_PROPERTY_NAME_PATTERN;
-import static io.microsphere.spring.cloud.client.actuator.ConfigurationPropertyHasFeaturesAutoConfiguration.PROPERTY_PREFIX;
-import static io.microsphere.spring.cloud.client.actuator.ConfigurationPropertyHasFeaturesAutoConfiguration.getBeanName;
-import static io.microsphere.spring.cloud.client.actuator.ConfigurationPropertyHasFeaturesAutoConfiguration.getQualifierFeatureName;
+import static io.microsphere.spring.cloud.client.actuator.FeaturesUtils.getHasFeaturesBeanName;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
@@ -49,15 +44,19 @@
* @since 1.0.0
*/
@SpringBootTest(
- classes = ConfigurationPropertyHasFeaturesAutoConfigurationTest.class,
+ classes = {
+ RestTemplate.class,
+ ConfigurationPropertyHasFeaturesAutoConfigurationTest.class
+ },
properties = {
- "microsphere.spring.cloud.features.jdbc.JdbcTemplate=org.springframework.jdbc.core.JdbcTemplate",
- "microsphere.spring.cloud.features.rest.RestTemplate=org.springframework.web.client.RestTemplate",
- "microsphere.spring.cloud.features.rest=org.springframework.web.client.RestOperations",
- "microsphere.spring.cloud.features.redis.RedisConnection=org.springframework.data.redis.connection.RedisConnection",
- "microsphere.spring.cloud.features.redis=org.springframework.data.redis.connection.RedisConnectionFactory," +
+ "microsphere.spring.cloud.features.abstract.rest=org.springframework.web.client.RestOperations",
+ "microsphere.spring.cloud.features.abstract.redis=org.springframework.data.redis.connection.RedisConnectionFactory," +
"org.springframework.data.redis.core.RedisTemplate," +
"org.springframework.data.redis.core.StringRedisTemplate",
+ "microsphere.spring.cloud.features.named.jdbc.JdbcTemplate=org.springframework.jdbc.core.JdbcTemplate",
+ "microsphere.spring.cloud.features.named.rest.RestTemplate=org.springframework.web.client.RestTemplate",
+ "microsphere.spring.cloud.features.named.redis.RedisConnection=org.springframework.data.redis.connection.RedisConnection",
+ "microsphere.spring.cloud.features.named.web.WebClient=org.springframework.web.reactive.function.client.WebClient"
}
)
@EnableAutoConfiguration
@@ -66,42 +65,35 @@ class ConfigurationPropertyHasFeaturesAutoConfigurationTest {
@Autowired
private Map hasFeaturesBeansMap;
+ @Autowired
+ private FeaturesEndpoint featuresEndpoint;
+
@Test
void test() {
+ HasFeatures hasFeatures = this.hasFeaturesBeansMap.get(getHasFeaturesBeanName("jdbc"));
+ assertNull(hasFeatures);
- testConstants();
+ hasFeatures = this.hasFeaturesBeansMap.get(getHasFeaturesBeanName("redis"));
+ assertNull(hasFeatures);
- HasFeatures hasFeatures = this.hasFeaturesBeansMap.get(getBeanName("jdbc"));
+ hasFeatures = this.hasFeaturesBeansMap.get(getHasFeaturesBeanName("web"));
assertNotNull(hasFeatures);
- assertTrue(hasFeatures.getAbstractFeatures().isEmpty());
- assertTrue(hasFeatures.getNamedFeatures().isEmpty());
-
- hasFeatures = this.hasFeaturesBeansMap.get(getBeanName("rest"));
- assertNotNull(hasFeatures);
-
List> abstractFeatures = hasFeatures.getAbstractFeatures();
- assertEquals(1, abstractFeatures.size());
- assertEquals(RestOperations.class, abstractFeatures.get(0));
-
List namedFeatures = hasFeatures.getNamedFeatures();
assertEquals(1, namedFeatures.size());
- NamedFeature namedFeature = namedFeatures.get(0);
- assertEquals(getQualifierFeatureName("rest", "RestTemplate"), namedFeature.getName());
- assertEquals(RestTemplate.class, namedFeature.getType());
+ assertEquals("web:WebClient", namedFeatures.get(0).getName());
+ assertTrue(abstractFeatures.isEmpty());
- hasFeatures = this.hasFeaturesBeansMap.get(getBeanName("redis"));
+ hasFeatures = this.hasFeaturesBeansMap.get(getHasFeaturesBeanName("rest"));
assertNotNull(hasFeatures);
- assertTrue(hasFeatures.getAbstractFeatures().isEmpty());
- assertTrue(hasFeatures.getNamedFeatures().isEmpty());
- }
+ abstractFeatures = hasFeatures.getAbstractFeatures();
+ namedFeatures = hasFeatures.getNamedFeatures();
+ assertEquals("rest:RestTemplate", namedFeatures.get(0).getName());
+ assertEquals(1, namedFeatures.size());
+ assertTrue(abstractFeatures.isEmpty());
- private void testConstants() {
- assertEquals("features", NAME);
- assertEquals("microsphere.spring.cloud.features.", PROPERTY_PREFIX);
- assertEquals("microsphere.spring.cloud.features.{}", ABSTRACT_FEATURE_PROPERTY_NAME_PATTERN);
- assertEquals("microsphere.spring.cloud.features.{}.{}", NAMED_FEATURE_PROPERTY_NAME_PATTERN);
- assertEquals(".features", BEAN_NAME_SUFFIX);
- }
+ assertNotNull(featuresEndpoint.features());
+ }
}
\ No newline at end of file
diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/actuator/FeaturesUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/actuator/FeaturesUtilsTest.java
new file mode 100644
index 00000000..c8395c7e
--- /dev/null
+++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/actuator/FeaturesUtilsTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package io.microsphere.spring.cloud.client.actuator;
+
+
+import org.junit.jupiter.api.Test;
+
+import static io.microsphere.spring.cloud.client.actuator.FeaturesUtils.getAbstractFeaturePropertyName;
+import static io.microsphere.spring.cloud.client.actuator.FeaturesUtils.getHasFeaturesBeanName;
+import static io.microsphere.spring.cloud.client.actuator.FeaturesUtils.getNamedFeaturePropertyName;
+import static io.microsphere.spring.cloud.client.actuator.FeaturesUtils.getQualifierFeatureName;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * {@link FeaturesUtils} Test
+ *
+ * @author Mercy
+ * @see FeaturesUtils
+ * @since 1.0.0
+ */
+class FeaturesUtilsTest {
+
+ @Test
+ void testGetAbstractFeaturePropertyName() {
+ String propertyName = getAbstractFeaturePropertyName("jdbc");
+ assertEquals("microsphere.spring.cloud.features.abstract.jdbc", propertyName);
+ }
+
+ @Test
+ void testGetNamedFeaturePropertyName() {
+ String propertyName = getNamedFeaturePropertyName("jdbc", "JdbcTemplate");
+ assertEquals("microsphere.spring.cloud.features.named.jdbc.JdbcTemplate", propertyName);
+ }
+
+ @Test
+ void testGetHasFeaturesBeanName() {
+ String beanName = getHasFeaturesBeanName("jdbc");
+ assertEquals("jdbc.features", beanName);
+ }
+
+ @Test
+ void testGetQualifierFeatureName() {
+ String featureName = getQualifierFeatureName("web", "rest-template");
+ assertEquals("web:rest-template", featureName);
+ }
+}
\ No newline at end of file
diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/actuator/constants/FeaturesConstantsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/actuator/constants/FeaturesConstantsTest.java
new file mode 100644
index 00000000..94b40948
--- /dev/null
+++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/actuator/constants/FeaturesConstantsTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package io.microsphere.spring.cloud.client.actuator.constants;
+
+
+import org.junit.jupiter.api.Test;
+
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.ABSTRACT;
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.ABSTRACT_FEATURE_PROPERTY_NAME_PATTERN;
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.ABSTRACT_FEATURE_PROPERTY_NAME_PREFIX;
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.BEAN_NAME_SUFFIX;
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.FEATURES;
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.NAMED;
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.NAMED_FEATURE_PROPERTY_NAME_PATTERN;
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.NAMED_FEATURE_PROPERTY_NAME_PREFIX;
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.PLACEHOLDER;
+import static io.microsphere.spring.cloud.client.actuator.constants.FeaturesConstants.PROPERTY_NAME_PREFIX;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * {@link FeaturesConstants} Test
+ *
+ * @author Mercy
+ * @see FeaturesConstants
+ * @since 1.0.0
+ */
+class FeaturesConstantsTest {
+
+ @Test
+ void testConstants() {
+ assertEquals("features", FEATURES);
+ assertEquals("abstract", ABSTRACT);
+ assertEquals("named", NAMED);
+ assertEquals("{}", PLACEHOLDER);
+
+ assertEquals("microsphere.spring.cloud.features", PROPERTY_NAME_PREFIX);
+ assertEquals("microsphere.spring.cloud.features.abstract.", ABSTRACT_FEATURE_PROPERTY_NAME_PREFIX);
+ assertEquals("microsphere.spring.cloud.features.named.", NAMED_FEATURE_PROPERTY_NAME_PREFIX);
+ assertEquals("microsphere.spring.cloud.features.abstract.{}", ABSTRACT_FEATURE_PROPERTY_NAME_PATTERN);
+ assertEquals("microsphere.spring.cloud.features.named.{}.{}", NAMED_FEATURE_PROPERTY_NAME_PATTERN);
+ assertEquals(".features", BEAN_NAME_SUFFIX);
+ }
+}
\ No newline at end of file
diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml
index 913cc30d..81d56506 100644
--- a/microsphere-spring-cloud-parent/pom.xml
+++ b/microsphere-spring-cloud-parent/pom.xml
@@ -20,7 +20,7 @@
- 0.1.22
+ 0.1.25
1.21.4
5.14.4