Skip to content

Instantly share code, notes, and snippets.

@RadianceL
Last active September 5, 2023 03:07
Show Gist options
  • Save RadianceL/3a877416c8eb61a43ab1af053d70a479 to your computer and use it in GitHub Desktop.
Save RadianceL/3a877416c8eb61a43ab1af053d70a479 to your computer and use it in GitHub Desktop.
PackageScanUtils
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