
com.kedauis.util.ClasspathPackageScanner Maven / Gradle / Ivy
The newest version!
package com.kedauis.util;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
/**
* author QiuYu
* date 2018/1/15
* time 14:26
* description 包扫描器
*/
@ConfigurationProperties(prefix = "spring.quartz")
public abstract class ClasspathPackageScanner {
private Logger logger = LoggerFactory.getLogger(ClasspathPackageScanner.class);
private String basePackage;
private ClassLoader cl;
public ClasspathPackageScanner() {
this.cl = getClassLoader();
}
public abstract ClassLoader getClassLoader();
public String getBasePackage() {
return basePackage;
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
/**
* Construct an instance with base package and class loader.
* @param basePackage The base package to scan.
* @param cl Use this class load to locate the package.
*/
public ClasspathPackageScanner(String basePackage, ClassLoader cl) {
this.basePackage = basePackage;
this.cl = cl;
}
/**
* Get all fully qualified names located in the specified package
* and its sub-package.
*
* @return A list of fully qualified names.
* @throws IOException can not connect file exception
*/
public List getFullyQualifiedClassNameList() throws IOException {
return doScan(basePackage, new ArrayList<>());
}
/**
* Actually perform the scanning procedure.
*
* @param basePackage
* @param nameList A list to contain the result.
* @return A list of fully qualified names.
* @throws IOException
*/
private List doScan(String basePackage, List nameList) throws IOException {
if(StringUtils.isBlank(basePackage)){
logger.info("未定义扫描根路径,放弃定时任务扫描!");
return nameList;
}
logger.info("开始扫描包{}下的所有类", basePackage);
// replace dots with splashes
String splashPath = dotToSplash(basePackage);
// get file path
URL url = cl.getResource(splashPath);
if(null == url){
logger.info("路径{}不存在或者路径下不存在任何对象!", basePackage);
return nameList;
}
String filePath = getRootPath(url);
// Get classes in that package.
// If the web server unzips the jar file, then the classes will exist in the form of
// normal file in the directory.
// If the web server does not unzip the jar file, then classes will exist in jar file.
List names = null; // contains the name of the class file. e.g., Apple.class will be stored as "Apple"
if (isJarFile(filePath)) {
// jar file
if (logger.isDebugEnabled()) {
logger.debug("{} 是一个JAR包", filePath);
}
names = readFromJarFile(filePath, splashPath);
} else {
// directory
if (logger.isDebugEnabled()) {
logger.debug("{} 是一个目录", filePath);
}
names = readFromDirectory(filePath);
}
for (String name : names) {
if (isClassFile(name)) {
//nameList.add(basePackage + "." + StringUtil.trimExtension(name));
nameList.add(toFullyQualifiedName(name, basePackage));
} else {
// this is a directory
// check this directory for more classes
// do recursive invocation
doScan(basePackage + "." + name, nameList);
}
}
if (logger.isDebugEnabled()) {
for (String n : nameList) {
logger.debug("找到{}", n);
}
}
return nameList;
}
/**
* Convert short class name to fully qualified name.
* @param basePackage 基础路径
* @param shortName 短名
* @return 全名
*/
private String toFullyQualifiedName(String shortName, String basePackage) {
StringBuilder sb = new StringBuilder(basePackage);
sb.append('.');
sb.append(trimExtension(shortName));
return sb.toString();
}
/**
* read file from jar file
* @param jarPath jar's path
* @param splashedPackageName base package in jar
* @return file's names
* @throws IOException jar open exception
*/
private List readFromJarFile(String jarPath, String splashedPackageName) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("从JAR包中读取类: {}", jarPath);
}
JarInputStream jarIn = new JarInputStream(new FileInputStream(jarPath));
JarEntry entry = jarIn.getNextJarEntry();
List nameList = new ArrayList<>();
while (null != entry) {
String name = entry.getName();
if (name.startsWith(splashedPackageName) && isClassFile(name)) {
// find class in jar file, substring it from the index of last '/'
// set the realClassName in list
nameList.add(name);
}
entry = jarIn.getNextJarEntry();
}
return nameList;
}
/**
* read from directory
* @param path file path
* @return file's name
*/
private List readFromDirectory(String path) {
File file = new File(path);
String[] names = file.list();
if (null == names) {
return null;
}
return Arrays.asList(names);
}
private boolean isClassFile(String name) {
return name.endsWith(".class");
}
private boolean isJarFile(String name) {
return name.endsWith(".jar");
}
/**
* "file:/home/whf/cn/fh" to"/home/whf/cn/fh"
* "jar:file:/home/whf/foo.jar!cn/fh" to "/home/whf/foo.jar"
* @param url full file path url
* @return root path
*/
public static String getRootPath(URL url) {
String fileUrl = url.getFile();
int pos = fileUrl.indexOf('!');
if (-1 == pos) {
return fileUrl;
}
return fileUrl.substring(5, pos);
}
/**
* "cn.fh.lightning" to "cn/fh/lightning"
* @param name package
* @return file path
*/
public static String dotToSplash(String name) {
return name.replaceAll("\\.", "/");
}
/**
* "Apple.class" to "Apple"
* "/com/package/Apple.class" to "Apple"
* @param name className
* @return class's real name
*/
public static String trimExtension(String name) {
int pos = name.indexOf('.');
if (-1 != pos) {
String className = name.substring(0, pos);
int poss = className.lastIndexOf("/");
if(-1 != poss){
return className.substring(poss+1);
}
return className;
}
return name;
}
/**
* /application/home to /home
* @param uri uri object
* @return root name
*/
public static String trimURI(String uri) {
String trimmed = uri.substring(1);
int splashIndex = trimmed.indexOf('/');
return trimmed.substring(splashIndex);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy