本文主要研究下springcloud的featuresEndpoint

/actuator/features

{
  "enabled": [
    {
      "type": "com.netflix.discovery.EurekaClient",
      "name": "Eureka Client",
      "version": "1.8.8",
      "vendor": null
    },
    {
      "type": "org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient",
      "name": "DiscoveryClient",
      "version": "2.0.0.RC1",
      "vendor": "Pivotal Software, Inc."
    },
    {
      "type": "org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient",
      "name": "LoadBalancerClient",
      "version": "2.0.0.RC1",
      "vendor": "Pivotal Software, Inc."
    },
    {
      "type": "com.netflix.ribbon.Ribbon",
      "name": "Ribbon",
      "version": "2.2.5",
      "vendor": null
    },
    {
      "type": "feign.Feign",
      "name": "Feign",
      "version": null,
      "vendor": null
    }
  ],
  "disabled": []
}

CommonsClientAutoConfiguration

spring-cloud-commons-2.0.0.RC1-sources.jar!/org/springframework/cloud/client/CommonsClientAutoConfiguration.java

@Configuration
@AutoConfigureOrder(0)
public class CommonsClientAutoConfiguration {

    @Configuration
    @EnableConfigurationProperties(DiscoveryClientHealthIndicatorProperties.class)
    @ConditionalOnClass(HealthIndicator.class)
    @ConditionalOnBean(DiscoveryClient.class)
    @ConditionalOnProperty(value = "spring.cloud.discovery.enabled", matchIfMissing = true)
    protected static class DiscoveryLoadBalancerConfiguration {
        @Bean
        @ConditionalOnProperty(value = "spring.cloud.discovery.client.health-indicator.enabled", matchIfMissing = true)
        public DiscoveryClientHealthIndicator discoveryClientHealthIndicator(
                DiscoveryClient discoveryClient, DiscoveryClientHealthIndicatorProperties properties) {
            return new DiscoveryClientHealthIndicator(discoveryClient, properties);
        }

        @Bean
        @ConditionalOnProperty(value = "spring.cloud.discovery.client.composite-indicator.enabled", matchIfMissing = true)
        @ConditionalOnBean(DiscoveryHealthIndicator.class)
        public DiscoveryCompositeHealthIndicator discoveryCompositeHealthIndicator(
                HealthAggregator aggregator, List<DiscoveryHealthIndicator> indicators) {
            return new DiscoveryCompositeHealthIndicator(aggregator, indicators);
        }

        @Bean
        public HasFeatures commonsFeatures() {
            return HasFeatures.abstractFeatures(DiscoveryClient.class,
                    LoadBalancerClient.class);
        }
    }

    @Configuration
    @ConditionalOnClass(Endpoint.class)
    @ConditionalOnProperty(value = "spring.cloud.features.enabled", matchIfMissing = true)
    protected static class ActuatorConfiguration {
        @Autowired(required = false)
        private List<HasFeatures> hasFeatures = new ArrayList<>();

        @Bean
        @ConditionalOnEnabledEndpoint
        public FeaturesEndpoint featuresEndpoint() {
            return new FeaturesEndpoint(this.hasFeatures);
        }
    }

}
这里有一个ActuatorConfiguration,用来注册featuresEndpoint

FeaturesEndpoint

spring-cloud-commons-2.0.0.RC1-sources.jar!/org/springframework/cloud/client/actuator/FeaturesEndpoint.java

@Endpoint(id = "features")
public class FeaturesEndpoint
        implements ApplicationContextAware {

    private final List<HasFeatures> hasFeaturesList;
    private ApplicationContext context;

    public FeaturesEndpoint(List<HasFeatures> hasFeaturesList) {
        this.hasFeaturesList = hasFeaturesList;
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.context = context;
    }

    @ReadOperation
    public Features features() {
        Features features = new Features();

        for (HasFeatures hasFeatures : this.hasFeaturesList) {
            List<Class<?>> abstractFeatures = hasFeatures.getAbstractFeatures();
            if (abstractFeatures != null) {
                for (Class<?> clazz : abstractFeatures) {
                    addAbstractFeature(features, clazz);
                }
            }

            List<NamedFeature> namedFeatures = hasFeatures.getNamedFeatures();
            if (namedFeatures != null) {
                for (NamedFeature namedFeature : namedFeatures) {
                    addFeature(features, namedFeature);
                }
            }
        }

        return features;
    }

    private void addAbstractFeature(Features features, Class<?> type) {
        String featureName = type.getSimpleName();
        try {
            Object bean = this.context.getBean(type);
            Class<?> beanClass = bean.getClass();
            addFeature(features, new NamedFeature(featureName, beanClass));
        }
        catch (NoSuchBeanDefinitionException e) {
            features.getDisabled().add(featureName);
        }
    }

    private void addFeature(Features features, NamedFeature feature) {
        Class<?> type = feature.getType();
        features.getEnabled()
                .add(new Feature(feature.getName(), type.getCanonicalName(),
                        type.getPackage().getImplementationVersion(),
                        type.getPackage().getImplementationVendor()));
    }
    //......
}
这里通过hasFeaturesList来组装两类features,一类是abstractFeatures,另外一类是namedFeatures

HasFeatures

spring-cloud-commons-2.0.0.RC1-sources.jar!/org/springframework/cloud/client/actuator/HasFeatures.java

public class HasFeatures {

    private final List<Class<?>> abstractFeatures = new ArrayList<>();

    private final List<NamedFeature> namedFeatures = new ArrayList<>();

    public static HasFeatures abstractFeatures(Class<?>... abstractFeatures) {
        return new HasFeatures(Arrays.asList(abstractFeatures),
                Collections.<NamedFeature> emptyList());
    }

    public static HasFeatures namedFeatures(NamedFeature... namedFeatures) {
        return new HasFeatures(Collections.<Class<?>> emptyList(),
                Arrays.asList(namedFeatures));
    }

    public static HasFeatures namedFeature(String name, Class<?> type) {
        return namedFeatures(new NamedFeature(name, type));
    }

    public static HasFeatures namedFeatures(String name1, Class<?> type1, String name2,
            Class<?> type2) {
        return namedFeatures(new NamedFeature(name1, type1),
                new NamedFeature(name2, type2));
    }

    public HasFeatures(List<Class<?>> abstractFeatures,
            List<NamedFeature> namedFeatures) {
        this.abstractFeatures.addAll(abstractFeatures);
        this.namedFeatures.addAll(namedFeatures);
    }

    public List<Class<?>> getAbstractFeatures() {
        return this.abstractFeatures;
    }

    public List<NamedFeature> getNamedFeatures() {
        return this.namedFeatures;
    }
}
这里定义了两类features,一类是abstractFeatures,一类是namedFeatures

HasFeatures实例

spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java

@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@Import(DiscoveryClientOptionalArgsConfiguration.class)
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
        CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
@AutoConfigureAfter(name = {"org.springframework.cloud.autoconfigure.RefreshAutoConfiguration",
        "org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration",
        "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration"})
public class EurekaClientAutoConfiguration {

    private ConfigurableEnvironment env;

    public EurekaClientAutoConfiguration(ConfigurableEnvironment env) {
        this.env = env;
    }

    @Bean
    public HasFeatures eurekaFeature() {
        return HasFeatures.namedFeature("Eureka Client", EurekaClient.class);
    }
    //......
}
比如eureka的client就注册了一个名为Eureka Client的namedFeature

小结

springcloud提供的featuresEndpoint,可以方便我们查看系统启动的一些features,进而了解系统特征。

doc


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...