import com.alibaba.fastjson.JSON;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.servlet.ServletContext;import javax.ws.rs.*;import javax.ws.rs.core.Context;import javax.ws.rs.core.MediaType;import java.io.File;import java.io.IOException;import java.lang.annotation.Annotation;import java.lang.reflect.Method;import java.lang.reflect.Parameter;import java.nio.charset.StandardCharsets;import java.nio.file.Files;import java.util.*;@Path("/api-doc")public class ApiDocGenerator { public static final Logger LOGGER = LoggerFactory.getLogger(ApiDocGenerator.class); public String baseUrl = "http://localhost:port/api"; public String apiOutputUrl = System.getProperty("user.home") + "/api-info-doc.json"; public static final String CLASSES_PATH = "classes/"; public static final String CLASS_SUFFIX = ".class"; private static final String API_DOC_KEY = "api-doc-key"; private HashSet requestTypes = new HashSet(); @GET @Path("/info") @Produces(MediaType.APPLICATION_JSON) public Object apiDoc(@Context ServletContext context) throws Exception { Object attribute = context.getAttribute(API_DOC_KEY); if (Objects.isNull(attribute)) { generateApiInfo(context); } return context.getAttribute(API_DOC_KEY); } private synchronized void generateApiInfo(ServletContext context) throws IOException { if (requestTypes.isEmpty()) { requestTypes.addAll(Arrays.asList(HttpMethod.GET, HttpMethod.POST, HttpMethod.DELETE, HttpMethod.PUT)); } String name = this.getClass().getResource("/").getPath(); File dir = new File(name); File[] dirs = dir.listFiles(); List<File> files = new ArrayList<>(); for (File file : dirs) { files.addAll(Arrays.asList(file.listFiles())); } loadClasses(files, context); } private void loadClasses(List<File> files, ServletContext context) throws IOException { Map<String, List<ApiInfo>> apiInfoMap = new HashMap<>(); files.stream().forEach(file -> parseClassFiles(apiInfoMap, file)); writeToFile(apiInfoMap, context); } private void writeToFile(Map<String, List<ApiInfo>> apiInfoMap, ServletContext context) throws IOException { String apiInfo = JSON.toJSONString(apiInfoMap); context.setAttribute(API_DOC_KEY, apiInfo); java.nio.file.Path path = new File(apiOutputUrl).toPath(); Files.deleteIfExists(path); Files.createFile(path).toFile(); Files.write(path, apiInfo.getBytes(StandardCharsets.UTF_8)); LOGGER.info("api info doc path is :{}", path); } private void parseClassFiles(Map<String, List<ApiInfo>> apiInfoMap, File file) { if (!file.isFile() || !file.getName().endsWith(CLASS_SUFFIX)) { return; } List<ApiInfo> apis = new ArrayList<>(); String url = file.toURI().toString(); String className = url.substring(url.lastIndexOf(CLASSES_PATH) + CLASSES_PATH.length()) .replace(CLASS_SUFFIX, "") .replace("/", "."); Class<?> clazz = null; try { clazz = this.getClass().getClassLoader().loadClass(className); } catch (ClassNotFoundException e) { e.printStackTrace(); } if (Objects.isNull(clazz)) { return; } String description = clazz.getSimpleName() .replace("Controller", "") .replace("Rest", ""); Api api = clazz.getAnnotation(Api.class); if (Objects.nonNull(api)) { description = api.description(); } Path rootPath = clazz.getAnnotation(Path.class); String value = rootPath.value(); if (Objects.isNull(rootPath)) { return; } Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { buildApiInfo(apis, value, method); } apiInfoMap.put(description, apis); } private void buildApiInfo(List<ApiInfo> apis, String value, Method method) { ApiInfo apiInfo = new ApiInfo(); apiInfo.setBaseUrl(baseUrl); Path urlPath = method.getAnnotation(Path.class); if (urlPath == null) { return; } Annotation[] annotations = method.getAnnotations(); Annotation requestType = Arrays.stream(annotations) .filter(item -> requestTypes.contains(item.annotationType().getSimpleName())) .findFirst() .get(); Api api = method.getAnnotation(Api.class); apiInfo.setRequestType(requestType.annotationType().getSimpleName()); String url = value + urlPath.value(); apiInfo.setFullUrl(baseUrl + url); apiInfo.setRequestUrl(url); parseRequestParams(method, apiInfo); String desc = StrUtil.isEmpty(api.description()) ? method.getName() : method.getName() + ":" + api.description(); apiInfo.setDescription(desc); apiInfo.setReturnType("JSON"); apis.add(apiInfo); } private void parseRequestParams(Method method, ApiInfo api) { Parameter[] parameters = method.getParameters(); List<Param> parameterList = new ArrayList(); for (Parameter parameter : parameters) { Class<?> parameterType = parameter.getType(); String paramType = ""; String name = ""; Annotation[] annotations = parameter.getAnnotations(); if (annotations[0].equals(FormParam.class)){ name = parameter.getAnnotation(FormParam.class).value(); paramType = "body(JSON)"; } if (annotations[0].equals(HeaderParam.class)){ name = parameter.getAnnotation(HeaderParam.class).value(); paramType = "header(k=v)"; } if (annotations[0].equals(QueryParam.class)){ name = parameter.getAnnotation(QueryParam.class).value(); paramType = "header(k=v)"; } if (annotations[0].equals(PathParam.class)){ name = parameter.getAnnotation(PathParam.class).value(); paramType = "path({parameterValue})"; } parameterList.add(new Param(name, parameterType.getSimpleName(), paramType)); } api.setParameters(parameterList); } class ApiInfo { private String baseUrl; private String fullUrl; private String requestUrl; private String requestType; private List<Param> parameters; private String description; private String returnType; public String getBaseUrl() { return baseUrl; } public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; } public String getFullUrl() { return fullUrl; } public void setFullUrl(String fullUrl) { this.fullUrl = fullUrl; } public String getRequestUrl() { return requestUrl; } public void setRequestUrl(String requestUrl) { this.requestUrl = requestUrl; } public String getRequestType() { return requestType; } public void setRequestType(String requestType) { this.requestType = requestType; } public List<Param> getParameters() { return parameters; } public void setParameters(List<Param> parameters) { this.parameters = parameters; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getReturnType() { return returnType; } public void setReturnType(String returnType) { this.returnType = returnType; } } class Param { private String name; private String dataType; private String paramType; public Param(String name, String dataType, String paramType) { this.name = name; this.dataType = dataType; this.paramType = paramType; } public String getDataType() { return dataType; } public void setDataType(String dataType) { this.dataType = dataType; } public String getParamType() { return paramType; } public void setParamType(String paramType) { this.paramType = paramType; } public String getName() { return name; } public void setName(String name) { this.name = name; } } static class StrUtil { public static String property(String column) { if (StrUtil.isEmpty(column) || !column.contains("_")) { return column; } StringBuilder property = new StringBuilder(); int index = -1; for (int i = 0; i < column.length(); i++) { if ('_' == column.charAt(i)) { index = i + 1; } else if (i == index) { property.append(Character.toUpperCase(column.charAt(i))); } else { property.append(column.charAt(i)); } } return property.toString(); } public static boolean isEmpty(CharSequence str) { return str == null || str.length() == 0; } } public @interface Api { String description(); }}