Java包扫描

一:包扫描简述
1.为什么要使用包扫描?

包扫描主要用于找到带有注解的类。我们知道注解再Java里面用的很频繁,可以配置XML文件或者注解,然后通过反射机制执行想要执行的方法。

2.包扫描介绍
  1. 我们可以通过用户提供的包名或者类名,扫描该包地下的所有类或者该类所在的包。
  2. 通过包扫描,我们可以得到该包下我们所要找的类(例如:带有注解的类或者接口或者枚举类型等)这里主要用于扫描带有注解的类。
  3. 因为找到该类,我们可以通过注解信息得到该类里面带有注解的成员或方法的信息,然后通过反射机制执行。
3.包扫描分类

我们知道再Java里面一个工程下可能由许多包或者Jar包,有时我们所需要的类可能再Jar包里,因此包扫描的时候我们都需要扫描。
但是普通包扫描和Jar包扫描的处理方法又不同,因此我们需要采用分而治之的思想,分开处理。

二:包扫描过程
1.普通包扫描
代码示例

这里根据包名,经过一系列处理(具体看下面详细过程)得到url协议名称,然后判断是Jar包还是普通包,如果是普通包就调用packetScanner(String curFile, String packName)这个方法,进行普通包扫描

private void packetScanner(String curFile, String packName) {
if (!curFile.isDirectory()) {
			return;
}
File[] files = curFile.listFiles();
		for(File file : files) {
			if (file.isFile() && file.getName().endsWith(".class")) {
				String fileName = file.getName().replace(".class", "");
				String className = packName + "." + fileName;
				try {
				Class<?> klass = Class.forName(className);
					dealClass(klass);
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}else if(file.isDirectory()) {
				packetScanner(file, packName + "." + file.getName());
			}
		}
	}
}

public void scanPacket(String packetName) {
       String packetPath = packetName.replace(".", "/");
       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		try {
			Enumeration<URL> resources = classLoader.getResources(packetPath);
			while(resources.hasMoreElements()) {
				URL url = resources.nextElement();
				if (url.getProtocol().equals("jar")) {
					scanJarPacket(url);
				} else {
					File file = new File(url.toURI());
					if (!file.exists()) {
						continue;
					}
					packetScanner(file, packetName);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}
2.Jar包扫描

这里根据包名,经过一系列处理(具体看下面详细过程)得到url协议名称,然后判断是Jar包还是普通包,如果是Jar包就调用private void scanJarPacket(URL url) 这个方法,进行Jar包扫描

代码展示
private void scanJarPacket(URL url) throws IOException {
	JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
	JarFile jarFile = jarURLConnection.getJarFile();
	Enumeration<JarEntry> jarEntries = jarFile.entries();
		while(jarEntries.hasMoreElements()) {
			JarEntry jarEntry = jarEntries.nextElement();
			String jarName = jarEntry.getName();
			if (jarEntry.isDirectory() || !jarName.endsWith(".class")) {
				continue;
			}
			String className = jarName.replace(".class", "").replaceAll("/", ".");
			try {
				Class<?> klass = Class.forName(className);
				if (klass.isAnnotation() 
						|| klass.isEnum()
						|| klass.isInterface()
						|| klass.isPrimitive()) {
					continue;
				}
				dealClass(klass);
				} catch (ClassNotFoundException e) {
						e.printStackTrace();
				}
			}
	}
public void scanPacket(String packetName) {
       String packetPath = packetName.replace(".", "/");
       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		try {
			Enumeration<URL> resources = classLoader.getResources(packetPath);
			while(resources.hasMoreElements()) {
				URL url = resources.nextElement();
				if (url.getProtocol().equals("jar")) {
					scanJarPacket(url);
				} else {
					File file = new File(url.toURI());
					if (!file.exists()) {
						continue;
					}
					packetScanner(file, packetName);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}
3.包扫描代码

下面是包扫描的全部代码

代码展示
package com.mec.packetScan.core;

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

public abstract class PacketScan {
	public PacketScan() {
	}
	
	//这是一个抽象方法,由外面实现
	public abstract void dealClass(Class<?> klass);
	
	private void packetScanner(File curFile, String packName) {
		//如果不是目录就结束方法的调用
		if (!curFile.isDirectory()) {
			return;
		}
		//该方法返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件
		File[] files = curFile.listFiles();
		for(File file : files) {
			if (file.isFile() && file.getName().endsWith(".class")) {
				String fileName = file.getName().replace(".class", "");
				//去掉“.class”后就是文件名,路径名加文件名就是类名
				String className = packName + "." + fileName;
				try {
					//根据类名称得到类类型
					Class<?> klass = Class.forName(className);
					dealClass(klass);
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}else if(file.isDirectory()) {
				//如果该文件是目录就再一次调用此方法,将路径名加文件名(下一次路径)传过去
				//一直这样递归调用,直到是文件为止
				packetScanner(file, packName + "." + file.getName());
			}
		}
	}
	
	private void scanJarPacket(URL url) throws IOException {
		/**
		 * 由API查得:
		 * 该方法返回一个URLConnection实例,表示与URL引用的远程对象的URL
		 * 如果对于URL的协议(如HTTP或JAR),则存在一个属于以下软件包或其子包之一的公共专用URLConnection子类:
		 * java.long, java.io, java.util, java.net,返回的连接将是该子类
		 */
		JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
		/**
		 * 由API查得:
		 * 返回此连接的JAR文件
		 * 如果连接是与JAR文件的条目的连接,则返回JAR文件对象
		 */
		JarFile jarFile = jarURLConnection.getJarFile();
		/**
		 * 由API查得:
		 * 返回zip文件条目的枚举
		 */
		Enumeration<JarEntry> jarEntries = jarFile.entries();
		while(jarEntries.hasMoreElements()) {
			JarEntry jarEntry = jarEntries.nextElement();
			String jarName = jarEntry.getName();
			//如果它是一个目录或者不是“.class”文件,就跳过
			if (jarEntry.isDirectory() || !jarName.endsWith(".class")) {
				continue;
			}
			String className = jarName.replace(".class", "").replaceAll("/", ".");
			try {
				Class<?> klass = Class.forName(className);
				//如果这个类是注解或者枚举或者接口或者八大基本类型就跳过
				if (klass.isAnnotation() 
						|| klass.isEnum()
						|| klass.isInterface()
						|| klass.isPrimitive()) {
					continue;
				}
				//调用抽象类
				dealClass(klass);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
	}
	
	//对包扫描方法重载,可以扫描多个包
	public void scanPacket(String[] packetNamees) {
		for(String packetName : packetNamees) {
			scanPacket(packetName);
		}
	}
	//对包扫描方法重载,提供多个类名,可以扫描该类所在的包
	public void scanPacket(Class<?>[] klasses) {
		for(Class<?> klass : klasses) {
			scanPacket(klass);
		}
	}
	
	public void scanPacket(Class<?> klass) {
		String path = klass.getPackage().getName();
		scanPacket(path);
	}
	
	public void scanPacket(String packetName) {
		/**
		 * 由API查得:
		 * 在windows下是\,但在编程语言\是转义字符的起时字符,所以路径中的\通常需要使用\\
		 * 如果是/就不需要转义了
		 * “\”一般表示本地目录的,比如电脑里的目录。
		 * “/”主要表示远程电脑或者网络上的。
		 * 这里将Java里的传过来的包名里的“.”转换为“/”,供下面的类加载器调用找到类加载器的资源
		 */
		String packetPath = packetName.replace(".", "/");
		/**
		 * 由API查得:
		 * 返回此Thread的上下文ClassLoader,上下文ClassLoader是由线程的创建者提供,
		 * 以便在加载类和资源时在此线程中运行的代码使用。如果不是set,默认是父线程的ClassLoader上下文。
		 * 原始线程的上下文ClassLoader通常设置为用于加载应用程序的类加载器。
		 */
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		try {
			/**
			 * 由API查得:
			 * 找到具有给定名称的资源,资源可以通过独立于代码位置的方式由类代码访问的一些资源(图像、音频、文本等)
			 * 资源的名称是标识资源的“/”分割路径名。
			 * 此方法首先将搜索父类加载器的资源;如果父级是null,则会搜索内置到虚拟机的类加载器的路径
			 */
			Enumeration<URL> resources = classLoader.getResources(packetPath);
			while(resources.hasMoreElements()) {
				//这里得到的是该类的包路径
				URL url = resources.nextElement();
				//url.getProtocol()获取此url协议的名称,如果是文件输出file;如果是jar包,输出jar
				if (url.getProtocol().equals("jar")) {
					//如果是“jar”包就调用“jar”包扫描的方法
					scanJarPacket(url);
				} else {
					//这里实例化一个文件类型的对象,需要uri类型的参数,因此需要将url转换为uri
					//这里得到的file对象是windows系统下的包路径
					File file = new File(url.toURI());
					//如果这个文件不存在,就继续查找
					if (!file.exists()) {
						continue;
					}
					//如果是普通包就调用普通包扫描的方法
					packetScanner(file, packetName);
				}
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}
}

包扫描的调用类
这里包扫描类是一个抽象类,我们实例化包扫描类的时候,需要首先先完成抽象方法
这里只是扫描到类,将其输出

package com.mec.packetScan.test;

import com.mec.packetScan.core.PacketScan;

public class Test {

	public static void main(String[] args) {
		
		PacketScan packetScan = new PacketScan() {
			
			@Override
			public void dealClass(Class<?> klass) {
				System.out.println(klass);
			}
		};
		packetScan.scanPacket("com.mec");
	}
}

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值