Java扫描包

Posted 疯狂的妞妞

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java扫描包相关的知识,希望对你有一定的参考价值。

在Java中,经常会有 “ 扫描某个包,然后找出全部的Class ” 的需求,Spring对这方面提供了支持,直接用即可,

Reflections这个工具包也很不错,Maven的依赖如下:

(之前写Hessian服务的时候都介绍过了)

<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.10</version>
</dependency>

Java扫描包

因为是偏底层的东西,自己编码虽然可以实现,但是代码安全会是一个非常麻烦、并且难以解决的问题;

以下代码,扫描自己的代码完全没问题,扫描别人代码的前提,必须保证代码不是瞎写的,.class文件确定可以被转换为Class对象的;

(当作是学习和交流吧,改进方案是对.class文件进行校验,校验通过之后,再将.class文件转换为Class对象,如果有兴趣自己继续完善吧,我已经弃坑了)。

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.List;

import com.sea.common.util.Resource;
import com.sea.common.util.StrStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 编译任意class文件,需要保证class文件可用,否则ERROR
 * 
 * @author ChenSS 2017年8月25日上午10:04:38
 *
 */
@Deprecated
public class MyClassLoader extends ClassLoader{
    private static Logger logger = LoggerFactory.getLogger(ClassLoader.class);

    public MyClassLoader() {
        super(Resource.getClassLoader());
    }
    
    public static void main(String[] args) throws ClassNotFoundException {
        MyClassLoader loader = new MyClassLoader();
        try {
            List<Class<?>> clazz = loader.load("C:/Users/ChenSS/Desktop/代码测试/build/classes/", "com.css.common.util");
            System.out.println(clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public List<Class<?>> load(String location, String pkg) {
        return load(new File(location), pkg);
    }

    public List<Class<?>> load(File file, String pkg) {
        //StrStream是自定义的字符串校验工具,用于查找前缀是pkg(包名)、并且不包含美元符的文件
        StrStream stream = new StrStream().prefix(pkg).notExists(‘$‘);
        return load(file, stream);
    }

    public List<Class<?>> load(File file, StrStream stream) {
        List<Class<?>> classes = new ArrayList<>();
        if (file.exists() && file.isDirectory()) {
            for (File dir : likeFile(file, "", stream)) {
                if (dir.isDirectory()) {
                    traverseFile(dir, dir.getName(), classes, stream);
                } else {
                    // 根目录的class
                }
            }
        }
        return classes;
    }

    /**
     * 递归搜索全部文件,查找全部.class文件
     * @param dir
     * @param pkg
     * @param classes
     * @param stream
     */ 
    public void traverseFile(File dir, String pkg, List<Class<?>> classes, StrStream stream) {
        for (File file : likeFile(dir, pkg, stream)) {
            if (file.isDirectory()) {
                //文件夹就遍历下一级目录
                traverseFile(file, pkg + ‘.‘ + file.getName(), classes, stream);
            } else {
                //根据.class文件直接生成Class<?>
                String className = null;
                try {
                    String fileName = file.getName();
                    className = pkg + ‘.‘ + fileName.substring(0, fileName.length() - 6);
                    Class<?> clazz = format(file, className);
                    logger.debug(clazz.toString());
                    classes.add(clazz);
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(className + " : load error");
                    continue;
                }
            }
        }
    }

    public Class<?> format(String fileName, String className) throws Exception {
        return format(new File(fileName), className);
    }

    /**
     * 从.class文件直接生成Class<?>,从IO到对象的转换,IO是未经过检查的;
     * 因此,如果想要使用此类,必须保证全部的.class文件可以创建Java类;
     * 手动编码、并且编译通过的.class文件不会有问题;
     * 扫描自己不熟悉的.class文件时,如果.class文件内容是错误的,有可能产生强制中断主函数的Error。
     */
    public Class<?> format(File file, String className) throws Exception {
        FileInputStream fis = new FileInputStream(file);
        FileChannel fileC = fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        WritableByteChannel wbc = Channels.newChannel(baos);
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
        while (fileC.read(buffer) > 0) {
            buffer.flip();
            wbc.write(buffer);
            buffer.clear();
        }
        byte[] b = baos.toByteArray();
        IOUtils.closeQuietly(baos, wbc, fileC, fis);
        return defineClass(className, b, 0, b.length);
    }

    /**
     * 
     * @param dir 文件夹
     * @param string 路径名
     * @param stream 字符串校验类
     * @return
     */
    public static File[] likeFile(File dir, String string, StrStream stream) {
        return dir.listFiles(new FileFilter() {

            @Override
            public boolean accept(File file) {
                return stream.string(string).like() || file.isFile();
            }
        });
    }
}

 

以上是关于Java扫描包的主要内容,如果未能解决你的问题,请参考以下文章

Java扫描classpath指定包路径下所有class

Android zxing Journeyapps 条码扫描器内部片段

Java 包扫描器

java 用于反射的Java方法(包扫描)

Java扫描包

无法在片段中启动 qrCode 相机扫描仪