主要思路:
- 读取项目路径中所有META-INF下的pom.properties,读取每行的内容,将项目加入到springboot-actuator的endpoint中,最后加入spring的bean中即可;
- springboot-actuator的 1.x版本 和 2.x版本 的endpoint写法不同,需要注意
- 只需项目启动加载一次,然后放入guava缓存中,后续需要时读取即可
- 首先需要引入guava版本依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
- 创建实体Dependency,用于返回对象属性
package com.xxx.xxx.actuator.entity;
import lombok.Data;
import java.util.Objects;
/**
* @ author Daly.Dai
* @ description
* @ date 2021/6/10
*/
@Data
public class Dependency {
private String groupId;
private String artifactId;
private String version;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dependency that = (Dependency) o;
return Objects.equals(groupId, that.groupId) &&
Objects.equals(artifactId, that.artifactId) &&
Objects.equals(version, that.version);
}
@Override
public int hashCode() {
return Objects.hash(groupId, artifactId, version);
}
}
Endpoint springboot-actuator 1.x 版本写法
- Endpoint GetJarVersionEndpoint,用于具体业务代码处理,最后记得加入configuration
package com.xxx.xxx.actuator.endpoint;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.kedacom.kdip.actuator.entity.Dependency;
import lombok.SneakyThrows;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import javax.annotation.PostConstruct;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
// getJarVersion 用于访问接口
@ConfigurationProperties(prefix = "endpoints.getJarVersion")
@Component
public class GetJarVersionEndpoint extends AbstractEndpoint<Set<Dependency>> {
// 创建缓存对象,使用guava缓存,用于在项目初始加载时候进行缓存
private static final Cache<String, Set<Dependency>> dependencyCache = CacheBuilder.newBuilder().build();
private static final String DEPENDENCY_KEY = "dependencies";
public GetJarVersionEndpoint() {
super("getJarVersion");
}
// 从缓存获取
@Override
public Set<Dependency> invoke() {
return dependencyCache.getIfPresent(DEPENDENCY_KEY);
}
// 项目初始i执行一次,并加入缓存
@SneakyThrows
@PostConstruct
private void initDependency() {
Set<Dependency> dependencies = new LinkedHashSet<>();
// 此方法读取pom.properties文件
PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource[] resources_1 = pathMatchingResourcePatternResolver.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "**/META-INF/**/pom.properties");
for (Resource resource : resources_1) {
Dependency dependency = new Dependency();
// 从jar包读取的文件需要转为流,再转化为字节,然后解析
byte[] data = FileCopyUtils.copyToByteArray(resource.getInputStream());
// 转化为String后,根据实际内容进行分割,此处根据换行符进行分割
String[] content = new String(data, Charset.forName("utf-8")).split("\n");
List<String> list = Arrays.asList(content);
for (String string : list) {
if (string.startsWith("version=") || string.startsWith("groupId=") || string.startsWith("artifactId=")) {
if (string.startsWith("version=")) {
dependency.setVersion(string.replace("version=", ""));
}
if (string.startsWith("groupId=")) {
dependency.setGroupId(string.replace("groupId=", ""));
}
if (string.startsWith("artifactId=")) {
dependency.setArtifactId(string.replace("artifactId=", ""));
}
}
}
dependencies.add(dependency);
}
// 存入guava缓存
dependencyCache.put(DEPENDENCY_KEY, dependencies);
}
// 此代码未用到
private static Resource[] concat(Resource[] a, Resource[] b) {
if (a == null) {
a = new Resource[0];
}
if (b == null) {
b = new Resource[0];
}
Resource[] c = new Resource[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
}
- 加入configuration,读取为spring bean
@Configuration
@EnableConfigurationProperties({GetJarVersionEndpoint.class})
public class ActuatorAutoConfiguration {
}
Endpoint springboot-actuator 2.x 版本写法
package com.xxx.xxx.actuator.endpoint;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.kedacom.kdip.actuator.entity.Dependency;
import lombok.SneakyThrows;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import javax.annotation.PostConstruct;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@WebEndpoint(id = "getJarVersion")
@Component
public class GetJarVersionEndpoint {
private static final Cache<String, Set<Dependency>> dependencyCache = CacheBuilder.newBuilder().build();
private static final String DEPENDENCY_KEY = "dependencies";
@ReadOperation
public Set<Dependency> list() {
return dependencyCache.getIfPresent(DEPENDENCY_KEY);
}
@SneakyThrows
@PostConstruct
private void initDependency() {
Set<Dependency> dependencies = new LinkedHashSet<>();
PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource[] resources_1 = pathMatchingResourcePatternResolver.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "**/META-INF/**/pom.properties");
for (Resource resource : resources_1) {
Dependency dependency = new Dependency();
byte[] data = FileCopyUtils.copyToByteArray(resource.getInputStream());
String[] content = new String(data, Charset.forName("utf-8")).split("\n");
List<String> list = Arrays.asList(content);
for (String string : list) {
if (string.startsWith("version=") || string.startsWith("groupId=") || string.startsWith("artifactId=")) {
if (string.startsWith("version=")) {
dependency.setVersion(string.replace("version=", ""));
}
if (string.startsWith("groupId=")) {
dependency.setGroupId(string.replace("groupId=", ""));
}
if (string.startsWith("artifactId=")) {
dependency.setArtifactId(string.replace("artifactId=", ""));
}
}
}
dependencies.add(dependency);
}
dependencyCache.put(DEPENDENCY_KEY, dependencies);
}
private static Resource[] concat(Resource[] a, Resource[] b) {
if (a == null) {
a = new Resource[0];
}
if (b == null) {
b = new Resource[0];
}
Resource[] c = new Resource[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
}
- 最后通过 http://ip:port/actuator/getJarVersion 访问