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. React - 环境准备

    1. 下载 node.js,在 https://nodejs.org/en/download/ 选择合适的版本. 2. 检查 node.js 是否安装成功. hueyMB:~ zhisenzheng$ ...

  2. Java入门系列(三)面向对象三大特性之封装、继承、多态

    面向对象综述 封装 封装的意义,在于明确标识出允许外部使用的所有成员函数和数据项,或者叫接口. 有了封装,就可以明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者:而外部调用者也可以知道 ...

  3. JS代码判断浏览器版本,支持IE6,IE7,IE8,IE9!三种方法!

    web开发的时候有时候会用到JS检测IE的版本,下面是检测Microsoft Internet Explorer版本的三种代码! 方法一: <script type="text/jav ...

  4. 基本控件文档-UISegment属性

    CHENYILONG Blog 基本控件文档-UISegment属性 Fullscreen   UISegment属性技术博客http://www.cnblogs.com/ChenYilong/ 新浪 ...

  5. laravel 和 thinkphp 条件查询的区别

    laravel:以二维数组形式where查询,可以为空,即,该条where不运行: thinkphp:以字符串形式查询,不能为空.

  6. python 进程 线程 协程

    并发与并行:并行是指两个或者多个事件在同一时刻发生:而并发是指两个或多个事件在同一时间间隔内发生.在单核CPU下的多线程其实都只是并发,不是并行. 进程是系统资源分配的最小单位,进程的出现是为了更好的 ...

  7. AopProxyUtils.getSingletonTarget(Ljava/lang/Object;)Ljava/lang/Object;大坑

    这个问题太坑了,试了好多个版本,都是依赖冲突导致的, https://blog.csdn.net/qq_15003505/article/details/78430595 最后找到这一篇博客解决了,就 ...

  8. 诺贝斯特(厦门)电气有限公司http://www.thebest.cn.com/

    诺贝斯特(厦门)电气有限公司,公司位于厦门市湖里区塘边社168号.是一家专注于智能电网用户端智能配用电以及电气安全产品研发.生产和销售的高新技术企业:致力于为工矿企业.建筑楼宇以及基础设施等智能电网用 ...

  9. js原生选择class DOM元素

    document.querySelector(".demo"); querySelector() 方法返回匹配指定选择器的第一个元素.如果需要返回所有的元素,使用 querySel ...

  10. Linux XOR.DDoS样本取证特征与清除

    一.取证特征 1)获取进程ID 使用top命令,查看占用内存率最高的十位随机名称进程名(示例:进程名pygdykcrqf) 2)获取进程对应路径 Linux 在启动一个进程时,系统会在/proc下创建 ...