site.sorghum.anno.method.MethodTemplateManager Maven / Gradle / Ivy
The newest version!
package site.sorghum.anno.method;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.FileResource;
import cn.hutool.core.io.resource.MultiResource;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.text.csv.CsvReader;
import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import site.sorghum.anno._common.AnnoBeanUtils;
import site.sorghum.anno.anno.proxy.AnnoBaseProxy;
import site.sorghum.anno.method.processor.MTBasicProcessor;
import site.sorghum.anno.method.processor.MethodBasicProcessor;
import site.sorghum.anno.method.resource.JarResource;
import site.sorghum.anno.method.resource.ResourceFinder;
import site.sorghum.anno.method.route.MethodRoute;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 方法模版工厂类
*
* @author songyinyin
* @since 2024/1/15 15:21
*/
@Slf4j
public class MethodTemplateManager {
public static final String METHOD_PATH = "method";
public static final double MT_DEFAULT_INDEX = 1000;
private static final Map> methodTemplateMap = new HashMap<>();
private static final Map> processorInfoMap = new HashMap<>();
private static final Map, MethodRoute> methodRouteBeanMap = new HashMap<>();
private static final Map, Object> proxyCache = new ConcurrentHashMap<>();
private static final Set processedOldProxy = new HashSet<>();
public static T create(Class clazz) {
Object proxy = proxyCache.computeIfAbsent(clazz, k -> {
Object object = Proxy.newProxyInstance(k.getClassLoader(), new Class[]{k}, new MethodTemplateInvocationHandler());
if (log.isDebugEnabled()) {
log.debug("This MethodTemplate created successfully: {}", k.getName());
}
return object;
});
return (T) proxy;
}
public static void parse(String packageName) throws IOException {
if (methodRouteBeanMap.isEmpty()) {
Set> methodRouteClass = ClassUtil.scanPackageBySuper(MethodRoute.class.getPackageName(), MethodRoute.class);
for (Class> routeClass : methodRouteClass) {
MethodRoute methodRoute = (MethodRoute) ReflectUtil.newInstance(routeClass);
methodRouteBeanMap.put(routeClass, methodRoute);
}
}
Set> classes = ClassUtil.scanPackageByAnnotation(packageName, MethodTemplate.class);
// 解析决策表
parse(classes, METHOD_PATH + "/*.csv", METHOD_PATH + "/**/*.csv");
// 兼容 AnnoBaseProxy#supportEntities
supportOld(packageName);
// 兼容无决策表的情况
supportNoRuleCsv(classes);
infoToMtMap();
}
/**
* 没有定义决策表,或者决策表中的部件不完整时,把所有方法模版接口的实现都追加进来
*/
private static void supportNoRuleCsv(Set> classes) {
for (Class> clazz : classes) {
MethodTemplate methodTemplate = AnnotationUtil.getAnnotation(clazz, MethodTemplate.class);
if (methodTemplate == null) {
continue;
}
Set mtMethods = Arrays.stream(findMethods(clazz)).map(Method::getName).collect(Collectors.toSet());
Collection> beans = AnnoBeanUtils.getBeansOfType(clazz);
for (Object bean : beans) {
Method[] methods = findMethods(bean.getClass());
for (Method method : methods) {
if (!mtMethods.contains(method.getName())) {
continue;
}
String subPath;
if (StrUtil.isNotBlank(methodTemplate.ruleDir())) {
subPath = methodTemplate.ruleDir();
} else {
continue;
}
String fullPath = "%s/%s/%s.csv".formatted(METHOD_PATH, subPath, method.getName());
Set processorInfos = processorInfoMap.computeIfAbsent(fullPath, k -> new LinkedHashSet<>());
Optional max = processorInfos.stream().max(Comparator.comparing(MTProcessorInfo::getIndex));
double index = max.map(mtProcessorInfo -> mtProcessorInfo.getIndex() + 1).orElse(10000D);
MethodUnit methodUnit = AnnotationUtil.getAnnotation(method, MethodUnit.class);
MTProcessorInfo info = new MTProcessorInfo();
info.setBeanName(StrUtil.lowerFirst(bean.getClass().getSimpleName()));
info.setBean(bean);
info.setMethodName(method.getName());
info.setExclude(false);
info.setFullPath(fullPath);
if (methodUnit != null) {
info.setPhase(methodUnit.phase());
info.setIndex(methodUnit.index());
} else {
info.setPhase(ExecutePhase.EXECUTE);
info.setIndex(index);
}
processorInfos.add(info);
}
}
}
}
/**
* 兼容 AnnoBaseProxy#supportEntities
*/
private static void supportOld(String packageName) {
List proxies = AnnoBeanUtils.getBeansOfType(AnnoBaseProxy.class);
for (AnnoBaseProxy> proxy : proxies) {
if (!proxy.getClass().getName().startsWith(packageName)) {
continue;
}
// 去重
if (processedOldProxy.contains(proxy.hashCode())){
continue;
}
processedOldProxy.add(proxy.hashCode());
String[] supportEntities = proxy.supportEntities();
if (ArrayUtil.isEmpty(supportEntities)) {
continue;
}
Method[] methods = findMethods(AnnoBaseProxy.class);
for (Method method : methods) {
String methodName = method.getName();
if (methodName.equals("supportEntities")) {
continue;
}
MethodUnit methodUnit = AnnotationUtil.getAnnotation(method, MethodUnit.class);
for (String supportEntity : supportEntities) {
MTProcessorInfo info = new MTProcessorInfo();
info.setBeanName(AnnoBeanUtils.getBeanName(proxy.getClass()));
info.setBean(proxy);
info.setMethodName(methodName);
info.setExclude(false);
String[] split = supportEntity.split("\\.");
String entityName = split[split.length - 1];
String key = "method/" + entityName + "/" + methodName + ".csv";
info.setFullPath(key);
if (methodUnit != null) {
info.setPhase(methodUnit.phase());
info.setIndex(methodUnit.index());
} else {
info.setPhase(ExecutePhase.EXECUTE);
info.setIndex(proxy.index());
}
Set mtProcessorInfos = processorInfoMap.computeIfAbsent(key, k -> new LinkedHashSet<>());
mtProcessorInfos.add(info);
}
}
}
}
private static Method[] findMethods(Class> clazz) {
Method[] methods = clazz.getDeclaredMethods();
// 过滤掉桥接方法 静态方法
List resultMethods = Arrays.stream(methods).filter(e -> !e.isBridge() && !Modifier.isStatic(e.getModifiers())).toList();
return resultMethods.toArray(new Method[0]);
}
public static void parse(Set> classes, String... locationPatterns) throws IOException {
MultiResource multiResource = new MultiResource();
for (String locationPattern : locationPatterns) {
MultiResource resources = ResourceFinder.of().find(locationPattern);
for (Resource resource : resources) {
multiResource.add(resource);
}
}
Map> infoMap = new HashMap<>();
for (Class> methodTemplateClass : classes) {
Method[] publicMethods = findMethods(methodTemplateClass);
for (Method publicMethod : publicMethods) {
Map> map = parseMethodTemplates(publicMethod, multiResource);
if (CollUtil.isNotEmpty(map)) {
infoMap.putAll(map);
}
}
}
if (CollUtil.isEmpty(infoMap)) {
return;
}
for (String key : infoMap.keySet()) {
Set infos = processorInfoMap.computeIfAbsent(key, k -> new LinkedHashSet<>());
infos.addAll(infoMap.get(key));
if (CollUtil.isEmpty(infos)) {
continue;
}
List excludeBeanNames = infos.stream()
.filter(MTProcessorInfo::isExclude)
.map(MTProcessorInfo::getBeanMethodName)
.toList();
Set list = infos.stream()
.filter(e -> StrUtil.isNotBlank(e.getBeanName()) && !excludeBeanNames.contains(e.getBeanMethodName()))
.sorted(Comparator.comparing(MTProcessorInfo::getIndex))
.collect(Collectors.toCollection(LinkedHashSet::new));
if (CollUtil.isNotEmpty(list)) {
processorInfoMap.put(key, list);
}
}
}
public static void infoToMtMap() {
for (Map.Entry> entry : processorInfoMap.entrySet()) {
String key = entry.getKey();
Set value = entry.getValue();
List process = value.stream()
.map(MethodTemplateManager::createProcessor)
.collect(Collectors.toList());
sortProcessors(process);
methodTemplateMap.put(key, process);
}
}
public static void clear() {
methodTemplateMap.clear();
processorInfoMap.clear();
methodRouteBeanMap.clear();
proxyCache.clear();
}
public static boolean isSupportMethod(Method method) {
return !method.getDeclaringClass().equals(Object.class);
}
private static Map> parseMethodTemplates(Method publicMethod, MultiResource resources) throws IOException {
Pair pair = getMethodFileName(publicMethod);
String methodName = publicMethod.getName();
Map> infoMap = new HashMap<>();
for (Resource resource : resources) {
String file = resource.getName();
if (!StrUtil.equals(file, pair.getValue())) {
continue;
}
FileResource fileResource = null;
if (resource instanceof FileResource variableFileResource) {
fileResource = variableFileResource;
}
if (resource instanceof JarResource variableJarResource) {
// 伪装为文件资源
fileResource = new FileResource(variableJarResource.getFile());
}
if (fileResource == null) {
continue;
}
List parentPathList = new ArrayList<>();
getParentPath(parentPathList, fileResource.getFile());
// method/test_sayHello.csv
String fullPath = parentPathList.stream().sorted(Comparator.reverseOrder()).collect(Collectors.joining("/")) + "/" + pair.getValue();
fullPath = FileUtil.normalize(fullPath);
try (BufferedReader bufferedReader = resource.getReader(StandardCharsets.UTF_8)) {
CsvReader reader = CsvUtil.getReader();
List list = reader.read(bufferedReader, MethodTemplateCsv.class);
if (CollUtil.isEmpty(list)) {
continue;
}
List processorInfos = infoMap.computeIfAbsent(fullPath, k -> new ArrayList<>());
for (MethodTemplateCsv templateCsv : list) {
MTProcessorInfo info = new MTProcessorInfo();
info.setPhase(templateCsv.getPhase());
info.setIndex(templateCsv.getIndex());
// 小写首字母
info.setBeanName(StrUtil.lowerFirst(templateCsv.getBeanName()));
info.setMethodName(templateCsv.getMethodName());
info.setExclude(templateCsv.isExclude());
info.setCondition(templateCsv.getCondition());
info.setFullPath(fullPath);
info.setMtMethodName(methodName);
info.deal();
processorInfos.add(info);
}
}
}
return infoMap;
}
private static MethodBasicProcessor createProcessor(MTProcessorInfo info) {
Object bean = info.getBean();
if (bean == null) {
bean = AnnoBeanUtils.getBean(info.getBeanName());
}
if (bean == null) {
bean = AnnoBeanUtils.getBean(ClassUtil.loadClass(StrUtil.upperFirst(info.getBeanName())));
}
if (bean == null) {
throw new MTException("Bean: " + info.getBeanName() + " not found");
}
if (StrUtil.isBlank(info.getMethodName())) {
if (bean instanceof MTBasicProcessor) {
return new MethodBasicProcessor(bean, "process", info);
} else {
return new MethodBasicProcessor(bean, info.getMtMethodName(), info);
}
} else {
return new MethodBasicProcessor(bean, info.getMethodName(), info);
}
}
public static void getParentPath(List parentPathList, File file) {
String parentFileName = file.getParentFile().getName();
if (StrUtil.equals(parentFileName, METHOD_PATH)) {
parentPathList.add(parentFileName);
} else {
parentPathList.add(parentFileName);
getParentPath(parentPathList, file.getParentFile());
}
}
/**
* 获取方法文件名
*
* @return key: 文件夹, value: 文件名
*/
public static Pair getMethodFileName(Method method) {
MethodTemplate annotation = getMethodTemplateAnnotation(method);
return Pair.of(annotation.ruleDir(), method.getName() + ".csv");
}
private static MethodTemplate getMethodTemplateAnnotation(Method method) {
MethodTemplate annotation = AnnotationUtil.getAnnotation(method.getDeclaringClass(), MethodTemplate.class);
if (annotation == null) {
annotation = AnnotationUtil.getAnnotation(method, MethodTemplate.class);
}
if (annotation == null) {
throw new MTException("MethodTemplate annotation not found in class: %s or method: %s".formatted(method.getDeclaringClass().getName(), method.getName()));
}
return annotation;
}
public static List getMethodProcessors(MTContext context) {
MethodTemplate methodTemplate = getMethodTemplateAnnotation(context.getMethod());
Class extends MethodRoute> routeClass = methodTemplate.route();
MethodRoute methodRoute = methodRouteBeanMap.get(routeClass);
String[] route = methodRoute.route(context);
List routeList = route == null ? null : Arrays.stream(route).filter(StrUtil::isNotBlank).toList();
Pair pair = getMethodFileName(context.getMethod());
String methodFileName = METHOD_PATH + "/" + pair.getKey() + "/" + pair.getValue();
if (CollUtil.isEmpty(routeList)) {
return getMethodProcessors(methodFileName);
}
List processors = new ArrayList<>();
for (String subPath : routeList) {
List subProcessors = getMethodProcessors("%s/%s/%s".formatted(METHOD_PATH, subPath, pair.getValue()));
if (CollUtil.isNotEmpty(subProcessors)) {
processors.addAll(subProcessors);
}
}
sortProcessors(processors);
return processors;
}
public static List getMethodProcessors(String methodFileName) {
return methodTemplateMap.get(FileUtil.normalize(methodFileName));
}
/**
* 方法部件排序
*/
private static void sortProcessors(List list) {
list.sort(Comparator.comparing(MethodBasicProcessor::getPhaseOrdinal)
.thenComparing(MethodBasicProcessor::getIndex));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy