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)"><</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">></span>com.google.guava<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">></span>guava<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">></span>21.0<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">></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<Class<?>> classes = new ArrayList<Class<?>>();</span> Set<Class<?>> classes = <span style="color: rgba(0, 0, 255, 1)">new</span> HashSet<><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<URL><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<Class<?>> 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 -> (file.isFile() && 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<Class<?>> 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<?> 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> <A <span style="color: rgba(0, 0, 255, 1)">extends</span> Annotation> Set<Class<?>> getAnnotationClasses(String packageName, Class<A> 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<Class<?>> controllers = <span style="color: rgba(0, 0, 255, 1)">new</span> HashSet<><span style="color: rgba(0, 0, 0, 1)">(); Set</span><Class<?>> 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> && clsList.size() > 0<span style="color: rgba(0, 0, 0, 1)">) { </span><span style="color: rgba(0, 0, 255, 1)">for</span> (Class<?><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();
}