java实现一个扫描包的工具类实例代码

所属分类: 软件编程 / java 阅读数: 21
收藏 0 赞 0 分享

前言

在很多的实际场景中,我们需要得到某个包名下面所有的类,比如我们在使用SpringMVC的时候,知道SpringMVC可以扫描指定包下的所有类,在平时的开发中,我们也有这样的场景,所以今天写一个扫描包的工具类,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

代码如下:

package com.gujin.utils;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class HQScanPackage
{
 /** 是否循环迭代 **/
 boolean recursive = true;
 /** 包名集合 **/
 private List<String> packageNames = new ArrayList<String>();
 /** 过滤器 **/
 private HQScanPackageFilter filter = null;
 /** 监听器 **/
 private HQScanPackageListener listener = null;

 /**
 * 是否循环迭代
 * 
 * @return
 */
 public boolean isRecursive()
 {
  return recursive;
 }

 /**
 * 设置是否循环迭代
 * 
 * @param recursive
 */
 public void setRecursive(boolean recursive)
 {
  this.recursive = recursive;
 }

 /**
 * 获得过滤器
 * 
 * @return
 */
 public HQScanPackageFilter getFilter()
 {
  return filter;
 }

 /**
 * 设置过滤器
 * 
 * @param filter
 */
 public void setFilter(HQScanPackageFilter filter)
 {
  this.filter = filter;
 }

 /**
 * 获得监听器
 * 
 * @return
 */
 public HQScanPackageListener getListener()
 {
  return listener;
 }

 /**
 * 设置监听器
 * 
 * @param listener
 */
 public void setListener(HQScanPackageListener listener)
 {
  this.listener = listener;
 }

 /**
 * 添加扫描包
 * 
 * @param packageName
 */
 public void addPackage(String packageName)
 {
  if (packageName == null || !packageName.matches("[\\w]+(\\.[\\w]+)*"))
  {
   throw new IllegalArgumentException("非法包名.");
  }
  this.packageNames.add(packageName);
 }

 /**
 * 清空扫描包
 */
 public void clearPackage()
 {
  this.packageNames.clear();
 }

 /**
 * 扫描
 */
 public void scan()
 {
  for (String packageName : packageNames)
  {
   scan(packageName);
  }
 }

 /**
 * 是否接受
 * 
 * @param clazz
 * @return
 */
 private boolean accept(Class<?> clazz)
 {
  if (this.filter != null)
  {
   return this.filter.accept(clazz);
  }
  return true;
 }

 /**
 * 触发扫描到合法类
 * 
 * @param clazz
 * @return
 */
 private void trrigerOnScanClass(Class<?> clazz)
 {
  if (this.listener != null)
  {
   this.listener.onScanClass(clazz);
  }
 }

 /**
 * 扫描到类
 * 
 * @param clazz
 * @return
 */
 private void onScanClass(Class<?> clazz)
 {
  if (accept(clazz))
  {
   trrigerOnScanClass(clazz);
  }
 }

 /**
 * 从包package中获取所有的Class
 * 
 * @param packageName
 * @return
 */
 private void scan(String packageName)
 {
  // 获取包的名字 并进行替换
  String packageDirName = packageName.replace('.', '/');
  // 定义一个枚举的集合 并进行循环来处理这个目录下的things
  Enumeration<URL> dirs = null;
  try
  {
   dirs = Thread.currentThread().getContextClassLoader()
    .getResources(packageDirName);
   // 循环迭代下去
   while (dirs.hasMoreElements())
   {
   // 获取下一个元素
   URL url = dirs.nextElement();
   // 得到协议的名称
   String protocol = url.getProtocol();
   // 如果是以文件的形式保存在服务器上
   if ("file".equals(protocol))
   {
    // 获取包的物理路径
    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
    // 以文件的方式扫描整个包下的文件 并添加到集合中
    findAndAddClassesInPackageByFile(packageName, filePath);
   }
   else if ("jar".equals(protocol))
   {
    // 如果是jar包文件
    // 定义一个JarFile
    JarFile jar = null;
    try
    {
     // 获取jar
     jar = ((JarURLConnection) url.openConnection()).getJarFile();
     // 从此jar包 得到一个枚举类
     Enumeration<JarEntry> entries = jar.entries();
     // 同样的进行循环迭代
     while (entries.hasMoreElements())
     {
      // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
      JarEntry entry = entries.nextElement();
      String name = entry.getName();
      // 如果是以/开头的
      if (name.charAt(0) == '/')
      {
      // 获取后面的字符串
      name = name.substring(1);
      }
      // 如果前半部分和定义的包名相同
      if (name.startsWith(packageDirName))
      {
      int idx = name.lastIndexOf('/');
      // 如果以"/"结尾 是一个包
      if (idx != -1)
      {
       // 获取包名 把"/"替换成"."
       packageName = name.substring(0, idx).replace('/',
         '.');
      }
      // 如果可以迭代下去 并且是一个包
      if ((idx != -1) || recursive)
      {
       // 如果是一个.class文件 而且不是目录
       if (name.endsWith(".class") && !entry.isDirectory())
       {
        // 去掉后面的".class" 获取真正的类名
        String className = name.substring(
         packageName.length() + 1,
         name.length() - 6);
        try
        {
         // 添加到classes
         // 使用Class.forName会触发类静态方法
         Class<?> clazz = Thread.currentThread()
          .getContextClassLoader().loadClass(
            packageName + '.' + className);
         onScanClass(clazz);
        }
        catch (ClassNotFoundException e)
        {
         System.err.println("加载类出错");
        }
       }
      }
      }
     }
    }
    catch (IOException e)
    {
     System.err.println("在扫描用户定义视图时从jar包获取文件出错");
     e.printStackTrace();
    }
   }
   }
  }
  catch (IOException e)
  {
   System.err.println("扫描出错");
   e.printStackTrace();
  }
 }

 /**
 * 以文件的形式来获取包下的所有Class
 * 
 * @param packageName
 * @param packagePath
 */
 private void findAndAddClassesInPackageByFile(String packageName,
   String packagePath)
 {
  // 获取此包的目录 建立一个File
  File dir = new File(packagePath);
  // 如果不存在或者 也不是目录就直接返回
  if (!dir.exists() || !dir.isDirectory())
  {
   System.err.println("用户定义包名 " + packageName + " 下没有任何文件");
   return;
  }
  // 如果存在 就获取包下的所有文件 包括目录
  File[] dirfiles = dir.listFiles(new FileFilter()
  {
   // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
   public boolean accept(File file)
   {
   return (recursive && file.isDirectory())
     || (file.getName().endsWith(".class"));
   }
  });
  // 循环所有文件
  for (File file : dirfiles)
  {
   // 如果是目录 则继续扫描
   if (file.isDirectory())
   {
   findAndAddClassesInPackageByFile(packageName + "." + file.getName(),
     file.getAbsolutePath());
   }
   else
   {
   // 如果是java类文件 去掉后面的.class 只留下类名
   String className = file.getName().substring(0,
     file.getName().length() - 6);
   try
   {
    Class<?> clazz = Thread.currentThread().getContextClassLoader()
      .loadClass(packageName + '.' + className);
    onScanClass(clazz);
   }
   catch (ClassNotFoundException e)
   {
    System.err.println("添加用户自定义视图类错误 找不到此类的.class文件");
    e.printStackTrace();
   }
   }
  }
 }

 /**
 * 扫描包过滤器
 * 
 * @author jianggujin
 *
 */
 public static interface HQScanPackageFilter
 {
  /**
  * 是否接受
  * 
  * @param clazz
  * @return
  */
  public boolean accept(Class<?> clazz);
 }

 /**
 * 扫描包监听
 * 
 * @author jianggujin
 *
 */
 public static interface HQScanPackageListener
 {
  /**
  * 扫描到合法类执行
  * 
  * @param clazz
  */
  public void onScanClass(Class<?> clazz);
 }

 public static void main(String[] args)
 {
  HQScanPackage scanPackage = new HQScanPackage();
  scanPackage.addPackage(HQScanPackage.class.getPackage().getName());
  scanPackage.setFilter(new HQScanPackageFilter()
  {

   @Override
   public boolean accept(Class<?> clazz)
   {
   return true;
   }
  });
  scanPackage.setListener(new HQScanPackageListener()
  {

   @Override
   public void onScanClass(Class<?> clazz)
   {
   System.out.println(clazz);
   }
  });
  scanPackage.scan();
 }
}

运行可以得到如下结果:

class com.gujin.utils.HQScanPackage$1 
class com.gujin.utils.HQScanPackage$2 
class com.gujin.utils.HQScanPackage$3 
interface com.gujin.utils.HQScanPackage$HQScanPackageFilter 
interface com.gujin.utils.HQScanPackage$HQScanPackageListener 
class com.gujin.utils.HQScanPackage 

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

更多精彩内容其他人还在看

Java的面向对象编程基本概念学习笔记整理

这篇文章主要介绍了Java的面向对象编程基本概念学习笔记整理,包括类与方法以及多态等支持面向对象语言中的重要特点,需要的朋友可以参考下
收藏 0 赞 0 分享

Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法

这篇文章主要介绍了Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法 的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

基于Java实现杨辉三角 LeetCode Pascal's Triangle

这篇文章主要介绍了基于Java实现杨辉三角 LeetCode Pascal's Triangle的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

Java中Spring获取bean方法小结

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中获取Spring配置的bean呢?下面通过本文给大家介绍Java中Spring获取bean方法小结,对spring获取bean方法相关知识感兴趣的朋友一起学习吧
收藏 0 赞 0 分享

如何计算Java对象占用了多少空间?

在Java中没有sizeof运算符,所以没办法知道一个对象到底占用了多大的空间,但是在分配对象的时候会有一些基本的规则,我们根据这些规则大致能判断出来对象大小,需要的朋友可以参考下
收藏 0 赞 0 分享

剖析Java中的事件处理与异常处理机制

这篇文章主要介绍了Java中的事件处理与异常处理机制,讲解Java是如何对事件或者异常作出响应以及定义异常的一些方法,需要的朋友可以参考下
收藏 0 赞 0 分享

详解Java的Struts2框架的结构及其数据转移方式

这篇文章主要介绍了详解Java的Struts2框架的结构及其数据转移方式,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

Java封装好的mail包发送电子邮件的类

本文给大家分享了2个java封装好的mail包发送电子邮件的类,并附上使用方法,小伙伴们可以根据自己的需求自由选择。
收藏 0 赞 0 分享

在Java的Struts中判断是否调用AJAX及用拦截器对其优化

这篇文章主要介绍了在Java的Struts中判断是否调用AJAX及用拦截器对其优化的方法,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

java多线程Future和Callable类示例分享

JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。今天我们就来研究下Future和Callab
收藏 0 赞 0 分享
查看更多