• java获取包下被指定注解的类


    方案一: 采用reflections 框架(此框架依赖com.google.guava)

            1、reflections框架地址:https://github.com/ronmamo/reflections

            2、项目依赖

            <dependency>
                <groupId>org.reflections</groupId>
                <artifactId>reflections</artifactId>
                <version>0.9.11</version>
            </dependency>
    
            <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId>
                <version>21.0</version>
            </dependency>

           3、实现代码

     //入参 要扫描的包名
     Reflections f = new Reflections("com.ggband.netty.execute.command");
     //入参 目标注解类
     Set<Class<?>> set = f.getTypesAnnotatedWith(Cmd.class);
    

    方案二: 采用ClassLoader扫描

         1、实现代码

    package com.ggband.netty;
    
    import java.io.File;
    import java.io.IOException;
    import java.lang.annotation.Annotation;
    import java.net.JarURLConnection;
    import java.net.URL;
    import java.net.URLDecoder;
    import java.util.Enumeration;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.jar.JarEntry;
    import java.util.jar.JarFile;
    
    public class Scanner {
        /**
         * 从包package中获取所有的Class
         *
         * @param packageName
         * @return
         */
        public Set<Class<?>> getClasses(String packageName) throws Exception {
    
            // 第一个class类的集合
            //List<Class<?>> classes = new ArrayList<Class<?>>();
            Set<Class<?>> classes = new HashSet<>();
            // 是否循环迭代
            boolean recursive = true;
            // 获取包的名字 并进行替换
            String packageDirName = packageName.replace('.', '/');
            // 定义一个枚举的集合 并进行循环来处理这个目录下的things
            Enumeration<URL> dirs;
            try {
                dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
                // 循环迭代下去
                while (dirs.hasMoreElements()) {
                    // 获取下一个元素
                    URL url = dirs.nextElement();
                    // 得到协议的名称
                    String protocol = url.getProtocol();
                    // 如果是以文件的形式保存在服务器上
                    if ("file".equals(protocol)) {
                        // 获取包的物理路径
                        String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                        // 以文件的方式扫描整个包下的文件 并添加到集合中
                        addClass(classes, filePath, packageName);
                    } else if ("jar".equals(protocol)) {
                        // 如果是jar包文件
                        // 定义一个JarFile
                        JarFile jar;
                        try {
                            // 获取jar
                            jar = ((JarURLConnection) url.openConnection()).getJarFile();
                            // 从此jar包 得到一个枚举类
                            Enumeration<JarEntry> entries = jar.entries();
                            // 同样的进行循环迭代
                            while (entries.hasMoreElements()) {
                                // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
                                JarEntry entry = entries.nextElement();
                                String name = entry.getName();
                                // 如果是以/开头的
                                if (name.charAt(0) == '/') {
                                    // 获取后面的字符串
                                    name = name.substring(1);
                                }
                                // 如果前半部分和定义的包名相同
                                if (name.startsWith(packageDirName)) {
                                    int idx = name.lastIndexOf('/');
                                    // 如果以"/"结尾 是一个包
                                    if (idx != -1) {
                                        // 获取包名 把"/"替换成"."
                                        packageName = name.substring(0, idx).replace('/', '.');
                                    }
                                    // 如果可以迭代下去 并且是一个包
                                    if ((idx != -1) || recursive) {
                                        // 如果是一个.class文件 而且不是目录
                                        if (name.endsWith(".class") && !entry.isDirectory()) {
                                            // 去掉后面的".class" 获取真正的类名
                                            String className = name.substring(packageName.length() + 1, name.length() - 6);
                                            try {
                                                // 添加到classes
                                                classes.add(Class.forName(packageName + '.' + className));
                                            } catch (ClassNotFoundException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                    }
                                }
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return classes;
        }
    
        public void addClass(Set<Class<?>> classes, String filePath, String packageName) throws Exception {
            File[] files = new File(filePath).listFiles(file -> (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory());
            assert files != null;
            for (File file : files) {
                String fileName = file.getName();
                if (file.isFile()) {
                    String classsName = fileName.substring(0, fileName.lastIndexOf("."));
                    if (!packageName.isEmpty()) {
                        classsName = packageName + "." + classsName;
                    }
                    doAddClass(classes, classsName);
                }
    
            }
        }
    
        public void doAddClass(Set<Class<?>> classes, final String classsName) throws Exception {
            ClassLoader classLoader = new ClassLoader() {
                @Override
                public Class<?> loadClass(String name) throws ClassNotFoundException {
                    return super.loadClass(name);
                }
            };
            classes.add(classLoader.loadClass(classsName));
        }
    
    
        public <A extends Annotation> Set<Class<?>> getAnnotationClasses(String packageName, Class<A> annotationClass) throws Exception {
    
            //找用了annotationClass注解的类
            Set<Class<?>> controllers = new HashSet<>();
            Set<Class<?>> clsList = getClasses(packageName);
            if (clsList != null && clsList.size() > 0) {
                for (Class<?> cls : clsList) {
                    if (cls.getAnnotation(annotationClass) != null) {
                        controllers.add(cls);
                    }
                }
            }
            return controllers;
        }
    
    
    }

      2、使用:

      Set<Class<?>> set = new Scanner().getAnnotationClasses("com.ggband.netty.execute.command", Cmd.class);

    扩充:现在就可以实现自己的业务了,比如 扫描com.ggband.netty.execute.command包下被Cmd注解的类 得到Cmd注解value和被注解类的实例

     Map<String, Command> beanContainer = new HashMap<>();
    try {
    //@1 采用reflections 框架(此框架依赖com.google.guava)
    // Reflections f = new Reflections("com.ggband.netty.execute.command");
    // Set<Class<?>> set = f.getTypesAnnotatedWith(Cmd.class);
    //@2 采用ClassLoader扫描
    Set<Class<?>> set = new Scanner().getAnnotationClasses("com.ggband.netty.execute.command", Cmd.class);
    for (Class<?> c : set) {
    Object bean = c.newInstance();
    Cmd annotation = c.getAnnotation(Cmd.class);
    beanContainer.put(Arrays.toString(annotation.value()), (Command) bean);
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    ggband
  • 相关阅读:
    application.properties多环境配置文件、jar包外部配置文件、配置项加密、程序中配置使用
    SpringBoot项目打war包部署Tomcat教程
    spring boot 集成 redis lettuce
    spring boot maven打包可运行jar包
    IDEA项目搭建十四——Web站点Controller基类及布局页静态资源设计
    Nginx Windows详细安装部署教程
    多线程编程CompletableFuture与parallelStream
    IDEA项目搭建十三——服务消费端与生产端通信实现
    【异常】MySQL建表报错:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"order"' at line 1
    【警告】mysql链接警告信息:Establishing SSL connection without server's identity verification is not recommended
  • 原文地址:https://www.cnblogs.com/ggband/p/11668879.html
Copyright © 2020-2023  润新知