Java通过ClassLoader加载指定的Jar包,并读取Jar包内类上的注解

说明

项目上有个需求,在系统启动时需要动态的加载Jar包,并且扫描jar包中类的注解。

实现

package org.yingqiang.system.annotation.scanner;

import java.io.*;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarInputStream;
import java.util.zip.ZipEntry;

/**
 * 查找jar包下的类是否包含Annotation
 */
public final class JarAnnotationScanner {

    private File jarFile;

    public JarAnnotationScanner(File jarFile) {
        this.jarFile = jarFile;
    }

    /**
     * 扫描
     *
     * @param annotationClass
     */
    public List<Class> scan(Class annotationClass) {
        List<Class> classList = new ArrayList<>();
        try (JarInputStream jarInputStream = new JarInputStream(new FileInputStream(jarFile))) {
            ZipEntry zipEntry;
            while ((zipEntry = jarInputStream.getNextEntry()) != null) {
                String entryName = zipEntry.getName();
                if (entryName.endsWith(".class")) {
                    String className = entryName.replace(".class", "").replaceAll("/", ".");
                    className = className.replaceAll("\\$", ".");
                    byte[] classData = loadClassData(jarInputStream);
                    JarClassLoader jarClassLoader = new JarClassLoader(classData);
                    try {
                        Class clazz = jarClassLoader.findClass(className);
                        Annotation annotation = clazz.getAnnotation(annotationClass);
                        if (annotation != null) {
                            classList.add(clazz);
                        }
                    } catch (Exception e) {
                        // ignore
                    }
                }
                jarInputStream.closeEntry();
            }
        } catch (Exception | NoClassDefFoundError e) {
            System.err.println(e.getMessage());
        }
        return classList;
    }

    /**
     * @param inputStream
     * @return
     * @throws IOException
     */
    private byte[] loadClassData(InputStream inputStream) throws IOException {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                baos.write(buffer, 0, length);
            }
            return baos.toByteArray();
        }
    }

    private class JarClassLoader extends ClassLoader {

        private byte[] classData;

        public JarClassLoader(byte[] classData) {
            this.classData = classData;
        }

        @Override
        protected Class findClass(String name) throws ClassNotFoundException {
            return defineClass(name, classData, 0, classData.length);
        }
    }

    /**
     * @param clazz
     * @return
     */
    public static String getPackageName(Class clazz) {
        if (clazz != null) {
            return clazz.getName().substring(0, clazz.getName().lastIndexOf('.'));
        }
        return "";
    }

}

测试

package org.yingqiang.system.annotation.scanner;

import java.io.File;
import java.lang.annotation.Annotation;
import java.util.List;

public class JarAnnotationScannerTest {

    public static void main(String[] args) {
        File jarFile = new File("D:\\Temp\\addon\\sms-email.jar");
        JarAnnotationScanner jarAnnotationScanner = new JarAnnotationScanner(jarFile);
        String annotationClassString = "org.springframework.boot.autoconfigure.SpringBootApplication";
        Class annotationClass = null;
        try {
            annotationClass = Class.forName(annotationClassString);
        } catch (ClassNotFoundException e) {
            // e.printStackTrace();
        }
        List<Class> classList = jarAnnotationScanner.scan((Class) annotationClass);
        for (Class clazz : classList) {
            System.out.println(clazz.getName());
            System.out.println(JarAnnotationScanner.getPackageName(clazz));
        }
    }

}

相关文章

Java常用的包(package)有哪些?有什么作用?

常用的包有8个,分别是:1.Java.lang包这个包下包含了Java语言的核心类,如String、Math、Sytem和Thread类等,使用这个包无需使用import语句导入,系统会自动导入这个包...

Idea项目的创建和模块的导入Java中方法的三种调用方式和方法重载

一:如何创建工程和模块File-->New-->Project...Eempt Project-->Next输入Finish-->Ok-->New Window关闭刚创建...

Node.js开发者必须了解的4个JS要点

Node.js是一个面向服务器的框架,立足于Chrome强大的V8 JS引擎。尽管它由C++编写而成,但是它及其应用是运行在JS上的。本文为开发者总结了4个Node.js要点。1. 非阻塞(Non-b...

Android应用编程基础第22篇:Java中的import

上一篇我们讲了包(package)的概念以及使用。今天我们讲讲如何在一个类中访问其他在不同包中的类的成员,这里我们要学习一个新的关键字import。到目前为止,所介绍的类都是属于同一个包(packag...

javaagent介绍、使用、实现详解

javaagent介绍jdk提供了一种强大的可以对已有class代码进行运行时注入修改的能力。 javaagent可以在启动时通过-javaagent:agentJarPath或运行时attach加载...