Last active
September 5, 2023 03:07
-
-
Save RadianceL/3a877416c8eb61a43ab1af053d70a479 to your computer and use it in GitHub Desktop.
PackageScanUtils
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.olympus.base.utils.support.utils; | |
import lombok.extern.slf4j.Slf4j; | |
import org.apache.commons.lang3.StringUtils; | |
import org.springframework.core.io.Resource; | |
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; | |
import org.springframework.core.io.support.ResourcePatternResolver; | |
import org.springframework.core.type.classreading.CachingMetadataReaderFactory; | |
import org.springframework.core.type.classreading.MetadataReader; | |
import org.springframework.core.type.classreading.MetadataReaderFactory; | |
import org.springframework.util.SystemPropertyUtils; | |
import java.lang.annotation.Annotation; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Modifier; | |
import java.util.*; | |
/** | |
* 包扫描 <br> | |
* since 2020/8/25 | |
* | |
* @author eddie.lys | |
*/ | |
@Slf4j | |
public class PackageScanUtils { | |
/** | |
* 扫描 scanPackages 下的文件的匹配符 | |
*/ | |
protected static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; | |
/** | |
* 结合spring的类扫描方式 根据需要扫描的包路径及相应的注解,获取最终测method集合 仅返回public方法,如果方法是非public类型的,不会被返回 | |
* 可以扫描工程下的class文件及jar中的class文件 | |
*/ | |
public static Set<Method> findClassAnnotationMethods(String scanPackages, Class<? extends Annotation> annotation) { | |
// 获取所有的类 | |
Set<String> clazzSet = findPackageClass(scanPackages, null); | |
Set<Method> methods = new HashSet<>(); | |
// 遍历类,查询相应的annotation方法 | |
for (String clazz : clazzSet) { | |
try { | |
Set<Method> ms = findAnnotationMethods(clazz, annotation); | |
methods.addAll(ms); | |
} catch (ClassNotFoundException ignore) { | |
} | |
} | |
return methods; | |
} | |
/** | |
* 根据扫描包的,查询下面的所有类 | |
* | |
* @param scanPackages 扫描的package路径 | |
*/ | |
public static Set<String> findPackageClass(String scanPackages, Class<? extends Annotation> anno) { | |
Set<String> clazzSet = new HashSet<>(); | |
if (StringUtils.isBlank(scanPackages)) { | |
return clazzSet; | |
} | |
// 验证及排重包路径,避免父子路径多次扫描 | |
Set<String> packages = checkPackage(scanPackages); | |
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); | |
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver); | |
for (String basePackage : packages) { | |
if (StringUtils.isBlank(basePackage)) { | |
continue; | |
} | |
String packageSearchPath = | |
ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX | |
+ org.springframework.util.ClassUtils.convertClassNameToResourcePath( | |
SystemPropertyUtils.resolvePlaceholders(basePackage)) | |
+ "/" | |
+ DEFAULT_RESOURCE_PATTERN; | |
try { | |
Resource[] resources = resourcePatternResolver.getResources(packageSearchPath); | |
for (Resource resource : resources) { | |
// 检查resource,这里的resource都是class | |
String clazz = loadClassName(metadataReaderFactory, resource); | |
if (Objects.nonNull(anno)) { | |
if (Objects.isNull(Class.forName(clazz).getAnnotation(anno))) { | |
continue; | |
} | |
} | |
clazzSet.add(clazz); | |
} | |
} catch (Exception e) { | |
log.error("获取包下面的类信息失败,package:" + basePackage, e); | |
} | |
} | |
return clazzSet; | |
} | |
/** | |
* 排重、检测package父子关系,避免多次扫描 | |
* | |
* @param scanPackages 目标包 | |
* @return 返回检查后有效的路径集合 | |
*/ | |
private static Set<String> checkPackage(String scanPackages) { | |
Set<String> packages = new HashSet<>(); | |
if (StringUtils.isBlank(scanPackages)) { | |
return packages; | |
} | |
// 排重路径 | |
Collections.addAll(packages, scanPackages.split(",")); | |
for (String pInArr : packages.toArray(new String[0])) { | |
if (StringUtils.isBlank(pInArr) || ".".equals(pInArr) || pInArr.startsWith(".")) { | |
continue; | |
} | |
if (pInArr.endsWith(".")) { | |
pInArr = pInArr.substring(0, pInArr.length() - 1); | |
} | |
Iterator<String> packageIte = packages.iterator(); | |
boolean needAdd = true; | |
while (packageIte.hasNext()) { | |
String pack = packageIte.next(); | |
if (pInArr.startsWith(pack + ".")) { | |
// 如果待加入的路径是已经加入的pack的子集,不加入 | |
needAdd = false; | |
} else if (pack.startsWith(pInArr + ".")) { | |
// 如果待加入的路径是已经加入的pack的父集,删除已加入的pack | |
packageIte.remove(); | |
} | |
} | |
if (needAdd) { | |
packages.add(pInArr); | |
} | |
} | |
return packages; | |
} | |
/** | |
* 加载资源,根据resource获取className | |
* | |
* @param metadataReaderFactory spring中用来读取resource为class的工具 | |
* @param resource 这里的资源就是一个Class | |
*/ | |
private static String loadClassName( | |
MetadataReaderFactory metadataReaderFactory, Resource resource) { | |
try { | |
if (resource.isReadable()) { | |
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource); | |
return metadataReader.getClassMetadata().getClassName(); | |
} | |
} catch (Exception e) { | |
log.error("根据resource获取类名称失败", e); | |
} | |
return null; | |
} | |
/** | |
* 把action下面的所有method遍历一次,标记他们是否需要进行敏感词验证 如果需要,放入cache中 | |
*/ | |
public static Set<Method> findAnnotationMethods(String fullClassName, Class<? extends Annotation> anno) throws ClassNotFoundException { | |
Set<Method> methodSet = new HashSet<>(); | |
Class<?> clz = Class.forName(fullClassName); | |
Method[] methods = clz.getDeclaredMethods(); | |
for (Method method : methods) { | |
if (method.getModifiers() != Modifier.PUBLIC) { | |
continue; | |
} | |
Annotation annotation = method.getAnnotation(anno); | |
if (annotation != null) { | |
methodSet.add(method); | |
} | |
} | |
return methodSet; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment