背景
随着分布式系统逐渐成熟及普遍应用,应用与应用直接通过微服务调用,需要对外部提供相应API接口,为次提供一套切面生成API接口的工具
技术实现
核心代码
切面基础信息
GenerateApi 定义接口切面信息
package fills.tools.generate.aspectj; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface GenerateApi { /* * API-接口名称 */ String name() default ""; /* * API-接口唯一标识 */ String key() default ""; /* * API-接口描述信息 */ String description() default ""; } |
GenerateMethod 定义方法切面信息
package fills.tools.generate.aspectj; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface GenerateMethod { /* * Method-方法名称 */ String name() default ""; /* * Method-方法唯一标识 */ String key() default ""; /* * Method-方法描述信息 */ String description() default ""; } |
GenerateMethodParams 定义方法参数数组切面信息
package fills.tools.generate.aspectj; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface GenerateMethodParams { GenerateMethodParam[] params(); } |
GenerateMethodParam 定义方法参数切面信息
package fills.tools.generate.aspectj; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface GenerateMethodParam { /* * Param-参数名称 */ String name() default ""; /* * Param-参数唯一标识 */ String key() default ""; /* * Param-参数描述信息 */ String description() default ""; /* * Param-参数是否必填 */ boolean isMust() default false; /* * Param-参数是否有子参数 */ boolean isChildren() default false; /* * Param-参数是否隐藏 */ boolean isHidden() default false; } |
GenerateEntity 定义实体对象切面信息
package fills.tools.generate.aspectj; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface GenerateEntity { /* * Param-参数名称 */ String name() default ""; /* * Param-参数唯一标识 */ String key() default ""; /* * Param-参数描述信息 */ String description() default ""; } |
GenerateFiled 定义实体对象属性切面信息
package fills.tools.generate.aspectj; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.FIELD,ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface GenerateFiled { /* * Param-参数名称 */ String name() default ""; /* * Param-参数唯一标识 */ String key() default ""; /* * Param-参数是否必填 */ boolean isMust() default false; /* * Param-参数是否有子参数 */ boolean isChildren() default false; /* * Param-参数是否隐藏 */ boolean isHidden() default false; } |
切面封装接口数据
package fills.tools.generate.impl; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import fills.tools.generate.aspectj.GenerateApi; import fills.tools.generate.aspectj.GenerateEntity; import fills.tools.generate.aspectj.GenerateFiled; import fills.tools.generate.aspectj.GenerateMethod; import fills.tools.generate.aspectj.GenerateMethodParam; import fills.tools.generate.aspectj.GenerateMethodParams; import fills.tools.generate.util.JsonTools; @SuppressWarnings({"rawtypes","unchecked"}) public class GenerateApiCache { private static Logger log = LoggerFactory.getLogger(GenerateApiCache.class); // API类名 方法名 参数名 参数描述对象 public static Map<String, Map<String,Map<String, Map>>> methodReqApiCache = new LinkedHashMap<>(); // 方法名 参数信息 public static Map<String, Map> methodRequestCache = new LinkedHashMap<>(); // 方法名 参数名 参数描述对象 public static Map<String,Map<String, Map>> methodResApiCache = new LinkedHashMap<>(); // 方法名 响应信息 public static Map<String, Map> methodResponseCache = new LinkedHashMap<>(); // 实体对象类型名称 实体对象参数信息 public static Map<String, Map> entityCache = new LinkedHashMap<>(); /** * @description: 获取spring容器Api注解,封装api接口 * @param: context * @return: * @author ysf * @date: 2021/8/12 19:12 */ public static void apiMethodCache(ApplicationContext context){ Map<String, Object> map = context.getBeansWithAnnotation(GenerateApi.class); for(Map.Entry<String, Object> entry : map.entrySet()) { Object obj = entry.getValue(); GenerateApi api = obj.getClass().getAnnotation(GenerateApi.class); String classKey = api.key()+"_"+api.name(); log.debug("接口唯一标识:"+classKey); Map<String,Map<String, Map>> methodMap = methodReqApiCache.get(classKey); if (methodMap == null) { methodMap = new LinkedHashMap<>(); methodReqApiCache.put(classKey,methodMap); } Method[] bs = null; String classPath = obj.getClass().getName().split("[$$]")[0]; try { bs = Class.forName(classPath).getMethods(); } catch (Exception e) { log.debug("反射获取类异常",e); continue; } for (Method m : bs) { Map<String,Map> method = null; if (m.isAnnotationPresent(GenerateMethod.class)) { GenerateMethod ao = m.getDeclaredAnnotation(GenerateMethod.class); String methodKey = ao.key()+"_"+ao.name(); log.debug("方法唯一标识:"+methodKey); method = methodMap.get(methodKey); if(method==null){ method = new LinkedHashMap<>(); methodMap.put(methodKey,method); } GenerateMethodParam[] params ={}; Map<String,GenerateMethodParam> mapParams = new LinkedHashMap<>(); if (m.isAnnotationPresent(GenerateMethodParams.class)) { GenerateMethodParams aip = m.getDeclaredAnnotation(GenerateMethodParams.class); params = aip.params(); if(params.length>0) { for (GenerateMethodParam param : params) { mapParams.put(param.key(), param); } } } Parameter[] parameters = m.getParameters(); if(parameters!=null&¶meters.length>0){ for(int i=0;i<parameters.length;i++){ Parameter p = parameters[i]; GenerateMethodParam gp = mapParams.get(p.getName()); boolean flag = true; if(gp==null){ if(i<params.length){ gp = params[i]; } }else{ flag = false; } setParam(p,gp,method,flag); } setRequestParams(methodKey,parameters); } Class rt = m.getReturnType(); if(rt!=null){ setResponseParams(methodKey,m); } } } } } /** * @description: 封装响应参数 * @param: methodKey * @param: c * @return: * @author ysf * @date: 2021/8/13 11:24 */ private static void setResponseParams(String methodKey,Method m){ Class c = m.getReturnType(); Map param = new LinkedHashMap(); param.put("key", c.getName()); param.put("isMust", false); param.put("type", m.getGenericReturnType().getTypeName()); if(c.isAnnotationPresent(GenerateEntity.class)) { GenerateEntity GenerateEntity = (GenerateEntity) c.getAnnotation(GenerateEntity.class); param.put("name", GenerateEntity.key()); }else{ param.put("name", c.getTypeName()); } Class c2 =null; String res = ""; int dataType = checkDataType(c); String r = ""; if(dataType<0){ c2 = c; r = getJsonStr(c,1); res = "".equals(r) ? c.getName() : r; }else if(dataType==0){ Type tp = m.getGenericReturnType(); if(tp instanceof ParameterizedType){ c2 = (Class<?>) ((ParameterizedType)tp).getActualTypeArguments()[0]; r = getJsonStr(c2,1); res ="["+("".equals(r) ? c2.getName() : r)+"]"; } }else if(dataType>0){ Type tp = m.getGenericReturnType(); if(tp instanceof ParameterizedType){ c2 = (Class<?>) ((ParameterizedType)tp).getActualTypeArguments()[1]; r = getJsonStr(c2,1); res ="{"+("".equals(r) ? c2.getName() : r)+"}"; } } if(c2!=null&&!c2.isPrimitive()){ setMoreParam(c2, param,1); }else { param.put("isMore", "false"); } methodResApiCache.put(methodKey , param); Map map = new HashMap(); map.put("dataType",m.getGenericReturnType().getTypeName()); if(!"".equals(res)) { map.put("dataValue",res); }else{ map.put("dataValue",c.getName()); } methodResponseCache.put(methodKey,map); } /** * @description: 封装请求参数 * @param: methodKey * @param: parameters * @return: * @author ysf * @date: 2021/8/13 11:06 */ private static void setRequestParams(String methodKey,Parameter[] parameters){ StringBuffer sb = new StringBuffer(); StringBuffer sb1 = new StringBuffer(); String name =""; for (Parameter p : parameters) { if (!p.getType().isPrimitive()) { int dataType = checkDataType(p.getType()); if(dataType<0) { name = getJsonStr(p.getType(),1); sb.append("".equals(name) ? p.getName() : name).append(","); sb1.append(p.getType().getTypeName()).append(" ,"); }else if(dataType==0){ name = getJsonStr(getDataType(p),1); sb.append("[").append("".equals(name) ? p.getName() : name).append("],"); sb1.append(p.getParameterizedType().getTypeName()).append(" ,"); }else if(dataType>0){ name = getJsonStr(getDataType(p),1); sb.append("{").append("".equals(name) ? p.getName() : name).append("},"); sb1.append(p.getParameterizedType().getTypeName()).append(" ,"); } } } if(sb.length()>0) { Map map = new HashMap(); map.put("dataType",sb1.toString().substring(0, sb1.toString().length() - 1)); map.put("dataValue",sb.toString().substring(0, sb.toString().length() - 1)); methodRequestCache.put(methodKey, map); } } /** * @description: 封装请求参数 count 用于解决相互依赖死循环 * @param: c * @param: count * @return: String * @author ysf * @date: 2021/8/13 15:40 */ private static String getJsonStr(Class c,int count){ String req = null; try { if(c!=null&&c.isAnnotationPresent(GenerateEntity.class)&&count<5){ Field[] fields = c.getDeclaredFields(); Map<String, String> fieldMap = new LinkedHashMap(); for (Field field : fields) { if (field.isAnnotationPresent(GenerateFiled.class)) { GenerateFiled amp = field.getAnnotation(GenerateFiled.class); if (amp.isHidden()) { continue; } int dataType = checkDataType(field.getType()); if(dataType<0) { fieldMap.put(field.getName(), getJsonStr(field.getType(),count+1)); }else if(dataType==0){ fieldMap.put(field.getName(), "["+getJsonStr(getDataType(field),count+1)+"]"); }else if(dataType>0){ fieldMap.put(field.getName(), "{"+getJsonStr(getDataType(field),count+1)+"}"); } } } if(!fieldMap.isEmpty()){ req = JsonTools.writeValueAsString(fieldMap, false); } } } catch (Exception e) { } if (req ==null) { return ""; }else{ return req; } } /** * @description: 封装方法参数 * @param: p * @param: aip * @param: method * @return: * @author ysf * @date: 2021/8/12 19:13 */ private static void setParam(Parameter p,GenerateMethodParam aip,Map<String,Map> method,boolean flag){ Map param = new LinkedHashMap(); param.put("key",flag ? p.getName() : aip!=null ? aip.key() : ""); param.put("name", aip !=null ? aip.name() : p.getName()); param.put("isMust", aip !=null ? aip.isMust() +"": ""+false); param.put("type", p.getParameterizedType().getTypeName()); if(!p.getType().isPrimitive()) { Class dataType = getDataType(p); if(dataType ==null){ setMoreParam(p.getType(), param,1); }else{ setMoreParam(dataType, param,1); } }else{ param.put("isMore", "false"); } method.put(aip !=null ? aip.key() : p.getName() , param); } /** * @description: 获取数据类型 * @param: p * @return: Class * @author ysf */ private static Class getDataType(Parameter p){ Class c3 = p.getType(); try { if (Collection.class.isAssignableFrom(c3)) { ParameterizedType pt = (ParameterizedType) p.getParameterizedType(); return (Class<?>) pt.getActualTypeArguments()[0]; } else if (Map.class.isAssignableFrom(c3)) { ParameterizedType pt = (ParameterizedType) p.getParameterizedType(); return (Class<?>) pt.getActualTypeArguments()[1]; } else if (c3.isArray()) { return p.getType().getComponentType(); } }catch (Exception e){ return null; } return null; }
/** * @description: 获取数据类型 * @param: field * @return: Class * @author ysf */ private static Class getDataType(Field field){ Class<?> genericityType = null; try { field.setAccessible(true); Class c3 = field.getType(); if (Collection.class.isAssignableFrom(c3) || Map.class.isAssignableFrom(c3)) { Type genericType = field.getGenericType(); if (null == genericType) { return genericityType; } if (genericType instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) genericType; genericityType = (Class<?>) pt.getActualTypeArguments()[0]; if (Map.class.isAssignableFrom(c3)) { genericityType = (Class<?>) pt.getActualTypeArguments()[1]; } } } else if (c3.isArray()) { genericityType = c3.getComponentType(); } }catch (Exception e){ return null; } return genericityType; } /** * @description: 校验数据类型 * @param: c3 * @return: int * @author ysf * @date: 2021/8/13 15:40 */ private static int checkDataType(Class c3){ if(Collection.class.isAssignableFrom(c3)){ return 0; }else if( Map.class.isAssignableFrom(c3)) { return 1; }else if(c3.isArray()){ return 1; } return -1; } /** * @description: 封装method 参数,count 避免相互依赖死循环,递归5次后结束 * @param: c2 * @param: param * @param: count * @return: * @author ysf * @date: 2021/8/12 19:14 */ private static void setMoreParam(Class c2,Map param,int count){ try { //Class c2 = Class.forName(c.getName()); if (c2.isAnnotationPresent(GenerateEntity.class)&&count<5) { param.put("isMore", "true"); Map<String, Map> fieldMap = entityCache.get(c2.getTypeName()); if(fieldMap==null){ fieldMap = new LinkedHashMap(); Field[] fields = c2.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(GenerateFiled.class)) { GenerateFiled amp = field.getAnnotation(GenerateFiled.class); if(amp.isHidden()){ continue; } fieldMap.put(field.getName(), setMapData(field,field.getType(),count,amp.name())); }else{ Class<?> genericityType =getDataType(field); if(genericityType!=null) { fieldMap.put(field.getName(), setMapData(field, genericityType, count, null)); } } } entityCache.put(c2.getTypeName(), fieldMap); } param.put("more",fieldMap); } else{ param.put("isMore", "false"); } }catch (Exception e){ log.error("反射获取对象异常",e); } } /** * @description: 封装参数类型 * @param: field * @param: c * @param: count * @param: name * @return: Map * @author ysf * @date: 2021/8/13 10:26 */ private static Map setMapData(Field field,Class c,int count,String name){ Map map = new LinkedHashMap(); map.put("key", field.getName()); map.put("name",name!=null ? name: field.getName()); map.put("isMust", "false"); map.put("type", field.getType().getName()); if(!field.getType().isPrimitive()) { setMoreParam(c, map,count+1); } return map; }
} |
封装Service-Api 文档和Html页面
package fills.tools.generate.impl; import java.awt.Desktop; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import fills.tools.generate.util.CreateDocxTableUtil; import fills.tools.generate.util.DubboApiHtmlTemp; import fills.tools.generate.util.FileUtil; import fills.tools.generate.util.JsonTools; @SuppressWarnings({ "rawtypes", "unchecked" }) public class CreateApiTools { private static Logger log = LoggerFactory.getLogger(CreateApiTools.class); public static Map<String,Map<String, List<Object[]>>> apiListReqArrayApi = new LinkedHashMap(); public static Map<String,Map<String, List<Object[]>>> apiListResArrayApi = new LinkedHashMap();
private static String DEFAULT_PATH = "./create/api/html/server-api.html";
static XWPFDocument xdoc = null;
/** * 生成API-HTML页面源码 * @return String */ public static String getApiHtml(){ return DubboApiHtmlTemp.getApiHtml(); }
/** * 生成API-HTML byte[] 字节流源码,用户页面 response.getOutputStream().write(byte[]); * @return byte[] */ public static byte[] getApiByte(){ try { return getApiHtml().getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { } return getApiHtml().getBytes(); }
/** * 生成本地接口并打开默认浏览器查看接口 */ public static void openHtml(){ try { FileUtil.writeContent(getApiHtml(), DEFAULT_PATH); Desktop.getDesktop().open(new File(DEFAULT_PATH)); } catch (IOException e) { } }
/** * 创建本地API-HTML页面 * @param createLocalPath */ public static void createLocalHtml(String createLocalPath){ FileUtil.writeContent(getApiHtml(), createLocalPath); }
/** * 创建本地docx接口文档 * @param createLocalPath */ public static void createLocalDocx(String createLocalPath){ if(xdoc==null){ getApiDocx(); } if(xdoc!=null){ try { FileUtil.createFile(createLocalPath); CreateDocxTableUtil.saveDocument(xdoc, createLocalPath); } catch (Exception e) { } } } /** * 获取api接口文档docx * @return */ public static XWPFDocument getApiDocx(){ if(apiListReqArrayApi.isEmpty()){ getClassParams(); } if(!apiListReqArrayApi.isEmpty()){ String[] fileds = new String[]{"参数名","二级参数名","三级参数名","参数说明","数据类型","是否必须","是否含子参数"}; int[] fileds_widths = new int[] {1504,1504,1504,1504,1504,300,300}; xdoc = new XWPFDocument(); for(Map.Entry<String,Map<String,List<Object[]>>> entry : apiListReqArrayApi.entrySet()){ String classKey = entry.getKey(); Map<String,List<Object[]>> reqMap = entry.getValue(); Map<String,List<Object[]>> resMap = apiListResArrayApi.get(classKey); for(Map.Entry<String, List<Object[]>> obj : reqMap.entrySet()){ String methodKey = obj.getKey(); List<Object[]> reqList = obj.getValue(); List<Object[]> resList = resMap.get(methodKey); Map<String,Map> reqParamMap = GenerateApiCache.methodRequestCache.get(methodKey); Map<String,Map> resParamMap = GenerateApiCache.methodResponseCache.get(methodKey); try { CreateDocxTableUtil.createTitle(xdoc, classKey, "28"); CreateDocxTableUtil.createTitle(xdoc, methodKey, "24"); CreateDocxTableUtil.createTitle(xdoc, "请求类型", "22"); CreateDocxTableUtil.createContent(xdoc, reqParamMap.get("dataType")+"", "20"); CreateDocxTableUtil.createTitle(xdoc, "请求示例", "22"); CreateDocxTableUtil.createContent(xdoc, reqParamMap.get("dataValue")+"", "20"); CreateDocxTableUtil.createTitle(xdoc, "请求参数", "22"); CreateDocxTableUtil.createTable(xdoc, fileds_widths, fileds, reqList, "18"); CreateDocxTableUtil.createTitle(xdoc, "响应类型", "22"); CreateDocxTableUtil.createContent(xdoc, resParamMap.get("dataType")+"", "20"); CreateDocxTableUtil.createTitle(xdoc, "响应示例", "22"); CreateDocxTableUtil.createContent(xdoc, resParamMap.get("dataValue")+"", "20"); CreateDocxTableUtil.createTitle(xdoc, "响应参数", "22"); CreateDocxTableUtil.createTable(xdoc, fileds_widths, fileds, resList, "18"); } catch (Exception e) { } } } return xdoc; } return null; } /** * 获取封装参数 */ private static void getClassParams(){ Map<String, Map<String,Map<String, Map>>> methodReqApiCache = GenerateApiCache.methodReqApiCache; for(Map.Entry<String, Map<String,Map<String, Map>>> entry : methodReqApiCache.entrySet()){ String classKey = entry.getKey(); Map<String,Map<String, Map>> methodMap = entry.getValue(); Map<String, List<Object[]>> methodReqMap = new HashMap<String, List<Object[]>>(); apiListReqArrayApi.put(classKey, methodReqMap); Map<String, List<Object[]>> methodResMap = new HashMap<String, List<Object[]>>(); apiListResArrayApi.put(classKey, methodResMap); getMethodParams(methodMap, methodReqMap,methodResMap); } log.debug(JsonTools.writeValueAsString(apiListReqArrayApi)); log.debug(JsonTools.writeValueAsString(apiListResArrayApi)); } /** * 封装请求,响应参数 * @param methodMap * @param paramList * @param methodResMap */ private static void getMethodParams(Map<String,Map<String, Map>> methodMap, Map<String, List<Object[]>> paramList,Map<String, List<Object[]>> methodResMap){ for(Map.Entry<String, Map<String, Map>> e : methodMap.entrySet()){ String methodKey = e.getKey(); List<Object[]> paramReqArray = new ArrayList<Object[]>(); getParams(e.getValue(), paramReqArray, 0); paramList.put(methodKey, paramReqArray); List<Object[]> paramResArray = new ArrayList<Object[]>(); getParam(GenerateApiCache.methodResApiCache.get(methodKey),paramResArray,0); methodResMap.put(methodKey, paramResArray); } } /** * 封装对象参数 * @param map * @param paramArray * @param paramSize */ private static void getParams(Map<String, Map> map,List<Object[]> paramArray,int paramSize){ for(Map.Entry<String, Map> field : map.entrySet()){ getParam(field.getValue(),paramArray, paramSize); } } /** * 封装行参数 * @param map * @param paramArray * @param paramSize */ private static void getParam(Map map,List<Object[]> paramArray,int paramSize){ if(paramSize>=3){ return; } //参数名称,二级参数名称 ,三级参数名称,参数说明 ,数据类型,是否必须,是否含有子参数 String isMore = map.get("isMore").toString(); paramArray.add(new Object[]{paramSize == 0 ? map.get("key") : "" , paramSize == 1 ? map.get("key") : "" , paramSize == 2 ? map.get("key") : "" , map.get("name") , map.get("type") , map.get("isMust") , isMore}); if("true".equals(isMore)){ getParams((Map<String, Map>)map.get("more"), paramArray, (paramSize+1)); } } /** * 获取docx文档 byte[] * @param document * @return byte[] */ public static byte[] getDocumentByte(XWPFDocument document){ ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] obj= null; try { document.write(out); obj = out.toByteArray(); out.close(); } catch (IOException e) { } return obj; } } |
监听Spring初始化容器封装接口数据
package fills.tools.generate.listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import fills.tools.generate.impl.GenerateApiCache; /** * 监听spring初始完成获取beans,封装接口数据 * @author ysf * */ @Component public class CenerateApiListener implements InitializingBean ,ApplicationContextAware{ private static Logger log = LoggerFactory.getLogger(GenerateApiCache.class); private static ApplicationContext applicationContext; @SuppressWarnings("static-access") @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void afterPropertiesSet() throws Exception { while(applicationContext==null){ log.info("applicationContext is null"); } log.info("applicationContext is not null"); GenerateApiCache.apiMethodCache(applicationContext); } } |
系统接入
Spring 接入一
<bean id="cenerateApiListener"
class="fills.tools.generate.listener.CenerateApiListener" />
Spring 接入二
<context:component-scan base-package="com.xxxx,fills.tools">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
SpringBoot 接入
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class, scanBasePackages = {"com.xxx", "fills.tools"})
输出接口文档
Html输出
@RequestMapping("sys/apihtml") public void serviceApiHtml(HttpServletResponse response) throws IOException { response.getOutputStream().write(CreateApiTools.getApiHtml().getBytes("UTF-8")); } |
Docx输出
@RequestMapping("sys/apidocx") public void serviceApiDocx(HttpServletResponse response) throws IOException { response.setHeader("Content-Disposition","attachment; filename=service-api.docx"); response.getOutputStream().write(CreateApiTools.getDocumentByte()); } |
展示效果
Service-Api html页面
Service-Api docx文档
系统JAR
切面生成service-api接口-jar
系统源码
aspectj切面生成service-api接口源码