• 004 关于Java如何扫描指定package下所有的类


    q前言:

      在工作中看到这个知识点,就顺便参考了百度的一些资料,整理一下,希望以后用的到。

    一:理论部分

    1.使用场景

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

    2.需求

      给定一个包名,编程得到该包(和其所有子包)下所有的类文件

    3.思路

      有的web server在部署运行时会解压jar包,因此class文件会在普通的文件目录下。

      如果web server不解压jar包,则class文件会直接存在于Jar包中。

      对于前者,只需定位到class文件所在目录,然后将class文件名读取出即可;

      对于后者,则需先定位到jar包所在目录,然后使用JarInputStream读取Jar包,得到class类名。

    二:程序实现

    1.程序目录

      

    2.ClasspathPackageScanner.java

      1 package com.scan;
      2 
      3 import org.slf4j.Logger;
      4 import org.slf4j.LoggerFactory;
      5 
      6 import java.io.File;
      7 import java.io.FileInputStream;
      8 import java.io.IOException;
      9 import java.net.URL;
     10 import java.util.ArrayList;
     11 import java.util.Arrays;
     12 import java.util.List;
     13 import java.util.jar.JarEntry;
     14 import java.util.jar.JarInputStream;
     15 
     16 public class ClasspathPackageScanner implements PackageScanner{
     17     private Logger logger = LoggerFactory.getLogger(ClasspathPackageScanner.class);
     18     private String basePackage;
     19     private ClassLoader cl;
     20 
     21     /**
     22      * 初始化
     23      * @param basePackage
     24      */
     25     public ClasspathPackageScanner(String basePackage) {
     26         this.basePackage = basePackage;
     27         this.cl = getClass().getClassLoader();
     28     }
     29     public ClasspathPackageScanner(String basePackage, ClassLoader cl) {
     30         this.basePackage = basePackage;
     31         this.cl = cl;
     32     }
     33     /**
     34      *获取指定包下的所有字节码文件的全类名
     35      */
     36     public List<String> getFullyQualifiedClassNameList() throws IOException {
     37         logger.info("开始扫描包{}下的所有类", basePackage);
     38         return doScan(basePackage, new ArrayList<String>());
     39     }
     40 
     41     /**
     42      *doScan函数
     43      * @param basePackage
     44      * @param nameList
     45      * @return
     46      * @throws IOException
     47      */
     48     private List<String> doScan(String basePackage, List<String> nameList) throws IOException {
     49         String splashPath = StringUtil.dotToSplash(basePackage);
     50         URL url = cl.getResource(splashPath);   //file:/D:/WorkSpace/java/ScanTest/target/classes/com/scan
     51         String filePath = StringUtil.getRootPath(url);
     52         List<String> names = null; // contains the name of the class file. e.g., Apple.class will be stored as "Apple"
     53         if (isJarFile(filePath)) {// 先判断是否是jar包,如果是jar包,通过JarInputStream产生的JarEntity去递归查询所有类
     54             if (logger.isDebugEnabled()) {
     55                 logger.debug("{} 是一个JAR包", filePath);
     56             }
     57             names = readFromJarFile(filePath, splashPath);
     58         } else {
     59             if (logger.isDebugEnabled()) {
     60                 logger.debug("{} 是一个目录", filePath);
     61             }
     62             names = readFromDirectory(filePath);
     63         }
     64         for (String name : names) {
     65             if (isClassFile(name)) {
     66                 nameList.add(toFullyQualifiedName(name, basePackage));
     67             } else {
     68                 doScan(basePackage + "." + name, nameList);
     69             }
     70         }
     71         if (logger.isDebugEnabled()) {
     72             for (String n : nameList) {
     73                 logger.debug("找到{}", n);
     74             }
     75         }
     76         return nameList;
     77     }
     78 
     79     private String toFullyQualifiedName(String shortName, String basePackage) {
     80         StringBuilder sb = new StringBuilder(basePackage);
     81         sb.append('.');
     82         sb.append(StringUtil.trimExtension(shortName));
     83         //打印出结果
     84         System.out.println(sb.toString());
     85         return sb.toString();
     86     }
     87 
     88     private List<String> readFromJarFile(String jarPath, String splashedPackageName) throws IOException {
     89         if (logger.isDebugEnabled()) {
     90             logger.debug("从JAR包中读取类: {}", jarPath);
     91         }
     92         JarInputStream jarIn = new JarInputStream(new FileInputStream(jarPath));
     93         JarEntry entry = jarIn.getNextJarEntry();
     94         List<String> nameList = new ArrayList<String>();
     95         while (null != entry) {
     96             String name = entry.getName();
     97             if (name.startsWith(splashedPackageName) && isClassFile(name)) {
     98                 nameList.add(name);
     99             }
    100 
    101             entry = jarIn.getNextJarEntry();
    102         }
    103 
    104         return nameList;
    105     }
    106 
    107     private List<String> readFromDirectory(String path) {
    108         File file = new File(path);
    109         String[] names = file.list();
    110 
    111         if (null == names) {
    112             return null;
    113         }
    114 
    115         return Arrays.asList(names);
    116     }
    117 
    118     private boolean isClassFile(String name) {
    119         return name.endsWith(".class");
    120     }
    121 
    122     private boolean isJarFile(String name) {
    123         return name.endsWith(".jar");
    124     }
    125 
    126     /**
    127      * For test purpose.
    128      */
    129     public static void main(String[] args) throws Exception {
    130         PackageScanner scan = new ClasspathPackageScanner("com.scan");
    131         scan.getFullyQualifiedClassNameList();
    132     }
    133 }

    3.PackageScanner接口

    1 package com.scan;
    2 import java.io.IOException;
    3 import java.util.List;
    4 public interface PackageScanner {
    5     public List<String> getFullyQualifiedClassNameList() throws IOException;
    6 }

    4.StringUtil.java

     1 package com.scan;
     2 
     3 import java.net.URL;
     4 
     5 public class StringUtil {
     6     private StringUtil() {
     7 
     8     }
     9     /**
    10      * "file:/home/whf/cn/fh" -> "/home/whf/cn/fh"
    11      * "jar:file:/home/whf/foo.jar!cn/fh" -> "/home/whf/foo.jar"
    12      */
    13     public static String getRootPath(URL url) {
    14         String fileUrl = url.getFile();
    15         int pos = fileUrl.indexOf('!');
    16 
    17         if (-1 == pos) {
    18             return fileUrl;
    19         }
    20 
    21         return fileUrl.substring(5, pos);
    22     }
    23 
    24     /**
    25      * "cn.fh.lightning" -> "cn/fh/lightning"
    26      * @param name
    27      * @return
    28      */
    29     public static String dotToSplash(String name) {
    30         return name.replaceAll("\.", "/");
    31     }
    32 
    33     /**
    34      * "Apple.class" -> "Apple"
    35      */
    36     public static String trimExtension(String name) {
    37         int pos = name.indexOf('.');
    38         if (-1 != pos) {
    39             return name.substring(0, pos);
    40         }
    41 
    42         return name;
    43     }
    44 
    45     /**
    46      * /application/home -> /home
    47      * @param uri
    48      * @return
    49      */
    50     public static String trimURI(String uri) {
    51         String trimmed = uri.substring(1);
    52         int splashIndex = trimmed.indexOf('/');
    53 
    54         return trimmed.substring(splashIndex);
    55     }
    56 }

    5.结果

      

      

  • 相关阅读:
    javascript对话框
    重构之美-走在Web标准化设计的路上[复杂表单]
    xhtml标准下的height:100%
    javascript简洁的LightBox
    Web标准学习书籍推荐
    Email
    jQuery插件Cookie
    Linq to sql 简单性能差异指引 2 (转)
    jQuery Impromptu
    UI
  • 原文地址:https://www.cnblogs.com/juncaoit/p/7591778.html
Copyright © 2020-2023  润新知