JAVA扫描包

我们在使用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

时间: 2024-08-02 12:35:48

JAVA扫描包的相关文章

java扫描某个包下的所有java类并加载

最近在学习java的反射和注解,实际情景中需要扫描某个包下的所有java类,然后使用类加载器加载类. 基本思路,获得程序的路径扫描src下某个包内的子包和java类,实现也比较简单. 运行环境:windows10+jdk1.8+eclipse 直接贴代码 package org.test.scanner; import java.io.File; import java.util.ArrayList; import java.util.List; /* * date:2019-07-23 * *

java.lang包

作者:gnuhpc 出处:http://www.cnblogs.com/gnuhpc/ 1.特性——不用import 2.String String x = "abc"; <=> String x= new String("abc"); 因为public final class java.lang.String; 而String x="The number " + y;中,在JAVA中不管是什么变量或者对象,在对String进行加和时

配置文件编译问题:扫描包的问题 和 编译字符编码问题 和编译器版本1.5问题

配置文件编译问题:扫描包的问题 和 编译字符编码问题 和编译器版本1.5问题<build > <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes&

JDK源码简析--java.lang包中的基础类库

题记 JDK,Java Development Kit. 我们必须先认识到,JDK只是,仅仅是一套Java基础类库而已,是Sun公司开发的基础类库,仅此而已,JDK本身和我们自行书写总结的类库,从技术含量来说,还是在一个层级上,它们都是需要被编译成字节码,在JRE中运行的,JDK编译后的结果就是jre/lib下得rt.jar,我们学习使用它的目的是加深对Java的理解,提高我们的Java编码水平. 本系列所有文章基于的JDK版本都是1.7.16. 本节内容 在本节中,简析java.lang包所包

1.java.io包中定义了多个流类型来实现输入和输出功能,

1.java.io包中定义了多个流类型来实现输入和输出功能,可以从不同的角度对其进行分 类,按功能分为:(C),如果为读取的内容进行处理后再输出,需要使用下列哪种流?(G)   A.输入流和输出流 B.字节流和字符流 C.节点流和处理流   D.File stream E.Pipe stream F.Random stream G.Filter stream

java的包导入机制总结与剖析

java中有两种包的导入机制 总结如下: 1>>单类型导入(single-type-import),例如import java.io.File;   2>>按需类型导入(type-import-on-demand),例如 import java.io.*; 关于这两种导入类型大家各有所爱,众说纷纭.这里分析一下这两种导入类型的大致工作原理供大家参考. 单类型导入比较好理解,仅仅导入一个public类或者接口.而对于按需类型导入,有人误解为导入一个包下的所有类,其实不然,看名字就知道

Java io包中特殊的抽象类FilterReader和FilterWriter实践

刚才在看帮助文档的时候,发现Java io包中FilterReader和FilterWriter类没使用过,所以想实践一下. 还用以前的老方法,new一个出来,结果提示有错误: Cannot instantiate the type FilterReader 不能实例化? 一查帮助文档原来这样写道: public abstract class FilterReader extends Reader Abstract class for reading filtered character str

java中包命名常见规则

做java的都知道java的包.类.接口.枚举.方法.常量.变量等等模型都有一套约定的命名规则! 学习每一种语言都应该学习对应语法和命名规则,以保持一个良好的编码风格.一来显示自己的专业.二来方便阅读和检查,快速供其他人参考,尽快理解项目代码结构! 今天首先谈谈一些常用的java package命名规则,以后有时间再写写java其他的一些规范! 首先我们该知道的是,java的包名都是由小写单词组成的,类名的首字母都是大写,也就是我们常用的驼峰写法,包路径一般都是对系统模板的定义与归类.这样看了包

java中包容易出现的错误及权限问题

/* 3,权限在不同地方的体现: public protected default private 同一类中: ok ok ok ok 同一包中: ok ok ok 子类中: ok ok 不同包中: ok 在同一包中子类中是可以访问父类中默认权限(default)的方法,但是在不同包中不行! 在不同包中如果子类继承了父类,那么子类就可以访问父类中的protected方法! */ //import mytest.*; /* package mytest; public class DemoA{ p