架构探险(三)自定义类加载器

今天我们来谈一谈架构探险中自定义的类加载器,一般我们若想实现自定义的类加载器,可以继承ClassLoader类,然后实现findClass方法即可,详细介绍可以看以下链接:
https://www.cnblogs.com/doit8791/p/5820037.html

本文主要谈一下架构探险中的实现方式。在getClassLoader方法中我们拿到的实际上时当前线程的ClassLoader。然后loadClass则直接调用Class.forName()方法,loadClass方法实现了类加载。核心的方法是怎么扫描一个包文件下的所有class文件。具体实现请看代码:

public static ClassLoader getClassLoader() {
        //这里我们获取当前线程的类加载器
        return Thread.currentThread().getContextClassLoader();
    }

    /**
     * 加载类
     * @param className
     * @param isInitialized
     * @return
     */
    public static Class<?> loadClass(String className, boolean isInitialized) {
        Class<?> cls = null;

        try {
            cls = Class.forName(className, isInitialized, getClassLoader());
        } catch (ClassNotFoundException ex) {
            logger.error("ClassNotFoundException", className);
        }

        return cls;
    }

    /**
     * 获取指定包名下的所有类
     * @param packageName
     * @return
     */
    public static Set<Class<?>> getClassSet(String packageName) {
        Set<Class<?>> classSet = new HashSet<>();

        try {
            Enumeration<URL> urlEnumeration = getClassLoader().getResources(packageName.replace(".", "/"));

            while (urlEnumeration.hasMoreElements()) {
                URL url = urlEnumeration.nextElement();

                if(url != null) {
                    String protocol = url.getProtocol();

                    if(protocol.equals("file")) {
                        String packagePath = url.getPath().replace("%20", "");
                        addClass(classSet, packagePath, packageName);
                    } else if(protocol.equals("jar")) {
                        // ??
                        // 可能永远执行不到,经测试如果直接把jar文件,copy到src路径下,则读取到jar时所用的protocol依然是file.
                        // JarUrlConnection 必须使用URL url = new Url("jar:file:/xxx.jar!/");
                        // ??
                        // 如果包含jar 我们需要提取jar里的class文件, 这里用的时JarURLConnection
                        // 这里还有其他选择,我们可以使用fastjson来提取jar文件
                        JarURLConnection jarUrlConn = (JarURLConnection) url.openConnection();

                        if(jarUrlConn != null) {
                            JarFile jarFile = jarUrlConn.getJarFile();

                            Enumeration<JarEntry> entities = jarFile.entries();

                            while (entities.hasMoreElements()) {
                                JarEntry jarEntity = entities.nextElement();
                                String jarEntityName = jarEntity.getName();
                                if(jarEntityName.endsWith(".class")) {
                                    String className = jarEntityName.substring(0, jarEntityName.lastIndexOf(".")).replace("/", ".");

                                    doAddClass(classSet, className);
                                }
                            }
                        }

                    }
                }
            }
        } catch (IOException ex) {
            logger.error("get class set failure", ex);
            throw new RuntimeException(ex);
        }

        return classSet;
    }

    private static void addClass(Set<Class<?>> classSet, String packagePath, final String packageName) {
        final File[] files = new File(packagePath).listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                return (file.exists() && 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(StringUtils.isNotEmpty(packageName)) {
                    className = String.format("%s.%s", packageName, className);
                }

                doAddClass(classSet, className);
            } else {
                // 如果是目录,继续扫描
                String subPackagePath = fileName;

                if(StringUtils.isNotEmpty(packagePath)) {
                    //作为新的扫描路径
                    subPackagePath = String.format("%s/%s", packagePath, fileName);
                }

                String subPackageName = fileName;

                if(StringUtils.isNotEmpty(packageName)) {
                    //扫描新的包,因为加载类需要全路径,所以我们需要不断拼接
                    subPackageName = String.format("%s.%s", packageName, subPackageName);
                }

                addClass(classSet, subPackagePath, subPackageName);
            }
        }
    }

    private static void doAddClass(Set<Class<?>> classSet, String className) {
        Class<?> clazz = loadClass(className, false);
        classSet.add(clazz);
    }

代码关于如何获取jar文件中的class文件,笔者在注释中写出来自己的一些疑问,欢迎大家分享自己的见解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值