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>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span> <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>com.google.guava<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span> <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>guava<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>21.0<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>

       3、实现代码

1
2
3
4
//入参 要扫描的包名
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 {

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第一个class类的集合
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">List&lt;Class&lt;?&gt;&gt; classes = new ArrayList&lt;Class&lt;?&gt;&gt;();</span>
    Set&lt;Class&lt;?&gt;&gt; classes = <span style="color: rgba(0, 0, 255, 1)">new</span> HashSet&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 是否循环迭代</span>
    <span style="color: rgba(0, 0, 255, 1)">boolean</span> recursive = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取包的名字 并进行替换</span>
    String packageDirName = packageName.replace('.', '/'<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 定义一个枚举的集合 并进行循环来处理这个目录下的things</span>
    Enumeration&lt;URL&gt;<span style="color: rgba(0, 0, 0, 1)"> dirs;
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        dirs </span>=<span style="color: rgba(0, 0, 0, 1)"> Thread.currentThread().getContextClassLoader().getResources(packageDirName);
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 循环迭代下去</span>
        <span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)"> (dirs.hasMoreElements()) {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取下一个元素</span>
            URL url =<span style="color: rgba(0, 0, 0, 1)"> dirs.nextElement();
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 得到协议的名称</span>
            String protocol =<span style="color: rgba(0, 0, 0, 1)"> url.getProtocol();
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果是以文件的形式保存在服务器上</span>
            <span style="color: rgba(0, 0, 255, 1)">if</span> ("file"<span style="color: rgba(0, 0, 0, 1)">.equals(protocol)) {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取包的物理路径</span>
                String filePath = URLDecoder.decode(url.getFile(), "UTF-8"<span style="color: rgba(0, 0, 0, 1)">);
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 以文件的方式扫描整个包下的文件 并添加到集合中</span>

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

    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> classes;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> addClass(Set&lt;Class&lt;?&gt;&gt; classes, String filePath, String packageName) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> Exception {
    File[] files </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> File(filePath).listFiles(file -&gt; (file.isFile() &amp;&amp; file.getName().endsWith(".class")) ||<span style="color: rgba(0, 0, 0, 1)"> file.isDirectory());
    </span><span style="color: rgba(0, 0, 255, 1)">assert</span> files != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (File file : files) {
        String fileName </span>=<span style="color: rgba(0, 0, 0, 1)"> file.getName();
        </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (file.isFile()) {
            String classsName </span>= fileName.substring(0, fileName.lastIndexOf("."<span style="color: rgba(0, 0, 0, 1)">));
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">packageName.isEmpty()) {
                classsName </span>= packageName + "." +<span style="color: rgba(0, 0, 0, 1)"> classsName;
            }
            doAddClass(classes, classsName);
        }

    }
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> doAddClass(Set&lt;Class&lt;?&gt;&gt; classes, <span style="color: rgba(0, 0, 255, 1)">final</span> String classsName) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> Exception {
    ClassLoader classLoader </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ClassLoader() {
        @Override
        </span><span style="color: rgba(0, 0, 255, 1)">public</span> Class&lt;?&gt; loadClass(String name) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> ClassNotFoundException {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.loadClass(name);
        }
    };
    classes.add(classLoader.loadClass(classsName));
}


</span><span style="color: rgba(0, 0, 255, 1)">public</span> &lt;A <span style="color: rgba(0, 0, 255, 1)">extends</span> Annotation&gt; Set&lt;Class&lt;?&gt;&gt; getAnnotationClasses(String packageName, Class&lt;A&gt; annotationClass) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> Exception {

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">找用了annotationClass注解的类</span>
    Set&lt;Class&lt;?&gt;&gt; controllers = <span style="color: rgba(0, 0, 255, 1)">new</span> HashSet&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
    Set</span>&lt;Class&lt;?&gt;&gt; clsList =<span style="color: rgba(0, 0, 0, 1)"> getClasses(packageName);
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (clsList != <span style="color: rgba(0, 0, 255, 1)">null</span> &amp;&amp; clsList.size() &gt; 0<span style="color: rgba(0, 0, 0, 1)">) {
        </span><span style="color: rgba(0, 0, 255, 1)">for</span> (Class&lt;?&gt;<span style="color: rgba(0, 0, 0, 1)"> cls : clsList) {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (cls.getAnnotation(annotationClass) != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
                controllers.add(cls);
            }
        }
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> 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();
}