建议按顺序阅读,请戳《初识篇》《WEB Endpoints》
JMX Endpoints,以JMX形式对外提供访问接口。(JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。)
█ 自动配置
META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration
- EndpointAutoConfiguration
公共配置,对于Web EndPoints与JMX EndPoints共用的配置。
@Configuration(proxyBeanMethods = false)
public class EndpointAutoConfiguration {
// 负责方法参数的转换的参数值映射器
@Bean
@ConditionalOnMissingBean
public ParameterValueMapper endpointOperationParameterMapper(
// 自动注入被@EndpointConverter标注的Converter与GenericConverter类型的bean
// Spring Boot Actuator默认没有提供,可自定义添加
@EndpointConverter ObjectProvider<Converter<?, ?>> converters,
@EndpointConverter ObjectProvider<GenericConverter> genericConverters) {
ConversionService conversionService = createConversionService(
converters.orderedStream().collect(Collectors.toList()),
genericConverters.orderedStream().collect(Collectors.toList()));
return new ConversionServiceParameterValueMapper(conversionService);
}
// 参数转换器
private ConversionService createConversionService(List<Converter<?, ?>> converters,
List<GenericConverter> genericConverters) {
// 默认为空,满足if条件
if (genericConverters.isEmpty() && converters.isEmpty()) {
// 添加Spring Boot默认提供的转换器
// ApplicationConversionService.getSharedInstance()也会调用new ApplicationConversionService();来添加默认提供的转换器
return ApplicationConversionService.getSharedInstance();
}
// 如果有自定义的转换器,在自定义的基础上,加上Spring Boot默认提供的转化器
ApplicationConversionService conversionService = new ApplicationConversionService();
converters.forEach(conversionService::addConverter);
genericConverters.forEach(conversionService::addConverter);
return conversionService;
}
// 缓存切面,第一次访问后,会将结果缓存起来。下一次再获取直接从缓存中取
// 可以设置缓存失效时间
@Bean
@ConditionalOnMissingBean
public CachingOperationInvokerAdvisor endpointCachingOperationInvokerAdvisor(Environment environment) {
return new CachingOperationInvokerAdvisor(new EndpointIdTimeToLivePropertyFunction(environment));
}
}
自定义转化器:
@Configuration
public class CustomConversionConfiguration{
/**
* 自定义Converter
* Converter接口是org.springframework.core.convert.converter.Converter
*
* 将Integer类型的值转换成String类型
*
*/
@Bean
// 加上注解标注给EndPoint提供服务
@EndpointConverter
public Converter<Integer, String> customConverter() {
return new CustomConverter();
}
static class CustomConverter implements Converter<Integer, String>{
@Override
public String convert(Integer source) {
if (source == null) {
return null;
}
return source.toString();
}
}
/**
*
* 自定义GenericConverter
* GenericConverter接口是:org.springframework.core.convert.converter.GenericConverter
*
* @return
*/
@Bean
// 加上注解标注给EndPoint提供服务
@EndpointConverter
public GenericConverter customGenericConverter() {
return new GenericConverter() {
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
Set<ConvertiblePair> convertiblePairSet = new HashSet<>(2);
// 将Integer类型的值转换成String类型的值
convertiblePairSet.add(new ConvertiblePair(Integer.class, String.class));
return convertiblePairSet;
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
// 实现自定义逻辑
return null;
}
};
}
}
- JmxEndpointAutoConfiguration
用于JMX EndPoints构建的相关配置。
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(JmxAutoConfiguration.class)
// JmxEndpointProperties:management.endpoints.jmx前缀的配置
@EnableConfigurationProperties(JmxEndpointProperties.class)
// 通过配置 spring.jmx.enabled=false关闭JMX Endpoints功能
@ConditionalOnProperty(prefix = "spring.jmx", name = "enabled", havingValue = "true")
public class JmxEndpointAutoConfiguration {
private final ApplicationContext applicationContext;
private final JmxEndpointProperties properties;
// 构造器注入
public JmxEndpointAutoConfiguration(ApplicationContext applicationContext, JmxEndpointProperties properties) {
this.applicationContext = applicationContext;
this.properties = properties;
}
// 创建JmxEndpointDiscoverer
// 根据@EndPoint、@JmxEndPoint标注的类
@Bean
@ConditionalOnMissingBean(JmxEndpointsSupplier.class)
public JmxEndpointDiscoverer jmxAnnotationEndpointDiscoverer(ParameterValueMapper parameterValueMapper,
ObjectProvider<OperationInvokerAdvisor> invokerAdvisors,
ObjectProvider<EndpointFilter<ExposableJmxEndpoint>> filters) {
return new JmxEndpointDiscoverer(this.applicationContext, parameterValueMapper,
invokerAdvisors.orderedStream().collect(Collectors.toList()),
filters.orderedStream().collect(Collectors.toList()));
}
// 创建和暴露JMX服务
// 参数jmxEndpointsSupplier会自动注入JmxEndpointDiscoverer
@Bean
@ConditionalOnSingleCandidate(MBeanServer.class)
public JmxEndpointExporter jmxMBeanExporter(MBeanServer mBeanServer, Environment environment,
ObjectProvider<ObjectMapper> objectMapper, JmxEndpointsSupplier jmxEndpointsSupplier) {
String contextId = ObjectUtils.getIdentityHexString(this.applicationContext);
EndpointObjectNameFactory objectNameFactory = new DefaultEndpointObjectNameFactory(this.properties, environment,
mBeanServer, contextId);
JmxOperationResponseMapper responseMapper = new JacksonJmxOperationResponseMapper(
objectMapper.getIfAvailable());
return new JmxEndpointExporter(mBeanServer, objectNameFactory, responseMapper,
jmxEndpointsSupplier.getEndpoints());
}
// EndPoint jmx服务暴露过滤器
// 根据配置:management.endpoints.jmx.exposure.include 设置暴露服务
// 根据配置:management.endpoints.jmx.exposure.exclude 设置不暴露服务
@Bean
public ExposeExcludePropertyEndpointFilter<ExposableJmxEndpoint> jmxIncludeExcludePropertyEndpointFilter() {
JmxEndpointProperties.Exposure exposure = this.properties.getExposure();
// 默认暴露所有服务
return new ExposeExcludePropertyEndpointFilter<>(ExposableJmxEndpoint.class, exposure.getInclude(),
exposure.getExclude(), "*");
}
}
█ 创建和暴露服务
在配置类JmxEndpointAutoConfiguration中创建JmxEndpointExporter时:
@Bean
@ConditionalOnSingleCandidate(MBeanServer.class)
public JmxEndpointExporter jmxMBeanExporter(MBeanServer mBeanServer, Environment environment,
ObjectProvider<ObjectMapper> objectMapper, JmxEndpointsSupplier jmxEndpointsSupplier) {
String contextId = ObjectUtils.getIdentityHexString(this.applicationContext);
EndpointObjectNameFactory objectNameFactory = new DefaultEndpointObjectNameFactory(this.properties, environment,
mBeanServer, contextId);
JmxOperationResponseMapper responseMapper = new JacksonJmxOperationResponseMapper(
objectMapper.getIfAvailable());
return new JmxEndpointExporter(mBeanServer, objectNameFactory, responseMapper,
jmxEndpointsSupplier.getEndpoints());
}
创建过程:
jmxEndpointsSupplier.getEndpoints(),jmxEndpointsSupplier会被自动注入JmxEndpointExporter,调用JmxEndpointExporter的getEndpoints,JmxEndpointExporter也是同样继承了EndpointDiscoverer,创建过程和WebEndpointDiscoverer一样,只是重写的createEndpoint创建的ExposableEndpoint类型不同,其返回DiscoveredJmxEndpoint类型:(关于WebEndpointDiscoverer创建过程,请戳《WEB Endpoints》)
@Override
protected ExposableJmxEndpoint createEndpoint(Object endpointBean, EndpointId id, boolean enabledByDefault,
Collection<JmxOperation> operations) {
return new DiscoveredJmxEndpoint(this, endpointBean, id, enabledByDefault, operations);
}
暴露过程:
new JmxEndpointExporter
return new JmxEndpointExporter(mBeanServer, objectNameFactory, responseMapper,
jmxEndpointsSupplier.getEndpoints());
(1)JmxEndpointExporter实现了InitializingBean接口,会在Spring初始化Bean的时候,调用afterPropertiesSet方法:
@Override
public void afterPropertiesSet() {
this.registered = register();
}
(2)register
private Collection<ObjectName> register() {
return this.endpoints.stream().map(this::register).collect(Collectors.toList());
}
this::register使用的是java8提供的新特性方法引用,即调用JmxEndpointExporter的有参方法register,参数是endpoints集合中的元素,是DiscoveredJmxEndpoint类型。
(3)register(endpoint)
// JmxEndpointExporter#register
private ObjectName register(ExposableJmxEndpoint endpoint) {
// endpoint是DiscoveredJmxEndpoint
Assert.notNull(endpoint, "Endpoint must not be null");
try {
ObjectName name = this.objectNameFactory.getObjectName(endpoint);
// 创建MBean
EndpointMBean mbean = new EndpointMBean(this.responseMapper, this.classLoader, endpoint);
// 注册MBean
this.mBeanServer.registerMBean(mbean, name);
return name;
}
catch (MalformedObjectNameException ex) {
throw new IllegalStateException("Invalid ObjectName for " + getEndpointDescription(endpoint), ex);
}
catch (Exception ex) {
throw new MBeanExportException("Failed to register MBean for " + getEndpointDescription(endpoint), ex);
}
}
this.mBeanServer.registerMBean(mbean, name); 这边属于调用JDK提供的API了。