获取特定注解的类(代码)

该文章介绍了一种方法,通过Java反射在指定包下查找包含特定注解的class文件。主要处理file和jar协议,遍历目录和jar文件,加载类并检查是否包含目标注解。
查找特定包名下的注解为annotationClass的class文件,本文只需要处理file和jar。
上代码。

package cn.ac.bcc.ebap.modules;
import org.apache.log4j.Logger;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Created by zhanghaipeng on 2019/8/2.
 */
public class GetClassesWithAnnotation {

    private static final Logger log = Logger.getLogger(GetClassesWithAnnotation.class);

    /**
     * 查找特定包名下的注解为annotationClass的class文件
     * 本文只需要处理file和jar
     * @param packageName 包名
     * @param annotationClass 待查注解的class
     * @return List<Class<?>> class集合
     */
    private List<Class<?>> getClassesWithAnnotationFromPackage(String packageName, Class<? extends Annotation> annotationClass) {
        List<Class<?>> classList = new ArrayList<Class<?>>();
        String packageDirName = packageName.replace('.', '/');
        Enumeration<URL> dirs = null;

        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
        }
        catch (IOException e) {
            log.error("Failed to get resource", e);
            return null;
        }

        while (dirs.hasMoreElements()) {
            URL url = dirs.nextElement();
            String protocol = url.getProtocol();//file

            //https://docs.oracle.com/javase/7/docs/api/java/net/URL.html
            //http, https, ftp, file, and jar
            //本文只需要处理file和jar
            if ("file".equals(protocol) ) {
                String filePath = null;
                try {
                    filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    log.info ("filePath{}:"+filePath);
                }
                catch (UnsupportedEncodingException e) {
                    log.error("Failed to decode class file", e);
                }
                getClassesWithAnnotationFromFilePath(packageName, filePath, classList, annotationClass);
            } else if ("jar".equals(protocol)) {
                JarFile jar = null;
                try {
                    jar = ((JarURLConnection) url.openConnection()).getJarFile();
                    //扫描jar包文件 并添加到集合中
                }
                catch (Exception e) {
                    log.error("Failed to decode class jar", e);
                }

                List<Class<?>> alClassList = new ArrayList<Class<?>>();
                findClassesByJar(packageName, jar, alClassList);
                getClassesWithAnnotationFromAllClasses(alClassList, annotationClass, classList);
            }
            else {
                log.warn("can't process the protocol={}"+protocol);
            }
        }

        return classList;
    }

    private static void findClassesByJar(String pkgName, JarFile jar, List<Class<?>> classes) {
        String pkgDir = pkgName.replace(".", "/");
        Enumeration<JarEntry> entry = jar.entries();

        while (entry.hasMoreElements()) {
            // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文
            JarEntry jarEntry = entry.nextElement();
            String name = jarEntry.getName();
            // 如果是以/开头的
            if (name.charAt(0) == '/') {
                // 获取后面的字符串
                name = name.substring(1);
            }

            if (jarEntry.isDirectory() || !name.startsWith(pkgDir) || !name.endsWith(".class")) {
                continue;
            }
            //如果是一个.class文件 而且不是目录
            // 去掉后面的".class" 获取真正的类名
            String className = name.substring(0, name.length() - 6);
            Class<?> tempClass = loadClass(className.replace("/", "."));
            // 添加到集合中去
            if (tempClass != null) {
                classes.add(tempClass);
            }
        }
    }

    /**
     * 加载类
     * @param fullClsName 类全名
     * @return
     */
    private static Class<?> loadClass(String fullClsName ) {
        try {
            return Thread.currentThread().getContextClassLoader().loadClass(fullClsName );
        } catch (ClassNotFoundException e) {
            log.error("PkgClsPath loadClass", e);
        }
        return null;
    }
    
    private void getClassesWithAnnotationFromFilePath(String packageName, String filePath, List<Class<?>> classList,
                                                      Class<? extends Annotation> annotationClass) {
        Path dir = Paths.get(filePath);

        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
            for(Path path : stream) {
                String fileName = String.valueOf(path.getFileName()); // for current dir , it is 'helloworld'
                //如果path是目录的话, 此处需要递归,
                boolean isDir = Files.isDirectory(path);
                if(isDir) {
                    getClassesWithAnnotationFromFilePath(packageName + "." + fileName , path.toString(), classList, annotationClass);
                }
                else if(fileName.endsWith (".class")) {
                    String className = fileName.substring(0, fileName.length() - 6);

                    Class<?> classes = null;
                    String fullClassPath = packageName + "." + className;
                    try {
                        log.info("fullClassPath={}"+fullClassPath);
//                        ClassLoader cl = ClassLoader.getSystemClassLoader ( );
                        ClassLoader cl = Thread.currentThread().getContextClassLoader();
                        classes = cl.loadClass (fullClassPath);
                    }
                    catch (ClassNotFoundException e) {
                        log.error("Failed to find class={}"+fullClassPath, e);
                    }

                    if (null != classes ) {
//                        log.info(classes);
                        Annotation a = classes.getAnnotation(annotationClass);
                        if(null != a){
                            //查看Table注解的name
                            try {
                                Method m = annotationClass.getMethod ("name", null);
                                Object o = m.invoke (a, null);
                                log.info (o);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace ( );
                            } catch (InvocationTargetException e) {
                                e.printStackTrace ( );
                            } catch (NoSuchMethodException e) {
                                e.printStackTrace ( );
                            }
                            //查看Table注解的name
                            classList.add (classes);
                        }
                    }
                }
            }
        }
        catch (IOException e) {
            log.error("Failed to read class file", e);
        }
    }

    private void getClassesWithAnnotationFromAllClasses(List<Class<?>> inClassList,
                                                        Class<? extends Annotation> annotation, List<Class<?>> outClassList) {
        for(Class<?> myClasss : inClassList) {
            if (null != myClasss && null != myClasss.getAnnotation(annotation)) {
                outClassList.add(myClasss);
            }
        }
    }

    public static void main(String[] args) {
        GetClassesWithAnnotation getClassesWithAnnotation = new GetClassesWithAnnotation();
        String packageName = "cn.ac.bcc.ebap.modules.sys.entity";
        Class<? extends Annotation> annotationClass = javax.persistence.Table.class;
        List<Class<?>> classes = getClassesWithAnnotation.getClassesWithAnnotationFromPackage(packageName, annotationClass);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值