Java扫描包

在Java中,经常会有 “ 扫描某个包,然后找出全部的Class ” 的需求。

Spring对这方面提供了支持,直接用即可,AbstractApplicationContext (上下文)、ConfigurableListableBeanFactory(BeanFactory)等对象
都可以实现扫描包的效果。

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Controller;

class BeanFactoryPostProcessorExample implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //获取带有Controller注解的Bean
        String[] names= beanFactory.getBeanNamesForAnnotation(Controller.class);
    }
}

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

    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>22.0</version>
    </dependency>
    <dependency>
      <groupId>org.reflections</groupId>
      <artifactId>reflections</artifactId>
      <version>0.9.10</version>
    </dependency>
    <dependency>
      <groupId>org.javassist</groupId>
      <artifactId>javassist</artifactId>
      <version>3.15.0-GA</version>
    </dependency>
        Reflections reflections = new Reflections("com.sea.server.hessian");
        Set<Class<?>> hessianImpls = reflections.getTypesAnnotatedWith(Controller.class);

Java扫描包

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

代码直接通过IO流转对象的方式实现,代码运行成功的前提是.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();
            }
        });
    }
}
原文地址:https://www.cnblogs.com/chenss15060100790/p/9158011.html