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. fopen()、fwrite()、fread()函数使用说明与示例

    fopen()函数: 1.作用: 在C语言中fopen()函数用于打开指定路径的文件,获取指向该文件的指针. 2.函数原型: FILE * fopen(const char * path,const  ...

  2. nginx配置详情(总结)

    Nginx简介 Nginx是一款开源代码的高性能HTTP服务器和反向代理服务器,同时支持IMAP/POP3/SMTP代理服务 Nginx工作原理 Nginx由内核和模块组成,完成工作是通过查找配置文件 ...

  3. ConcurrentHashMap 产生NullPointerException

    今天测试在发给我一段报错日志后,根据日志定位到从ConcurrentHashMap 的缓存中get的时候,ConcurrentHashMap的底层抛出了空指针,当时感觉很奇怪为什么在get的时候产生空 ...

  4. HTML面试基础问题

    1.Doctype作用?严格模式与混杂模式如何区分?它们有何意义?   1)<!DICTYPE>声明位于文档中的最前面,处于<html>标签之前,告诉浏览器的解析器,用什么文档 ...

  5. 【学习DIV+CSS】2. 学习CSS(一)--CSS控制页面样式

    1. CSS如何控制页面 使用XHTML+CSS布局页面,其中有一个很重要的特点就是“结构与表现相分离”(结构指XHTML,表现指CSS).有人这样描述这种分离的关系,结构XHTML好比一个人,表现C ...

  6. CSS line-height与行内框

    一.line-height的定义 line-height,行高,是指文本行基线间的垂直距离. 1.    什么是基线? 一般而言,一个文本行一共有四条线,从上到下依次为顶线.中线.基线.底线:在英文中 ...

  7. bootstrap-datetimepicker中设置中文

    1.引入插件文件,同时引入相应的语言文件 <script src="bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.j ...

  8. 20165320 预备作业3 :Linux安装及命令入门

    一.VirtualBox与Linux的安装 我是按照老师给的链接下的最新版本的VirtualBox5.26,然后Ubuntu软件(版本是16.04,最新的是17)是自己在网上找的旧版本下好的,因为我在 ...

  9. JS简介——(一)

    0.结构

  10. os._exit(), sys.exit(), exit()

    1. sys.exit(n) 退出程序引发SystemExit异常, 可以捕获异常执行些清理工作. n默认值为0, 表示正常退出. 其他都是非正常退出. 还可以sys.exit("sorry ...