关于包扫描的解决以及通过包扫描获取加注解的类,执行相应方法

前段时间遇到了一个需要用XML文件映射关系处理的问题,当时使用XML文件配置再解析解决的,在最近时间发现有个更加思路清晰以及方便处理的方法就是使用注解,所以这一篇就给大家讲一下如何进行包扫描,以及获取对应注解下的类、方法、成员、并且执行对应方法。

· 包扫描

首先要进行包扫描,得将包分为Jar包和普通包,并且将包名变为JVM能找到的路径名,代码如下:

import java.io.File;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public abstract class ScannerPackage {
	public void scannerPackage(String packageName) {
		String packagePath = packageName.replace('.', '/');
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		try {
			Enumeration<URL> resources = classLoader.getResources(packagePath);
			while (resources.hasMoreElements()) {
				URL url = resources.nextElement();
				//按Jar包扫描
				if (url.getProtocol().equals("jar")) {
					scanJpackage(url);
				} else {
				// 按普通包扫描
					File root = new File(url.toURI());
					scanPackage(root, packageName);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	//对于包扫描后得到的类,要给出外部一个能调用的处理方法,所以给成抽象类
	public abstract void dealClass(Class<?> klass);
	
	//这是对普通包的扫描
	private void scanPackage(File root, String packageName) {
		if (!root.isDirectory()) {
			return;
		}
		File[] files = root.listFiles();
		for (File file : files) {
			if (file.isFile() && file.getName().endsWith(".class")) {
				String fileName = file.getName().replace(".class", "");
				String className = packageName + "." + fileName;
				try {
					Class<?> klass = Class.forName(className);
					dealClass(klass);
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			} else if (file.isDirectory()) {
				scanPackage(file, packageName + "." + file.getName());
			}
		}
	}
	
	
	//这是对Jar包的扫描
	private void scanJpackage(URL url) {
		try {
			JarURLConnection urlConnection = (JarURLConnection) url.openConnection();
			JarFile jarFile = urlConnection.getJarFile();
			Enumeration<JarEntry> jarEntries = jarFile.entries();
			while (jarEntries.hasMoreElements()) {
				JarEntry jarEntry = jarEntries.nextElement();
				String name = jarEntry.getName();
				if (jarEntry.isDirectory() || !name.endsWith(".class")) {
					continue;
				}
				name = name.replace(".class", "");
				String className = name.replace('/', '.');
				Class<?> klass = Class.forName(className);
				dealClass(klass);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
}

下面给出测试类测试的截图:

这是工程文件:

java引入的jar包扫描注解 java扫描注解的类_java

这是测试类代码:

java引入的jar包扫描注解 java扫描注解的类_jar_02

这是结果:

java引入的jar包扫描注解 java扫描注解的类_测试类_03


从结果可以看出来,即使我没有给全包名仍然找出来了下面的类;

· 注解

既然我们已经做好了这个包扫描的工具,那么我们要处理的就是如何使用这个工具得到注解下的类,以及执行相应的方法,首先我们给出注解类,图如下:

分别给出对于方法的注解

java引入的jar包扫描注解 java扫描注解的类_java引入的jar包扫描注解_04

java引入的jar包扫描注解 java扫描注解的类_java引入的jar包扫描注解_05

java引入的jar包扫描注解 java扫描注解的类_jar_06

作为示例,我给ClassA加了注解,如下:

java引入的jar包扫描注解 java扫描注解的类_测试类_07

然后我们的任务就是通过包扫描,得到这个类,然后执行这个方法,注意,这个类中有个方法是没有注解的:
下面是测试类的代码,使用了我们上面做出来的包扫描工具:

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.mec.Annotation.AMethod;
import com.mec.Annotation.AType;
import com.mec.ScannerPackage.ScanPackage;

public class Test {

	public static void main(String[] args) {
		new ScanPackage() {
			
			@Override
			public void dealClass(Class<?> klass) {
				//这里我们排除了接口,八大基本类,数组,枚举,注解类,还有没加注解的类的干扰
				if (klass.isInterface() || klass.isPrimitive()
						|| klass.isArray() || klass.isAnnotation()
						|| klass.isEnum()
						|| !klass.isAnnotationPresent(AType.class)) {
					return;
				}	
				processClass(klass);
			}

			private void processClass(Class<?> klass) {
				try {
					Object object = klass.newInstance();
					Method[] methods = klass.getDeclaredMethods();
					for(Method method:methods) {
						if(!method.isAnnotationPresent(AMethod.class)) {						
							continue;
						}
						System.out.println(method.getName());				
						// 在这里要根据方法的对象和参数的值反射执行该方法
						Field[] fields = klass.getFields();
						try {
							method.invoke(object, "1",2);
						} catch (IllegalArgumentException | InvocationTargetException e) {
							e.printStackTrace();
						}
					}									
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}	
			}
		}.scannerPackage("com.mec");

	}

}

结果如下:

java引入的jar包扫描注解 java扫描注解的类_jar_08


可以看出来我们成功取得了有注释的方法名字,并且成功执行了有注释的方法!

总结:

  • 包扫描要注意分为Jar包和普通包处理!为什么要分?(牵扯到类加载器的问题)。
  • 对于输入包名不全要做递归处理
  • 注解的创建以及使用也要注意

上述代码没有进行jar部分的测试图片,但是本人亲自测试可以执行,不仅可以取出jre中的还能取出自定义jar包中的,所以请大家放心!(不想贴图的原因主要还是太懒。。),对了,如果你需要反射机制执行自定义jar包中注解类以及下面的方法,一定要注意打包的时候勾选一个选项,图我贴在下面,一定要注意!!! 不然是无法取出来的。

java引入的jar包扫描注解 java扫描注解的类_测试类_09


第二个选项一定要打对勾!
第二个选项一定要打对勾!!
第二个选项一定要打对勾!!!

查询了相关资料,好像这个是生成文件夹目录结构,反正打上对勾就对了!