包扫描工具实现(详解)

文章介绍了如何在Java中实现包扫描,通过扫描指定包下的类,特别是带有特定注解的类,以便后续使用反射机制进行处理。文章提供了从普通包和Jar包中扫描的实现代码,并通过接口`IClassDealer`让用户自定义处理找到的类。
摘要由CSDN通过智能技术生成

前言

注解在 Java 是一个非常重要的存在,而且它出现的非常频繁。

在一个工程下可能有许多的包或者Jar包,为了结合注解可以准确的定位到一个需要的类上,并且扫描到一个包下的所有类方便我们使用反射机制,所以产生了包扫描工具实现的想法,它可以帮我们找到带有注解的类,再通过反射执行。

包扫描

我们可以通过用户提供的包名,扫描该包下的所有类。

实现思路(需求分析):

通过博主思考,我们需要实现功能:

  • 一个工程下存在普通包或者Jar包,分开处理 Jar 包和普通包;

  • 得到该包下我们所要找的类(例如:带有注解的类或者接口或者枚举类型等)这里主要用于扫描带有注解的类。

因为通过包扫描找到该类,我们可以通过注解信息得到该类里面带有注解的成员或方法的信息,然后通过反射机制执行。

具体实现

  • 创建一个 IClassDealer接口:
public interface IClassDealer {
	void classDealer(Class<?> klass);
}
  • 包扫描类实现:

    • 这里根据包名,通过得到 url 协议名称,然后判断是 Jar 包还是普通包,如果是普通包就调用 fileScan(String packageName, File dir)这个方法,进行普通包扫描,如果是 Jar 包,就调用jarScan(String packageName, URL url),进行 Jar 扫描。

      	public void scanPackage(String packageName) throws URISyntaxException {
      		String packagePath = packageName.replace(".", "/");
      		URL url = Thread.currentThread().getContextClassLoader().getResource(packagePath);
      		if (url.getProtocol().equals("file")) {
      			File root = new File(url.toURI());
      			fileScan(packageName, root);
      		} else if (url.getProtocol().equals("jar")) {
      			try {
      				jarScan(packageName, url);
      			} catch (IOException e) {
      				e.printStackTrace();
      			}
      		}
      	}
      
    • 普通包扫描

      	private void fileScan(String packageName, File dir) {
      		File[] files = dir.listFiles();
      		for (File file : files) {
      			if (file.isDirectory()) {
      				fileScan(packageName + "." + file.getName(), file);
      			} else {
      				String fileName = file.getName();
      				if (!fileName.endsWith(".class") || fileName.contains("$")) {
      					continue;
      				}
      				fileName = fileName.replace(".class", "");
      				String className = packageName  + "." + fileName;
      				try {
      					Class<?> klass = Class.forName(className);
      					classDealer.classDealer(klass);
      				} catch (ClassNotFoundException e) {
      					e.printStackTrace();
      					continue;
      				}
      			}
      		}
      	}
      
    • Jar 扫描

      	private void jarScan(String packageName, URL url) throws IOException {
      		JarURLConnection connection = (JarURLConnection) url.openConnection();
      		JarFile jarFile = connection.getJarFile();
      		Enumeration<JarEntry> entries = jarFile.entries();
      		while (entries.hasMoreElements()) {
      			JarEntry entry = entries.nextElement();
      			String entryName = entry.getName();
      			String className = entryName.replace("/", ".");
      			
      			if (!className.startsWith(packageName) || className.contains("$") || !entryName.endsWith(".class")) {
      				continue;
      			}
      			className = className.replace(".class", "");
      			try {
      				Class<?> klass = Class.forName(className);
      				classDealer.classDealer(klass);
      			} catch (ClassNotFoundException e) {
      				e.printStackTrace();
      			}
      		}
      	}
      

完整代码

  • 创建一个 IClassDealer接口:
public interface IClassDealer {
	void classDealer(Class<?> klass);
}
  • 包扫描具体代码:
package com.hb.util;

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 class PackageScanner {
    /**
    *因为扫描到的类的相关具体操作不是本工具需要完成的,
	*因此给一个接口,具体操作由工具使用者完成。
	*用这个方法可以去筛选使用者需要扫描的包下的类达到自己的目的。
    */
	private IClassDealer classDealer;
	
	public PackageScanner() {
		this.classDealer = new IClassDealer() {
			@Override
			public void classDealer(Class<?> klass) {
			}
		};
	}

	public PackageScanner addClassDealer(IClassDealer classDealer) {
		this.classDealer = classDealer;
		return this;
	}

	public void scanPackage(String packageName) throws URISyntaxException {
		String packagePath = packageName.replace(".", "/");
		URL url = Thread.currentThread().getContextClassLoader().getResource(packagePath);
		if (url.getProtocol().equals("file")) {
			File root = new File(url.toURI());
			fileScan(packageName, root);
		} else if (url.getProtocol().equals("jar")) {
			try {
				jarScan(packageName, url);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void jarScan(String packageName, URL url) throws IOException {
		JarURLConnection connection = (JarURLConnection) url.openConnection();
		JarFile jarFile = connection.getJarFile();
		Enumeration<JarEntry> entries = jarFile.entries();
		while (entries.hasMoreElements()) {
			JarEntry entry = entries.nextElement();
			String entryName = entry.getName();
			String className = entryName.replace("/", ".");
			
			if (!className.startsWith(packageName) || className.contains("$") || !entryName.endsWith(".class")) {
				continue;
			}
			className = className.replace(".class", "");
			try {
				Class<?> klass = Class.forName(className);
				classDealer.classDealer(klass);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void fileScan(String packageName, File dir) {
		File[] files = dir.listFiles();
		for (File file : files) {
			if (file.isDirectory()) {
				fileScan(packageName + "." + file.getName(), file);
			} else {
				String fileName = file.getName();
				if (!fileName.endsWith(".class") || fileName.contains("$")) {
					continue;
				}
				fileName = fileName.replace(".class", "");
				String className = packageName  + "." + fileName;
				try {
					Class<?> klass = Class.forName(className);
					classDealer.classDealer(klass);
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
					continue;
				}
			}
		}
	}
}

测试运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HB0o0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值