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

 package com.scan;

 import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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; public class ClasspathPackageScanner implements PackageScanner{
private Logger logger = LoggerFactory.getLogger(ClasspathPackageScanner.class);
private String basePackage;
private ClassLoader cl; /**
* 初始化
* @param basePackage
*/
public ClasspathPackageScanner(String basePackage) {
this.basePackage = basePackage;
this.cl = getClass().getClassLoader();
}
public ClasspathPackageScanner(String basePackage, ClassLoader cl) {
this.basePackage = basePackage;
this.cl = cl;
}
/**
*获取指定包下的所有字节码文件的全类名
*/
public List<String> getFullyQualifiedClassNameList() throws IOException {
logger.info("开始扫描包{}下的所有类", basePackage);
return doScan(basePackage, new ArrayList<String>());
} /**
*doScan函数
* @param basePackage
* @param nameList
* @return
* @throws IOException
*/
private List<String> doScan(String basePackage, List<String> nameList) throws IOException {
String splashPath = StringUtil.dotToSplash(basePackage);
URL url = cl.getResource(splashPath); //file:/D:/WorkSpace/java/ScanTest/target/classes/com/scan
String filePath = StringUtil.getRootPath(url);
List<String> names = null; // contains the name of the class file. e.g., Apple.class will be stored as "Apple"
if (isJarFile(filePath)) {// 先判断是否是jar包,如果是jar包,通过JarInputStream产生的JarEntity去递归查询所有类
if (logger.isDebugEnabled()) {
logger.debug("{} 是一个JAR包", filePath);
}
names = readFromJarFile(filePath, splashPath);
} else {
if (logger.isDebugEnabled()) {
logger.debug("{} 是一个目录", filePath);
}
names = readFromDirectory(filePath);
}
for (String name : names) {
if (isClassFile(name)) {
nameList.add(toFullyQualifiedName(name, basePackage));
} else {
doScan(basePackage + "." + name, nameList);
}
}
if (logger.isDebugEnabled()) {
for (String n : nameList) {
logger.debug("找到{}", n);
}
}
return nameList;
} private String toFullyQualifiedName(String shortName, String basePackage) {
StringBuilder sb = new StringBuilder(basePackage);
sb.append('.');
sb.append(StringUtil.trimExtension(shortName));
//打印出结果
System.out.println(sb.toString());
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<String>();
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("com.scan");
scan.getFullyQualifiedClassNameList();
}
}

3.PackageScanner接口

 package com.scan;
import java.io.IOException;
import java.util.List;
public interface PackageScanner {
public List<String> getFullyQualifiedClassNameList() throws IOException;
}

4.StringUtil.java

 package com.scan;

 import java.net.URL;

 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);
}
}

5.结果

  

  

004 关于Java如何扫描指定package下所有的类的更多相关文章

  1. java读取指定package下的所有class

     JAVA如何扫描一个包下面的所有类,并加载到内存中去? spring中有一个<context:component-scan base-package="com.controller& ...

  2. Java 获取指定包下的所有类

    package com.s.rest.util; import java.io.File; import java.io.FileFilter; import java.io.IOException; ...

  3. Java递归输出指定路径下所有文件及文件夹

    package a.ab; import java.io.File; import java.io.IOException; public class AE { public static void ...

  4. Java反射1——扫描某个包下的所有类

    1.从包package中获取所有的Class /** * 从包package中获取所有的Class * * @param pack * @return */ public static Set< ...

  5. 我的Android进阶之旅------>Android使用正则表达式匹配扫描指定目录下的所有媒体文件(音乐、图像、视频文件)

    今天使用正则表达式匹配指定目录下的所有媒体文件,下面将这份代码简化了,可以收藏下来,当作工具类. package match; import java.io.File; import java.uti ...

  6. JAVA 扫描指定路径下所有的jar包,并保存所有实现固定接口的类型

    private static Map<String, Object> loadAllJarFromAbsolute(String directoryPath) throws NoSuchM ...

  7. java批量修改指定目录下的文件名

    package io; import java.io.File; import java.io.IOException; /** * @Auther: Code * @Date: 2018/9/9 1 ...

  8. java递归删除指定目录下的文件和文件夹

    public static boolean deleteFolder(String delDir) { File delFolder = new File(delDir); File[] delFil ...

  9. java下载文件指定目录下的文件

    方法一: @RequestMapping('download')def download(HttpServletRequest request, HttpServletResponse respons ...

随机推荐

  1. 二分算法的应用——最大化平均值 POJ 2976 Dropping tests

    最大化平均值 有n个物品的重量和价值分别wi 和 vi.从中选出 k 个物品使得 单位重量 的价值最大. 限制条件: <= k <= n <= ^ <= w_i <= v ...

  2. 二分算法的应用——最大化最小值 POJ2456 Aggressive cows

    Aggressive cows Time Limit: 1000MS Memory Limit: 65536K Total Submissions: Accepted: Description Far ...

  3. IntelliJ IDEA连接cvs超时Error refreshing view: Timeout while trying to connect to host

    在使用IntelliJ IDEA连接cvs的时候,有时会报超时错误: Error refreshing view: Timeout while trying to connect to host: 1 ...

  4. python核心编程笔记——Chapter6

    Chapter 6.序列:字符串,列表和元组 这章内容比较多啊,看得比较久,而且题目又难很多. 6.1鉴定一个字符串是否是另外一个字符串的字串,这道题目不是很确定,好像没有直接的判定吧. 直接是否内建 ...

  5. Exp2:后门原理与实践

    Exp2:后门原理与实践 1 实践目标 任务一:使用netcat获取主机操作Shell,cron启动 (0.5分) 任务二:使用socat获取主机操作Shell, 任务计划启动 (0.5分) 任务三: ...

  6. ECMAScript 6中的var,let,const

    var的变量提升 console.log(a); //输出undefined ; 他的实际执行顺序是: var a: console.log(a); a= 这就是var的变量提升 const命令的用法 ...

  7. [转]QList内存释放

    QList<T> 的释放分两种情况: 1.T的类型为非指针,这时候直接调用clear()方法就可以释放了,看如下测试代码 #include <QtCore/QCoreApplicat ...

  8. cin.get()和cin.getline()之间的区别

    cin.getline()和cin.get()都是对输入的面向行的读取,即一次读取整行而不是单个数字或字符,但是二者有一定的区别. cin.get()每次读取一整行并把由Enter键生成的换行符留在输 ...

  9. aarch64_a1

    AGReader-1.2-16.fc26.aarch64.rpm 2017-02-14 07:01 50K fedora Mirroring Project ATpy-0.9.7-11.fc26.no ...

  10. C# 读取指定文件夹下所有文件

    #region 读取文件 //返回指定目录中的文件的名称(绝对路径) string[] files = System.IO.Directory.GetFiles(@"D:\Test" ...