提交 e94cfb10 authored 作者: 黄承天's avatar 黄承天

api模块首次提交

上级 1a95535d
package com.tykj.workflowcore.api.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Callable {
}
package com.tykj.workflowcore.api.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CallableApi {
String name() default "";
String description() default "";
}
package com.tykj.workflowcore.api.controller;
import com.tykj.workflowcore.api.entity.ClassInfo;
import com.tykj.workflowcore.api.service.ApiService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController("/api/info")
public class ApiController {
private final ApiService apiService;
public ApiController(ApiService apiService) {
this.apiService = apiService;
}
@GetMapping
public ResponseEntity<List<ClassInfo>> findAll(){
return ResponseEntity.ok(apiService.findAll());
}
}
package com.tykj.workflowcore.api.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiInfo {
private String name;
private List<EntityInfo> params;
private EntityInfo ret;
}
package com.tykj.workflowcore.api.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ClassInfo {
private String name;
private List<ApiInfo> apiInfos;
}
package com.tykj.workflowcore.api.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class EntityInfo {
private String type;
private String name;
private List<EntityInfo> fields;
}
package com.tykj.workflowcore.api.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Parameter {
private Class<?> clz;
private Object obj;
}
package com.tykj.workflowcore.api.service;
import com.tykj.workflowcore.api.annotations.Callable;
import com.tykj.workflowcore.api.annotations.CallableApi;
import com.tykj.workflowcore.api.entity.ApiInfo;
import com.tykj.workflowcore.api.entity.ClassInfo;
import com.tykj.workflowcore.api.entity.EntityInfo;
import com.tykj.workflowcore.api.entity.Parameter;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class ApiService {
private final SpringBeanService springBeanService;
public ApiService(SpringBeanService springBeanService) {
this.springBeanService = springBeanService;
}
/**
* 读取主项目中的所有带有@CallAble注解的类中带有@CallAbleApi注解的方法
* 解析成一个结构化列表数据并返回
*
* @return 结构化列表数据,内容为带有指定注解的方法列表,也就是列出主项目中可调用的方法。
*/
public List<ClassInfo> findAll() {
List<ClassInfo> classInfos = load();
for (ClassInfo classInfo : classInfos) {
System.out.println(classInfo);
}
return classInfos;
}
private List<ClassInfo> load() {
//获取所有类
List<Class<?>> classes = loadClassByLoader(getClass().getClassLoader());
return classes.stream()
.map(this::classInfo)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private ClassInfo classInfo(Class<?> clz) {
try {
//确认Callable注解
if (clz.isAnnotationPresent(Callable.class)) {
List<ApiInfo> apiInfos = Arrays.stream(clz.getMethods())
.map(this::apiInfo)
.filter(Objects::nonNull)
.collect(Collectors.toList());
return new ClassInfo(clz.getName(), apiInfos);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private ApiInfo apiInfo(Method method) {
//确认CallableApi注解
if (method.isAnnotationPresent(CallableApi.class)) {
List<EntityInfo> params = Arrays.stream(method.getParameters())
.map(parameter -> entityInfo(parameter.getType(), parameter.getName()))
.collect(Collectors.toList());
EntityInfo ret = entityInfo(method.getReturnType(), null);
return new ApiInfo(method.getName(), params, ret);
} else {
return null;
}
}
private EntityInfo entityInfo(Class<?> clz, String name) {
String classType = clz.getName();
List<EntityInfo> fields = new ArrayList<>();
if (isNotBasicClass(clz)) {
for (Field field : clz.getDeclaredFields()) {
Class<?> fieldClass = field.getType();
String fieldName = field.getName();
field.setAccessible(true);
//确认类中是否嵌套了相同类型的字段 防止死循环
boolean isDifferentClass = !Objects.equals(clz, fieldClass);
EntityInfo fieldClassInfo;
if (isDifferentClass) {
fieldClassInfo = entityInfo(fieldClass, fieldName);
} else {
fieldClassInfo = new EntityInfo(fieldClass.getSimpleName(), field.getName(), new ArrayList<>());
}
fields.add(fieldClassInfo);
}
}
return new EntityInfo(
classType,
name,
fields
);
}
private static Boolean isNotBasicClass(Class<?> clz) {
String packagePath = clz.getPackage().getName();
return !Objects.equals(packagePath, "java.lang")
&& !Objects.equals(packagePath, "java.util");
}
/**
* 提供参数,调用指定类的指定方法,返回调用后的结果。
* 出异常时返回null
*
* @param className 类名
* @param apiName 方法名
* @param parameters 参数数据
* @return 调用后的返回值
*/
public Object invoke(String className, String apiName, List<Parameter> parameters) {
try {
Class<?> clz = Class.forName(className);
Class<?>[] parameterTypes = parameters.stream()
.map(Parameter::getClz)
.toArray(Class[]::new);
Method method = clz.getMethod(apiName, parameterTypes);
Object bean = springBeanService.getBean(clz);
Object[] params = parameters.stream()
.map(Parameter::getObj)
.toArray();
return method.invoke(bean, params);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
return null;
}
}
//通过loader加载所有类
private List<Class<?>> loadClassByLoader(ClassLoader load) {
List<Class<?>> classes = new ArrayList<>();
try {
Enumeration<URL> urls = load.getResources("");
//放所有类型
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
//文件类型(其实是文件夹)
if (url.getProtocol().equals("file")) {
loadClassByPath(null, url.getPath(), classes, load);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
//通过文件路径加载所有类 root 主要用来替换path中前缀(除包路径以外的路径)
private void loadClassByPath(String root, String path, List<Class<?>> list, ClassLoader load) {
File f = new File(path);
if (root == null) root = f.getPath();
//判断是否是class文件
if (f.isFile() && f.getName().matches("^.*\\.class$")) {
try {
String classPath = f.getPath();
//截取出className 将路径分割符替换为.(windows是\ linux、mac是/)
String className = classPath.substring(root.length() + 1, classPath.length() - 6).replace('/', '.').replace('\\', '.');
list.add(load.loadClass(className));
} catch (Exception ex) {
ex.printStackTrace();
}
} else {
File[] fs = f.listFiles();
if (fs == null) return;
for (File file : fs) {
loadClassByPath(root, file.getPath(), list, load);
}
}
}
}
package com.tykj.workflowcore.api.service;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import static java.util.Objects.isNull;
@Component
public class SpringBeanService implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(isNull(this.applicationContext)) {
this.applicationContext = applicationContext;
}
}
//获取applicationContext
public ApplicationContext getApplicationContext() {
return this.applicationContext;
}
//通过name获取 Bean.
public Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论