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