diff --git a/zyplayer-doc-dubbo/pom.xml b/zyplayer-doc-dubbo/pom.xml index 01da3e52..6c58128e 100644 --- a/zyplayer-doc-dubbo/pom.xml +++ b/zyplayer-doc-dubbo/pom.xml @@ -50,6 +50,11 @@ zyplayer-doc-core ${zyplayer.doc.version} + + com.zyplayer + zyplayer-doc-annotation + 1.0.6 + org.apache.curator curator-recipes diff --git a/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/bean/DubboDocInfo.java b/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/bean/DubboDocInfo.java index 0d521136..523c8fe5 100644 --- a/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/bean/DubboDocInfo.java +++ b/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/bean/DubboDocInfo.java @@ -24,6 +24,7 @@ public class DubboDocInfo { private String paramDesc; private Object paramValue; private Integer required; + private List params; public String getParamName() { return paramName; @@ -64,6 +65,14 @@ public class DubboDocInfo { public void setRequired(Integer required) { this.required = required; } + + public List getParams() { + return params; + } + + public void setParams(List params) { + this.params = params; + } } public Integer getVersion() { diff --git a/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/bean/DubboResponseInfo.java b/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/bean/DubboResponseInfo.java new file mode 100644 index 00000000..1e7bce9c --- /dev/null +++ b/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/bean/DubboResponseInfo.java @@ -0,0 +1,87 @@ +package com.zyplayer.doc.dubbo.framework.bean; + +import java.util.List; + +/** + * 请求参数对象 + * + * @author 暮光:城中城 + * @since 2019年2月10日 + */ +public class DubboResponseInfo { + private String name; + private String type; + private String desc; + private Object value; + + private String interfaceType; + private DubboResponseInfo keyInfo; + private List params; + + public DubboResponseInfo(String type) { + this.type = type; + } + + public DubboResponseInfo(String name, String type, String desc, Object value) { + this.name = name; + this.type = type; + this.desc = desc; + this.value = value; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public List getParams() { + return params; + } + + public void setParams(List params) { + this.params = params; + } + + public DubboResponseInfo getKeyInfo() { + return keyInfo; + } + + public void setKeyInfo(DubboResponseInfo keyInfo) { + this.keyInfo = keyInfo; + } + + public String getInterfaceType() { + return interfaceType; + } + + public void setInterfaceType(String interfaceType) { + this.interfaceType = interfaceType; + } +} diff --git a/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/bean/InterfaceType.java b/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/bean/InterfaceType.java new file mode 100644 index 00000000..c4cee117 --- /dev/null +++ b/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/bean/InterfaceType.java @@ -0,0 +1,5 @@ +package com.zyplayer.doc.dubbo.framework.bean; + +public enum InterfaceType { + map, list, set, arr +} diff --git a/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/constant/BaseType.java b/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/constant/BaseType.java new file mode 100644 index 00000000..d01a3322 --- /dev/null +++ b/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/constant/BaseType.java @@ -0,0 +1,39 @@ +package com.zyplayer.doc.dubbo.framework.constant; + +import org.apache.commons.lang.StringUtils; + +import java.util.HashSet; +import java.util.Set; + +public class BaseType { + + private static final Set baseType = new HashSet() {{ + add("java.lang.Boolean"); + add("java.lang.Character"); + add("java.lang.Byte"); + add("java.lang.Short"); + add("java.lang.Integer"); + add("java.lang.Long"); + add("java.lang.Float"); + add("java.lang.Double"); + add("java.lang.String"); + add("java.lang.Void"); + add("boolean"); + add("char"); + add("byte"); + add("short"); + add("int"); + add("long"); + add("float"); + add("double"); + add("void"); + }}; + + public static boolean isBaseType(String type) { + if (type == null || StringUtils.startsWith(type, "java.lang")) { + return true; + } + return baseType.contains(type); + } + +} diff --git a/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/service/ClassLoadService.java b/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/service/ClassLoadService.java new file mode 100644 index 00000000..88d74c1f --- /dev/null +++ b/zyplayer-doc-dubbo/src/main/java/com/zyplayer/doc/dubbo/framework/service/ClassLoadService.java @@ -0,0 +1,233 @@ +package com.zyplayer.doc.dubbo.framework.service; + +import com.alibaba.fastjson.JSON; +import com.zyplayer.doc.annotation.DocMethod; +import com.zyplayer.doc.annotation.DocParam; +import com.zyplayer.doc.dubbo.framework.bean.DubboResponseInfo; +import com.zyplayer.doc.dubbo.framework.bean.InterfaceType; +import com.zyplayer.doc.dubbo.framework.constant.BaseType; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.*; +import java.net.JarURLConnection; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public class ClassLoadService { + private static Logger logger = LoggerFactory.getLogger(ClassLoadService.class); + +// public static void main(String[] args) throws Exception { +// String serviceName = "com.zyplayer.dubbo.service.AnnotateService"; +// String jarGroup = "com.zyplayer"; +// String jarArtifact = "dubbo-api"; +// String jarVersion = "1.0"; +// String basePath = "file:D:/maven/repository"; +//// String basePath = "http://nexus.dmall.com:8081/nexus/content/groups/public"; +// new ClassLoadService().loadServerMethod(serviceName, basePath, jarGroup, jarArtifact, jarVersion); +// } + + public static void main(String[] args) throws Exception { + String serviceName = "com.dmall.data.service.dubbo.DataIndicatorsService"; + String jarGroup = "com.dmall.data"; + String jarArtifact = "data-api-client"; + String jarVersion = "1.0.9.SNAPSHOTS"; + String basePath = "http://nexus.dmall.com:8081/nexus/content/groups/public"; + new ClassLoadService().loadServerMethod(serviceName, basePath, jarGroup, jarArtifact, jarVersion); + } + + public void loadServerMethod(String serverName, String basePath, String jarGroup, String jarArtifact, String jarVersion) throws Exception { + // jar:file:D:/maven/repository/com/zyplayer/dubbo-api/1.0/dubbo-api-1.0.jar!/ + String jarPath = jarGroup.replaceAll("\\.", "/") + "/" + jarArtifact + "/" + jarVersion + "/" + jarArtifact + "-" + jarVersion + ".jar"; + URL jarUrl = new URL("jar:" + basePath + "/" + jarPath + "!/"); + JarFile jar = ((JarURLConnection) jarUrl.openConnection()).getJarFile(); + JarEntry jarEntry = jar.getJarEntry(serverName.replaceAll("\\.", "/") + ".class"); + if (jarEntry == null) { + logger.info("未找到类"); + return; + } + URL fileUrl = new URL(basePath + "/" + jarPath); + URLClassLoader classLoader = new URLClassLoader(new URL[]{fileUrl}, Thread.currentThread().getContextClassLoader()); + Class clazz = classLoader.loadClass(serverName); + Method[] methods = clazz.getMethods(); + for (Method method : methods) { + Map docParamMap = new HashMap<>(); + DocParam[] annotation = method.getAnnotationsByType(DocParam.class); + if (annotation != null) { + for (DocParam docParam : annotation) { + docParamMap.put(docParam.name(), docParam.value()); + } + } + String methodDesc = null; + Class returnType = method.getReturnType(); + DubboResponseInfo responseInfo = this.getInfoByClass(classLoader, returnType, 0); + DocMethod docMethod = method.getAnnotation(DocMethod.class); + if (docMethod != null) { + methodDesc = docMethod.value(); + responseInfo.setDesc(docMethod.response()); + } + String methodName = method.getName(); + Type[] parameterTypes = method.getGenericParameterTypes(); + Parameter[] parameters = method.getParameters(); + List paramList = new LinkedList<>(); + for (int i = 0; i < parameterTypes.length; i++) { + DubboResponseInfo responseInfoParam = this.getInfoByType(classLoader, parameterTypes[i], parameters[i].getType(), 0); + String desc = docParamMap.get(parameters[i].getName()); + if (StringUtils.isNotBlank(desc)) { + responseInfoParam.setDesc(desc); + } + responseInfoParam.setName(parameters[i].getName()); + paramList.add(responseInfoParam); + } + logger.info("methodName:{},methodDesc:{},paramList:{},responseInfo:{}", methodName, methodDesc, JSON.toJSONString(paramList), JSON.toJSONString(responseInfo)); + } + } + + private DubboResponseInfo getInfoByType(URLClassLoader classLoader, String resultType, Integer recursion) { + DubboResponseInfo dubboResponseInfo = new DubboResponseInfo(resultType); + if (BaseType.isBaseType(resultType)) { + return dubboResponseInfo; + } + try { + Class clazz = classLoader.loadClass(resultType); + DubboResponseInfo responseInfo = this.getInfoByClass(classLoader, clazz, recursion); + if (responseInfo.getDesc() == null) { + responseInfo.setDesc(dubboResponseInfo.getDesc()); + } + return responseInfo; + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return dubboResponseInfo; + } + + private DubboResponseInfo getInfoByType(URLClassLoader classLoader, Type type, Class typeClazz, Integer recursion) throws Exception { + String typeName = type.getTypeName(); + DubboResponseInfo dubboResponseInfo = new DubboResponseInfo(typeName); + dubboResponseInfo.setName(type.getTypeName()); + if (BaseType.isBaseType(typeName)) { + return dubboResponseInfo; + } + Class clazz = null; + if (typeClazz.isArray()) { + dubboResponseInfo.setInterfaceType(InterfaceType.arr.name()); + clazz = typeClazz.getComponentType(); + } else if (typeClazz.isAssignableFrom(Map.class)) { + dubboResponseInfo.setInterfaceType(InterfaceType.map.name()); + if (type instanceof ParameterizedType) { + Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments(); + Class clazzKey = classLoader.loadClass(actualTypeArguments[0].getTypeName()); + dubboResponseInfo.setKeyInfo(this.getInfoByClass(classLoader, clazzKey, recursion)); + clazz = classLoader.loadClass(actualTypeArguments[1].getTypeName()); + } + } else if (typeClazz.isAssignableFrom(List.class) || typeClazz.isAssignableFrom(Set.class)) { + if (type instanceof ParameterizedType) { + String typeNameSub = ((ParameterizedType) type).getActualTypeArguments()[0].getTypeName(); + clazz = classLoader.loadClass(typeNameSub); + } + if (typeClazz.isAssignableFrom(List.class)) { + dubboResponseInfo.setInterfaceType(InterfaceType.list.name()); + } else if (typeClazz.isAssignableFrom(Set.class)) { + dubboResponseInfo.setInterfaceType(InterfaceType.set.name()); + } + } else { + clazz = classLoader.loadClass(typeName); + } + if (clazz != null) { + DubboResponseInfo responseInfo = this.getInfoByClass(classLoader, clazz, recursion); + responseInfo.setName(typeName); + // 字段注释 优先于 类上的注释 + if (StringUtils.isNotBlank(dubboResponseInfo.getDesc())) { + responseInfo.setDesc(dubboResponseInfo.getDesc()); + } + responseInfo.setKeyInfo(dubboResponseInfo.getKeyInfo()); + responseInfo.setInterfaceType(dubboResponseInfo.getInterfaceType()); + return responseInfo; + } + return dubboResponseInfo; + } + + private DubboResponseInfo getInfoByField(URLClassLoader classLoader, Field field, Integer recursion) throws Exception { + String typeName = field.getType().getTypeName(); + DubboResponseInfo dubboResponseInfo = new DubboResponseInfo(typeName); + dubboResponseInfo.setName(field.getName()); + DocParam docResponse = field.getAnnotation(DocParam.class); + if (docResponse != null) { + dubboResponseInfo.setDesc(docResponse.value()); + } + if (BaseType.isBaseType(typeName)) { + return dubboResponseInfo; + } + Class clazz = null; + if (field.getType().isArray()) { + dubboResponseInfo.setInterfaceType(InterfaceType.arr.name()); + clazz = field.getType().getComponentType(); + } else if (field.getType().isAssignableFrom(Map.class)) { + dubboResponseInfo.setInterfaceType(InterfaceType.map.name()); + Type genericType = field.getGenericType(); + if (genericType instanceof ParameterizedType) { + Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments(); + Class clazzKey = classLoader.loadClass(actualTypeArguments[0].getTypeName()); + dubboResponseInfo.setKeyInfo(this.getInfoByClass(classLoader, clazzKey, recursion)); + clazz = classLoader.loadClass(actualTypeArguments[1].getTypeName()); + } + } else if (field.getType().isAssignableFrom(List.class) || field.getType().isAssignableFrom(Set.class)) { + Type genericType = field.getGenericType(); + if (genericType instanceof ParameterizedType) { + String typeNameSub = ((ParameterizedType) genericType).getActualTypeArguments()[0].getTypeName(); + clazz = classLoader.loadClass(typeNameSub); + } + if (field.getType().isAssignableFrom(List.class)) { + dubboResponseInfo.setInterfaceType(InterfaceType.list.name()); + } else if (field.getType().isAssignableFrom(Set.class)) { + dubboResponseInfo.setInterfaceType(InterfaceType.set.name()); + } + } else { + clazz = classLoader.loadClass(typeName); + } + if (clazz != null) { + DubboResponseInfo responseInfo = this.getInfoByClass(classLoader, clazz, recursion); + responseInfo.setName(field.getName()); + // 字段注释 优先于 类上的注释 + if (StringUtils.isNotBlank(dubboResponseInfo.getDesc())) { + responseInfo.setDesc(dubboResponseInfo.getDesc()); + } + responseInfo.setKeyInfo(dubboResponseInfo.getKeyInfo()); + responseInfo.setInterfaceType(dubboResponseInfo.getInterfaceType()); + return responseInfo; + } + return dubboResponseInfo; + } + + private DubboResponseInfo getInfoByClass(URLClassLoader classLoader, Class clazz, Integer recursion) { + DubboResponseInfo dubboResponseInfo = new DubboResponseInfo(clazz.getTypeName()); + DocParam docResponse = clazz.getAnnotation(DocParam.class); + if (docResponse != null) { + dubboResponseInfo.setDesc(docResponse.value()); + } + if (BaseType.isBaseType(clazz.getTypeName())) { + return dubboResponseInfo; + } + recursion++; + // 默认最大支持3个层级,感觉已经满足了 + if (recursion > 3) { + return dubboResponseInfo; + } + List paramList = new LinkedList<>(); + Field[] fieldArr = clazz.getDeclaredFields(); + for (Field field : fieldArr) { + field.setAccessible(true); + try { + paramList.add(this.getInfoByField(classLoader, field, recursion)); + } catch (Exception e) { + e.printStackTrace(); + } + } + dubboResponseInfo.setParams(paramList); + return dubboResponseInfo; + } +} diff --git a/zyplayer-doc-manage/src/main/resources/application.yml b/zyplayer-doc-manage/src/main/resources/application.yml index 5f9cc568..fc124b58 100644 --- a/zyplayer-doc-manage/src/main/resources/application.yml +++ b/zyplayer-doc-manage/src/main/resources/application.yml @@ -64,7 +64,7 @@ zyplayer: dubbo: # 优先使用zookeeper,未配置时找nacos的配置 zookeeper: -# url: 127.0.0.1:2181 + url: 127.0.0.1:2181 # 服务参数那些信息的服务地址,dubbo7.0新特性 # metadata-url: 127.0.0.1:2181 nacos: diff --git a/zyplayer-doc-other/pom.xml b/zyplayer-doc-other/pom.xml index a8910c9d..6a66f4ef 100644 --- a/zyplayer-doc-other/pom.xml +++ b/zyplayer-doc-other/pom.xml @@ -14,6 +14,7 @@ zyplayer-doc-test + zyplayer-doc-annotation diff --git a/zyplayer-doc-other/zyplayer-doc-annotation/pom.xml b/zyplayer-doc-other/zyplayer-doc-annotation/pom.xml new file mode 100644 index 00000000..6b0cc57f --- /dev/null +++ b/zyplayer-doc-other/zyplayer-doc-annotation/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + com.zyplayer + zyplayer-doc-annotation + 1.0.6 + jar + zyplayer-doc-annotation + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + + diff --git a/zyplayer-doc-other/zyplayer-doc-annotation/src/main/java/com/zyplayer/doc/annotation/DocMethod.java b/zyplayer-doc-other/zyplayer-doc-annotation/src/main/java/com/zyplayer/doc/annotation/DocMethod.java new file mode 100644 index 00000000..3a4bb1f9 --- /dev/null +++ b/zyplayer-doc-other/zyplayer-doc-annotation/src/main/java/com/zyplayer/doc/annotation/DocMethod.java @@ -0,0 +1,19 @@ +package com.zyplayer.doc.annotation; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DocMethod { + + /** + * @return 方法的说明 + */ + String value(); + + /** + * @return 返回结果的说明 + */ + String response() default ""; +} diff --git a/zyplayer-doc-other/zyplayer-doc-annotation/src/main/java/com/zyplayer/doc/annotation/DocParam.java b/zyplayer-doc-other/zyplayer-doc-annotation/src/main/java/com/zyplayer/doc/annotation/DocParam.java new file mode 100644 index 00000000..e7421758 --- /dev/null +++ b/zyplayer-doc-other/zyplayer-doc-annotation/src/main/java/com/zyplayer/doc/annotation/DocParam.java @@ -0,0 +1,20 @@ +package com.zyplayer.doc.annotation; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Repeatable(DocParams.class) +public @interface DocParam { + + /** + * @return 说明 + */ + String value(); + + /** + * @return 字段名 + */ + String name() default ""; +} diff --git a/zyplayer-doc-other/zyplayer-doc-annotation/src/main/java/com/zyplayer/doc/annotation/DocParams.java b/zyplayer-doc-other/zyplayer-doc-annotation/src/main/java/com/zyplayer/doc/annotation/DocParams.java new file mode 100644 index 00000000..3001b33b --- /dev/null +++ b/zyplayer-doc-other/zyplayer-doc-annotation/src/main/java/com/zyplayer/doc/annotation/DocParams.java @@ -0,0 +1,14 @@ +package com.zyplayer.doc.annotation; + +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DocParams { + + /** + * @return 多个参数说明 + */ + DocParam[] value(); +}