1. import com.alibaba.fastjson.JSON;
    2. import org.slf4j.Logger;
    3. import org.slf4j.LoggerFactory;
    4. import javax.servlet.ServletContext;
    5. import javax.ws.rs.*;
    6. import javax.ws.rs.core.Context;
    7. import javax.ws.rs.core.MediaType;
    8. import java.io.File;
    9. import java.io.IOException;
    10. import java.lang.annotation.Annotation;
    11. import java.lang.reflect.Method;
    12. import java.lang.reflect.Parameter;
    13. import java.nio.charset.StandardCharsets;
    14. import java.nio.file.Files;
    15. import java.util.*;
    16. @Path("/api-doc")
    17. public class ApiDocGenerator {
    18. public static final Logger LOGGER = LoggerFactory.getLogger(ApiDocGenerator.class);
    19. public String baseUrl = "http://localhost:port/api";
    20. public String apiOutputUrl = System.getProperty("user.home") + "/api-info-doc.json";
    21. public static final String CLASSES_PATH = "classes/";
    22. public static final String CLASS_SUFFIX = ".class";
    23. private static final String API_DOC_KEY = "api-doc-key";
    24. private HashSet requestTypes = new HashSet();
    25. @GET
    26. @Path("/info")
    27. @Produces(MediaType.APPLICATION_JSON)
    28. public Object apiDoc(@Context ServletContext context) throws Exception {
    29. Object attribute = context.getAttribute(API_DOC_KEY);
    30. if (Objects.isNull(attribute)) {
    31. generateApiInfo(context);
    32. }
    33. return context.getAttribute(API_DOC_KEY);
    34. }
    35. private synchronized void generateApiInfo(ServletContext context) throws IOException {
    36. if (requestTypes.isEmpty()) {
    37. requestTypes.addAll(Arrays.asList(HttpMethod.GET, HttpMethod.POST, HttpMethod.DELETE, HttpMethod.PUT));
    38. }
    39. String name = this.getClass().getResource("/").getPath();
    40. File dir = new File(name);
    41. File[] dirs = dir.listFiles();
    42. List<File> files = new ArrayList<>();
    43. for (File file : dirs) {
    44. files.addAll(Arrays.asList(file.listFiles()));
    45. }
    46. loadClasses(files, context);
    47. }
    48. private void loadClasses(List<File> files, ServletContext context) throws IOException {
    49. Map<String, List<ApiInfo>> apiInfoMap = new HashMap<>();
    50. files.stream().forEach(file -> parseClassFiles(apiInfoMap, file));
    51. writeToFile(apiInfoMap, context);
    52. }
    53. private void writeToFile(Map<String, List<ApiInfo>> apiInfoMap, ServletContext context) throws IOException {
    54. String apiInfo = JSON.toJSONString(apiInfoMap);
    55. context.setAttribute(API_DOC_KEY, apiInfo);
    56. java.nio.file.Path path = new File(apiOutputUrl).toPath();
    57. Files.deleteIfExists(path);
    58. Files.createFile(path).toFile();
    59. Files.write(path, apiInfo.getBytes(StandardCharsets.UTF_8));
    60. LOGGER.info("api info doc path is :{}", path);
    61. }
    62. private void parseClassFiles(Map<String, List<ApiInfo>> apiInfoMap, File file) {
    63. if (!file.isFile() || !file.getName().endsWith(CLASS_SUFFIX)) {
    64. return;
    65. }
    66. List<ApiInfo> apis = new ArrayList<>();
    67. String url = file.toURI().toString();
    68. String className = url.substring(url.lastIndexOf(CLASSES_PATH) + CLASSES_PATH.length())
    69. .replace(CLASS_SUFFIX, "")
    70. .replace("/", ".");
    71. Class<?> clazz = null;
    72. try {
    73. clazz = this.getClass().getClassLoader().loadClass(className);
    74. } catch (ClassNotFoundException e) {
    75. e.printStackTrace();
    76. }
    77. if (Objects.isNull(clazz)) {
    78. return;
    79. }
    80. String description = clazz.getSimpleName()
    81. .replace("Controller", "")
    82. .replace("Rest", "");
    83. Api api = clazz.getAnnotation(Api.class);
    84. if (Objects.nonNull(api)) {
    85. description = api.description();
    86. }
    87. Path rootPath = clazz.getAnnotation(Path.class);
    88. String value = rootPath.value();
    89. if (Objects.isNull(rootPath)) {
    90. return;
    91. }
    92. Method[] methods = clazz.getDeclaredMethods();
    93. for (Method method : methods) {
    94. buildApiInfo(apis, value, method);
    95. }
    96. apiInfoMap.put(description, apis);
    97. }
    98. private void buildApiInfo(List<ApiInfo> apis, String value, Method method) {
    99. ApiInfo apiInfo = new ApiInfo();
    100. apiInfo.setBaseUrl(baseUrl);
    101. Path urlPath = method.getAnnotation(Path.class);
    102. if (urlPath == null) {
    103. return;
    104. }
    105. Annotation[] annotations = method.getAnnotations();
    106. Annotation requestType = Arrays.stream(annotations)
    107. .filter(item -> requestTypes.contains(item.annotationType().getSimpleName()))
    108. .findFirst()
    109. .get();
    110. Api api = method.getAnnotation(Api.class);
    111. apiInfo.setRequestType(requestType.annotationType().getSimpleName());
    112. String url = value + urlPath.value();
    113. apiInfo.setFullUrl(baseUrl + url);
    114. apiInfo.setRequestUrl(url);
    115. parseRequestParams(method, apiInfo);
    116. String desc = StrUtil.isEmpty(api.description()) ?
    117. method.getName() : method.getName() + ":" + api.description();
    118. apiInfo.setDescription(desc);
    119. apiInfo.setReturnType("JSON");
    120. apis.add(apiInfo);
    121. }
    122. private void parseRequestParams(Method method, ApiInfo api) {
    123. Parameter[] parameters = method.getParameters();
    124. List<Param> parameterList = new ArrayList();
    125. for (Parameter parameter : parameters) {
    126. Class<?> parameterType = parameter.getType();
    127. String paramType = "";
    128. String name = "";
    129. Annotation[] annotations = parameter.getAnnotations();
    130. if (annotations[0].equals(FormParam.class)){
    131. name = parameter.getAnnotation(FormParam.class).value();
    132. paramType = "body(JSON)";
    133. }
    134. if (annotations[0].equals(HeaderParam.class)){
    135. name = parameter.getAnnotation(HeaderParam.class).value();
    136. paramType = "header(k=v)";
    137. }
    138. if (annotations[0].equals(QueryParam.class)){
    139. name = parameter.getAnnotation(QueryParam.class).value();
    140. paramType = "header(k=v)";
    141. }
    142. if (annotations[0].equals(PathParam.class)){
    143. name = parameter.getAnnotation(PathParam.class).value();
    144. paramType = "path({parameterValue})";
    145. }
    146. parameterList.add(new Param(name, parameterType.getSimpleName(), paramType));
    147. }
    148. api.setParameters(parameterList);
    149. }
    150. class ApiInfo {
    151. private String baseUrl;
    152. private String fullUrl;
    153. private String requestUrl;
    154. private String requestType;
    155. private List<Param> parameters;
    156. private String description;
    157. private String returnType;
    158. public String getBaseUrl() {
    159. return baseUrl;
    160. }
    161. public void setBaseUrl(String baseUrl) {
    162. this.baseUrl = baseUrl;
    163. }
    164. public String getFullUrl() {
    165. return fullUrl;
    166. }
    167. public void setFullUrl(String fullUrl) {
    168. this.fullUrl = fullUrl;
    169. }
    170. public String getRequestUrl() {
    171. return requestUrl;
    172. }
    173. public void setRequestUrl(String requestUrl) {
    174. this.requestUrl = requestUrl;
    175. }
    176. public String getRequestType() {
    177. return requestType;
    178. }
    179. public void setRequestType(String requestType) {
    180. this.requestType = requestType;
    181. }
    182. public List<Param> getParameters() {
    183. return parameters;
    184. }
    185. public void setParameters(List<Param> parameters) {
    186. this.parameters = parameters;
    187. }
    188. public String getDescription() {
    189. return description;
    190. }
    191. public void setDescription(String description) {
    192. this.description = description;
    193. }
    194. public String getReturnType() {
    195. return returnType;
    196. }
    197. public void setReturnType(String returnType) {
    198. this.returnType = returnType;
    199. }
    200. }
    201. class Param {
    202. private String name;
    203. private String dataType;
    204. private String paramType;
    205. public Param(String name, String dataType, String paramType) {
    206. this.name = name;
    207. this.dataType = dataType;
    208. this.paramType = paramType;
    209. }
    210. public String getDataType() {
    211. return dataType;
    212. }
    213. public void setDataType(String dataType) {
    214. this.dataType = dataType;
    215. }
    216. public String getParamType() {
    217. return paramType;
    218. }
    219. public void setParamType(String paramType) {
    220. this.paramType = paramType;
    221. }
    222. public String getName() {
    223. return name;
    224. }
    225. public void setName(String name) {
    226. this.name = name;
    227. }
    228. }
    229. static class StrUtil {
    230. public static String property(String column) {
    231. if (StrUtil.isEmpty(column) || !column.contains("_")) {
    232. return column;
    233. }
    234. StringBuilder property = new StringBuilder();
    235. int index = -1;
    236. for (int i = 0; i < column.length(); i++) {
    237. if ('_' == column.charAt(i)) {
    238. index = i + 1;
    239. } else if (i == index) {
    240. property.append(Character.toUpperCase(column.charAt(i)));
    241. } else {
    242. property.append(column.charAt(i));
    243. }
    244. }
    245. return property.toString();
    246. }
    247. public static boolean isEmpty(CharSequence str) {
    248. return str == null || str.length() == 0;
    249. }
    250. }
    251. public @interface Api {
    252. String description();
    253. }
    254. }