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. Shell记录-Shell命令(磁盘)

    inux中df命令的功能是用来检查linux服务器的文件系统的磁盘空间占用情况.可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息. 1.命令格式 df [选项] [文件] Shell ...

  2. 批量更新demo

    因为批量更新数据库的时候,如果数据量太多,就会报错,这时候可以通过逻辑,批量更新,demo如下 @Test public void testbatch() { /** * 批量的值 */ int ma ...

  3. pandas 视频讲座 from youtube

    Stephen Simmons - Pandas from the inside - YouTube https://www.youtube.com/watch?v=Dr3Hv7aUkmU 2016年 ...

  4. 【整理】HTML5游戏开发学习笔记(1)- 骰子游戏

    <HTML5游戏开发>,该书出版于2011年,似乎有些老,可对于我这样没有开发过游戏的人来说,却比较有吸引力,选择自己感兴趣的方向来学习html5,css3,相信会事半功倍.不过值得注意的 ...

  5. 20155213 2016-2017-2 《Java程序设计》第七周学习总结

    20155213 2016-2017-2 <Java程序设计>第七周学习总结 教材学习内容总结 Lambda 如果使用JDK8的话,可以使用Lambda特性去除重复的信息. 在只有Lamb ...

  6. python 操作 memcache

    目录 Memcached Memcached安装 python操作Memcached Memcache模块常用方法 Memcached Memcached是一个高性能的分布式内存对象缓存系统,用于动态 ...

  7. 在mac环境下用QT使用OpenGL,glut,glfw

    只需要在新建工程中.pro文件中添加: #opengl glut LIBS+= -framework opengl -framework glut 就可以使用glut了. 继续添加: ##glfw L ...

  8. ClientDataset 三层 var and out arguments must match parameter

    ​​​将Delphi升级到10.1.2后,从客户端传ClientDataset的Delta数据到服务端程序时,出现var and out arguments must match parameter错 ...

  9. 浅谈mysql配置优化和sql语句优化【转】

    做优化,我在这里引用淘宝系统分析师蒋江伟的一句话:只有勇于承担,才能让人有勇气,有承担自己的错误的勇气.有承担错误的勇气,就有去做事得勇气.无论做什么事,只要是对的,就要去做,勇敢去做.出了错误,承担 ...

  10. poj1976

    dp #include <cstdio> #include <cstring> #include <algorithm> using namespace std; ...