自己动手写IOC容器系列 ( 一 )---Class收集器

Class收集器(本章代码:https://gitee.com/kkk3582/MyIOC/tree/v1.0.0

本章代码对应的Tag 为:v1.0.0

前置说明: IOC的总体流程

  1. 扫包,获取被目标注解标注的Class
    • 如 @Service 、@Configuration等
  2. 分析上一步得到的Class
    • 获取类上注解的相关的值
      • 例如,获取@Service(isSinglet = true) 中是否是单例的布尔值
    • 获取该类(以及父类)中被 目标注解标注的Field
      • 如 @Autowired 、 @Value等
  3. 使用反射,并依据上述获取到的所有Class、注解信息实现 getBean系列方法
    • 按BeanId获取
    • 按class name获取
    • 按某个接口,批量获取

注:以上所有注解,只有名称沿用了spring ioc中的命名。所以,发现有用法与你平时接触的不一致时,请不要惊讶。

Class Collector 的实现

Class Collector 的工作目标:将所有Class找出来。
对,它的工作就是这么简单!只需要做这么点工作就行了。要是一切都这么简单,那该有多好啊!

  1. 基本思路:使用递归,扫描整个项目下所有的class文件
  2. 将所有class都搜集起来。(对,你没看错,所有class都搜集起来)

基本思路以及理清楚了,接下来看代码了。

读者:啊,等等,你刚才不是说只是收集被 @Service这些注解标记的class么?
我:啊对对对,目标是只收集特定的class,但当前阶段,我们不用管这么多,一股脑儿都收集起来就对了。日后会再继续处理这些class的。(可能时下一章,也可能是下下章,我也不确定)

首先,我们创建一个类ClassCollector.java,并定义了set<Class<?>> collecte(String path)方法,等待我们去实现它。

package zm.ioc.classCollector;

import org.apache.log4j.Logger;

public class ClassCollector {
    private static final Logger log = Logger.getLogger(ClassCollector.class);    

    public static final ClassCollector CLASS_COLLECTOR = new ClassCollector();
    
    /**
    * 使用单例模式
    */
    private ClassCollector(){}
    
	/**
     * Collecte all classes under the path
     * @param path
     * @return
     */
    public Set<Class<?>> collecte(String path){
        log.debug("Load class start.(Path is " + path + ")");
        return null;
    }
}

第二步,我们需要一个方法递归的遍历指定目录下的所有文件,并找出所有class文件.

前置知识:
java.io.File类能帮我们完成这项任务:

  1. file.listFiles()方法:能列出当前目录下的所有文件(包括子目录)
  2. file.isDirectory()方法:能判断当前file是不是目录
  3. file.getName()方法:获取文件名

所以,我们依靠强大的java.io.File,实现了以下方法

/**
     * 递归收集class
     * @param clazzes 用于收集class的一个容器
     * @param path 当前处理的绝对路径,格式为 /xxx/xxx/xxx
     * @param classPath 与当前目录同步的java package路径 zm.ioc.xxx
     */
    private void doCollecte(Set<Class<?>> clazzes, String path, String classPath){
        File currentFolder = new File(path);//获取当前路径的File对象
        File[] files = currentFolder.listFiles();// 列出当前目录下所有文件(包括子目录)
        if(files == null || files.length == 0){
            return;
        }

		// 开始遍历
        for(File file : files){
            String fileName = file.getName();
            if(file.isDirectory()){ 
            	// 遍历到一个目录时,需要再当前目录的基础上,
            	// 拼接上该子目录后,递归再调用本方法
                String subPath = path + "/" + fileName;//拼接上子目录的名称,获取到子目录的完整路径
                String subClassPath = classPath  + fileName + ".";//同理,获取到子目录对于的java包路径
                this.doCollecte(clazzes, subPath, subClassPath);//递归调用
            }
            //当遍历到的不是一个目录(也就是一个文件)时
            if( this.ifClassFile(file)){
            	// 当文件时class文件时(其实就是判断文件后缀是不是 .class)
            	// 将class文件名去掉后缀并 拼接再当前的java包路径后面
            	// 例如当前包路径为 zm.ioc.test 
            	//     当前class文件名为 Test.class
            	//  可知:class的全名为 zm.ioc.test.Test  
            	// 此时,就可以使用 Class.forName("zm.ioc.test.Test")获取到class对象了。
                String className = classPath + fileName.replace(".class", "");
                Class<?> clazz = this.loadClass(className);
				
				// 不容易啊,经历了这么多磨难,终于把我们要的东西(class对象)收集到了
				// 找个对象真不容易!!!!
                clazzes.add(clazz); 
            }
        }
     // 为了doCollecte方法中的抽象层次而提取的一个方法
     // 用来判断是否时一个class文件
     private boolean ifClassFile(File file){
        return file != null && file.isFile() && file.getName().endsWith(".class");
     }
     
     // 为了doCollecte方法中的抽象层次,并把难看的try-catch藏起来,而提取的一个方法
     // 加载一个class对象
     private Class<?> loadClass(String className){
        try{
            return Class.forName(className);
        }catch(Exception e){
            log.error("Load class error!",e);

        }
        return null; 
      }
    }

第三步,测试一下

public static void main(String[] args) {
        ClassCollector classCollector = ClassCollector.CLASS_COLLECTOR;
        
        //获取class文件所在的目录
        String rootPath = System.getProperty("user.dir") + "/target/classes/";
        
        log.info("Class collect start ! Root path is " + rootPath);
        Set<Class<?>> clazzes = classCollector.collecte(rootPath);
		
		// 打印出来检查下,看看结果是否正确
        clazzes.stream().forEach((c)->log.info(c));
    }

这是再我电脑的执行结果,获取到了两个class对象,分别是:zm.ioc.demo.bean.BeanA, zm.ioc.classCollector.ClassColletor在这里插入图片描述

完工~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值