Java如何扫描指定package下所有的类

来源:互联网 发布:永诚网络 编辑:程序博客网 时间:2024/05/16 12:08

在写一个MVC框架,需要从包中扫描出组件并注册到容器中,而JDK没有提供现成的从方法,只能自己实现。

功能:
给定一个包名,编程得到该包(和其所有子包)下所有的类文件。如,输入包名com.myapp.util, 输出该包下类的全限定名com.myapp.util.StringUtils, com.app.util.ImageUtils等。

思路:
有的web server在部署运行时会解压jar包,因此class文件会在普通的文件目录下。如果web server不解压jar包,则class文件会直接存在于Jar包中。对于前者,只需定位到class文件所在目录,然后将class文件名读取出即可;对于后者,则需先定位到jar包所在目录,然后使用JarInputStream读取Jar包,得到class类名。

实现:
这是从写好的项目代码中直接copy出来的,如果要运行这段代码,需要把所有的Logger.debug改成System.out.println()

/** * This scanner is used to find out all classes in a package. * Created by whf on 15-2-26. */public class ClasspathPackageScanner implements PackageScanner {    private Logger logger = LoggerFactory.getLogger(ClasspathPackageScanner.class);    private String basePackage;    private ClassLoader cl;    /**     * Construct an instance and specify the base package it should scan.     * @param basePackage The base package to scan.     */    public ClasspathPackageScanner(String basePackage) {        this.basePackage = basePackage;        this.cl = getClass().getClassLoader();    }    /**     * 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     */    @Override    public List<String> getFullyQualifiedClassNameList() throws IOException {        logger.info("开始扫描包{}下的所有类", basePackage);        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<String> doScan(String basePackage, List<String> nameList) throws IOException {        // replace dots with splashes        String splashPath = StringUtil.dotToSplash(basePackage);        // get file path        URL url = cl.getResource(splashPath);        String filePath = StringUtil.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<String> 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.     * e.g., String -> java.lang.String     */    private String toFullyQualifiedName(String shortName, String basePackage) {        StringBuilder sb = new StringBuilder(basePackage);        sb.append('.');        sb.append(StringUtil.trimExtension(shortName));        return sb.toString();    }    private List<String> 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<String> nameList = new ArrayList<>();        while (null != entry) {            String name = entry.getName();            if (name.startsWith(splashedPackageName) && isClassFile(name)) {                nameList.add(name);            }            entry = jarIn.getNextJarEntry();        }        return nameList;    }    private List<String> 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");    }    /**     * For test purpose.     */    public static void main(String[] args) throws Exception {        PackageScanner scan = new ClasspathPackageScanner("cn.fh.lightning.bean");        scan.getFullyQualifiedClassNameList();    }}

上面的代码中用到了StringUtils类,如下:

public class StringUtil {    private StringUtil() {    }    /**     * "file:/home/whf/cn/fh" -> "/home/whf/cn/fh"     * "jar:file:/home/whf/foo.jar!cn/fh" -> "/home/whf/foo.jar"     */    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" -> "cn/fh/lightning"     * @param name     * @return     */    public static String dotToSplash(String name) {        return name.replaceAll("\\.", "/");    }    /**     * "Apple.class" -> "Apple"     */    public static String trimExtension(String name) {        int pos = name.indexOf('.');        if (-1 != pos) {            return name.substring(0, pos);        }        return name;    }    /**     * /application/home -> /home     * @param uri     * @return     */    public static String trimURI(String uri) {        String trimmed = uri.substring(1);        int splashIndex = trimmed.indexOf('/');        return trimmed.substring(splashIndex);    }}

执行结果:
这里写图片描述

0 0
原创粉丝点击