赞
踩
要扫描包下的所有类,分类路径下扫描和jar包扫描两种,其中jar包扫描又分项目中引入的三方jar包,同级maven的多个子项目jar相互引用,还有jdk jar包(这里不考虑,一般没哪个项目会扫描jdk jar包里的类).
我先声明一个接口,用于应对不同类型的class扫描:
- public interface Scan {
-
- String CLASS_SUFFIX = ".class";
-
- Set<Class<?>> search(String packageName, Predicate<Class<?>> predicate);
-
- default Set<Class<?>> search(String packageName){
- return search(packageName,null);
- }
-
- }
其中 Predicate 是jdk8新增的Lambda表达式相关内容,可以筛选要扫描的类。
从类路径扫描指定包下的类:
- public class FileScanner implements Scan {
-
- private String defaultClassPath = FileScanner.class.getResource("/").getPath();
-
- public String getDefaultClassPath() {
- return defaultClassPath;
- }
-
- public void setDefaultClassPath(String defaultClassPath) {
- this.defaultClassPath = defaultClassPath;
- }
-
- public FileScanner(String defaultClassPath) {
- this.defaultClassPath = defaultClassPath;
- }
-
- public FileScanner(){}
-
- private static class ClassSearcher{
- private Set<Class<?>> classPaths = new HashSet<>();
-
- private Set<Class<?>> doPath(File file,String packageName, Predicate<Class<?>> predicate,boolean flag) {
-
- if (file.isDirectory()) {
- //文件夹我们就递归
- File[] files = file.listFiles();
- if(!flag){
- packageName = packageName+"."+file.getName();
- }
-
- for (File f1 : files) {
- doPath(f1,packageName,predicate,false);
- }
- } else {//标准文件
- //标准文件我们就判断是否是class文件
- if (file.getName().endsWith(CLASS_SUFFIX)) {
- //如果是class文件我们就放入我们的集合中。
- try {
- Class<?> clazz = Class.forName(packageName + "."+ file.getName().substring(0,file.getName().lastIndexOf(".")));
- if(predicate==null||predicate.test(clazz)){
- classPaths.add(clazz);
- }
- } catch (ClassNotFoundException e) {
- throw new ScannerClassException(e.getMessage(),e);
- }
- }
- }
- return classPaths;
- }
- }
-
- @Override
- public Set<Class<?>> search(String packageName, Predicate<Class<?>> predicate) {
- //先把包名转换为路径,首先得到项目的classpath
- String classpath = defaultClassPath;
- //然后把我们的包名basPack转换为路径名
- String basePackPath = packageName.replace(".", File.separator);
- String searchPath = classpath + basePackPath;
- return new ClassSearcher().doPath(new File(searchPath),packageName, predicate,true);
- }
- }
从jar中扫描相关类:
- public class JarScanner implements Scan {
-
- @Override
- public Set<Class<?>> search(String packageName, Predicate<Class<?>> predicate) {
-
- Set<Class<?>> classes = new HashSet<>();
-
- try {
- //通过当前线程得到类加载器从而得到URL的枚举
- Enumeration<URL> urlEnumeration = Thread.currentThread().getContextClassLoader().getResources(packageName.replace(".", "/"));
- while (urlEnumeration.hasMoreElements()) {
- URL url = urlEnumeration.nextElement();//得到的结果大概是:jar:file:/C:/Users/ibm/.m2/repository/junit/junit/4.12/junit-4.12.jar!/org/junit
- String protocol = url.getProtocol();//大概是jar
- if ("jar".equalsIgnoreCase(protocol)) {
- //转换为JarURLConnection
- JarURLConnection connection = (JarURLConnection) url.openConnection();
- if (connection != null) {
- JarFile jarFile = connection.getJarFile();
- if (jarFile != null) {
- //得到该jar文件下面的类实体
- Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
- while (jarEntryEnumeration.hasMoreElements()) {
- /*entry的结果大概是这样:
- org/
- org/junit/
- org/junit/rules/
- org/junit/runners/*/
- JarEntry entry = jarEntryEnumeration.nextElement();
- String jarEntryName = entry.getName();
- //这里我们需要过滤不是class文件和不在basePack包名下的类
- if (jarEntryName.contains(".class") && jarEntryName.replaceAll("/", ".").startsWith(packageName)) {
- String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replace("/", ".");
- Class cls = Class.forName(className);
- if(predicate == null || predicate.test(cls)){
- classes.add(cls);
- }
- }
- }
- }
- }
- }else if("file".equalsIgnoreCase(protocol)){
- //从maven子项目中扫描
- FileScanner fileScanner = new FileScanner();
- fileScanner.setDefaultClassPath(url.getPath().replace(packageName.replace(".", "/"),""));
- classes.addAll(fileScanner.search(packageName,predicate));
- }
- }
- }catch (ClassNotFoundException | IOException e){
- throw new ScannerClassException(e.getMessage(),e);
- }
- return classes;
- }
- }
之后,我们可以使用一个委派模式对以上两个结果进行合并,得出扫描结果:
- public class ScanExecutor implements Scan {
-
- private volatile static ScanExecutor instance;
-
- @Override
- public Set<Class<?>> search(String packageName, Predicate<Class<?>> predicate) {
- Scan fileSc = new FileScanner();
- Set<Class<?>> fileSearch = fileSc.search(packageName, predicate);
- Scan jarScanner = new JarScanner();
- Set<Class<?>> jarSearch = jarScanner.search(packageName,predicate);
- fileSearch.addAll(jarSearch);
- return fileSearch;
- }
-
- private ScanExecutor(){}
-
- public static ScanExecutor getInstance(){
- if(instance == null){
- synchronized (ScanExecutor.class){
- if(instance == null){
- instance = new ScanExecutor();
- }
- }
- }
- return instance;
- }
-
- }
封装一个工具类,方便调用:
- public class ClassScannerUtils {
-
- public static Set<Class<?>> searchClasses(String packageName){
- return searchClasses(packageName,null);
- }
-
- public static Set<Class<?>> searchClasses(String packageName, Predicate predicate){
- return ScanExecutor.getInstance().search(packageName,predicate);
- }
-
- }
源码地址:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。