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();
            }
        });
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM