实现一个简单的类加载器

功能

  用来加载某包下的所有类,比如使用了某些注解的所有类,比如所有的@Service,或者Controller类
  实现类加载器的3个功能

  • 获取类加载器
  • 根据类名称加载类
  • 获取指定包下的所有类

获取类加载器


  获取类加载器的实现非常简单,简单到只需要一句话,获取当前线程的ClassLoader即可。
  

     /**
     * 获取类加载器
     * 
     * @return
     */
    public static ClassLoader getClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }

根据类名称加载类


根据类名称加载类,这个方法是利用Class.forName实现的。相当于在Class.forName修饰了一层。实现如下:

/**
     * 加载类
     *
     * @param className     类名
     * @param isInitialized 此处的是否初始化标志指的是是否执行类的静态代码块
     * @return
     */
    public static Class<?> loadClass(String className, boolean isInitialized) {
        Class<?> cls;
        try {
            cls = Class.forName(className, isInitialized, getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return cls;
    }

一个值得注意的点是: 参数 boolean isInitialized表达的标志位指的是是否执行类的静态代码块,为了提高性能可以设置为false。


获取指定包下的所有类


  获取指定包下的所有类是最麻烦的一件事情,因为包可以是file,也可是jar包。另外在包名和实际包所在的路径需要转换,比如需要把”.”转为”/” 把”%20”转换为” “;对于jar包的类,使用JarURLConnnection连接
  


    /**
     * 获取指定包下的所有类
     *
     * @param packageName
     * @return
     */
    public static Set<Class<?>> getClassSet(String packageName) {
        Set<Class<?>> classSet = new HashSet<Class<?>>();
        try {
            /*获取资源,将包名替换为包路径*/
            Enumeration<URL> urlEnumeration = getClassLoader().getResources(packageName.replaceAll(".", "/"));
            while (urlEnumeration.hasMoreElements()) {
                URL url = urlEnumeration.nextElement();
                if (url != null) {
                    String protocol = url.getProtocol();
                    /*file方式处理*/
                    if (protocol.equals("file")) {
                        /*%20换成空格*/
                        String packagePath = url.getPath().replaceAll("%20", " ");
                        addClass(classSet, packagePath, packageName);
                    } else if (protocol.equals("jar")) { /*jar包处理*/
                        /*打开连接*/
                        JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                        if (jarURLConnection != null) {
                            JarFile jarFile = jarURLConnection.getJarFile();
                            if (jarFile != null) {
                                /*遍历元素*/
                                Enumeration<JarEntry> jarEntries = jarFile.entries();
                                while (jarEntries.hasMoreElements()) {
                                    JarEntry jarEntry = jarEntries.nextElement();
                                    /*以class结尾*/
                                    if (jarEntry.getName().endsWith(".class")) {
                                        /*获取类名,并将"/"转换为"."*/
                                        String className = jarEntry.getName().substring(0, jarEntry.getName().lastIndexOf(".")).replaceAll("/", ".");
                                        addClassAction(classSet, className);
                                    }
                                }
                            }
                        }
                    }
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return classSet;
    }

    /**
     * 内部调用loadClass,并将Class添加到Set中
     *
     * @param classSet
     * @param className
     */
    private static void addClassAction(Set<Class<?>> classSet, String className) {
        Class<?> cls = loadClass(className, false);
        classSet.add(cls);
    }

    /**
     * @param classSet
     * @param packagePath
     * @param packageName
     */
    private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) {
        File[] files = new File(packagePath).listFiles(new FileFilter() {
            /*将所有的是class文件和目录文件返回*/
            public boolean accept(File file) {
                return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
            }
        });
        for (File file : files) {
            String fileName = file.getName();
            /*文件方式处理*/
            if (file.isFile()) {
                String className = fileName.substring(0, fileName.lastIndexOf("."));
                if (packageName != null) {
                    className = packageName + "." + className;
                }
                addClassAction(classSet, className);
            } else {  /*文件夹方式处理*/
                String subPackagePath = fileName;
                if (packagePath != null) {
                    /*子目录*/
                    subPackagePath = packagePath + "/" + subPackagePath;
                }
                String subPackageName = fileName;
                if (packageName != null) {
                    /*子包名*/
                    subPackageName = packageName + "." + subPackageName;
                }
                /*递归处理*/
                addClass(classSet, subPackagePath, subPackageName);
            }
        }
    }

  函数注释写得很清楚,可以细细琢磨。
 
 
一个值得注意的细节,关于匿名内部类,如果要访问外部类的变量,则需要再外部变量加final修饰 如下所示:
  原因是执行匿名内部类的时候,形成的函数的栈帧无法访问到外部类栈帧的局部变量,所有把外部变量final化则可以访问到这个变量,具体可以参考其他文章。只是突然想到这个知识点并稍微说说而已。

   */
    private static void addClass(Set<Class<?>> classSet, String packagePath, final String packageName) {
        File[] files = new File(packagePath).listFiles(new FileFilter() {
            /*将所有的是class文件和目录文件返回,一个细节,如果匿名内部类里要使用 packageName 则在addClass参数中要加入修饰词 final
            * 即 final String packageName
            public boolean accept(File file) {
                return (packageName != null && file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
            }
        });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值