测试加载外部jar读取类信息

This commit is contained in:
暮光:城中城
2020-10-31 20:47:52 +08:00
parent ccf4196f0e
commit e0289aa247
12 changed files with 477 additions and 1 deletions

View File

@@ -50,6 +50,11 @@
<artifactId>zyplayer-doc-core</artifactId>
<version>${zyplayer.doc.version}</version>
</dependency>
<dependency>
<groupId>com.zyplayer</groupId>
<artifactId>zyplayer-doc-annotation</artifactId>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>

View File

@@ -24,6 +24,7 @@ public class DubboDocInfo {
private String paramDesc;
private Object paramValue;
private Integer required;
private List<DubboDocParam> params;
public String getParamName() {
return paramName;
@@ -64,6 +65,14 @@ public class DubboDocInfo {
public void setRequired(Integer required) {
this.required = required;
}
public List<DubboDocParam> getParams() {
return params;
}
public void setParams(List<DubboDocParam> params) {
this.params = params;
}
}
public Integer getVersion() {

View File

@@ -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<DubboResponseInfo> 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<DubboResponseInfo> getParams() {
return params;
}
public void setParams(List<DubboResponseInfo> 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;
}
}

View File

@@ -0,0 +1,5 @@
package com.zyplayer.doc.dubbo.framework.bean;
public enum InterfaceType {
map, list, set, arr
}

View File

@@ -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<String> baseType = new HashSet<String>() {{
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);
}
}

View File

@@ -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<String, String> 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<DubboResponseInfo> 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<DubboResponseInfo> 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;
}
}

View File

@@ -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:

View File

@@ -14,6 +14,7 @@
<modules>
<module>zyplayer-doc-test</module>
<module>zyplayer-doc-annotation</module>
</modules>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zyplayer</groupId>
<artifactId>zyplayer-doc-annotation</artifactId>
<version>1.0.6</version>
<packaging>jar</packaging>
<name>zyplayer-doc-annotation</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -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 "";
}

View File

@@ -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 "";
}

View File

@@ -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();
}