手写spring(三)------------------------------------------------------------------初始化配置文件、扫描包获取类名、实例化

项目地址:https://github.com/gongxianshengjiadexiaohuihui/noobspring

通过手写spring(一)我们知道,我们可以调用父类的config获取初始化参数contextConfigLocation的值,这里面在spring中存的是一个正则表达式,这里我为了图方便,并没用用正则表达式(后续会完善),得到这个之后我们就知道配置文件的路径,所以我们      doLoadConfig的工作就是读取配置文件的内容并保存

 private void doLoadConfig(String path){
        logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>初始化配置文件");
        /**
         * 先把classpath:剥离,后续在添加内容
         */
        InputStream fis = null;
        try {
            fis = this.getClass().getClassLoader().getResourceAsStream(path.replace("classpath:",""));
            p.load(fis);
        }catch (Exception e){
            logger.error(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>初始化配置文件失败");
            e.printStackTrace();
            throw new RuntimeException("初始化配置文件失败");
        }finally {
            if(null != fis){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();

                }
            }
        }

      logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>初始化配置文件成功");
    }

第一步把classpath:剥离出来

然后获取 此路径下文件的输入流,并且把内容保存到配置文件p中

这里来讲一下,Class.getResourceAsStream和Class.getClassLoader.getResourceAsStream的路径问题

Class.getResourceAsStream(String path) path不以‘/’开头时默认是从此类所在的包下获取资源,以‘/’开头是从ClassPath根下获取

第二个,默认是从ClassPath的根下获取,path不能以‘/’开头

还有一种是ServletContext.getResourceAsStream默认是从WebApp根目录下获取资源

我们从配置文件中获取了需要扫描的包路径,然后扫描包和其子目录,并保存扫描到的类名

    private void doScanner(String packageName){
        URL url = this.getClass().getClassLoader().getResource(packageName.replaceAll("\\.","/"));
        File dir = new File(url.getFile());
        for(File file: dir.listFiles()){
            if(file.isDirectory()){
                doScanner(packageName + "." + file.getName());
            }else{
                classNames.add(packageName + "." + file.getName().replace(".class"," ").trim());
            }

        }
    }

这里是一个递归函数,我们因为我们不仅要扫描该包,还要扫描该包下的子目录,所以如果发现该包下的文件是目录结构,就再次调用此函数。如果不是就保存该类的全限定名,保存到className中

接下来是利用java反射进行实例化并保存到ioc容器中

 private void doInstance(){
        logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>开始实例化");
        if(classNames.size() == 0){
            return;
        }
        try{
            for(String className: classNames){
                Class<?> clazz = Class.forName(className);
                if(clazz.isAnnotationPresent(NBController.class)){
                    /**
                     * 将类首字母小写
                     */
                    String beanName = StringUtil.lowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName,clazz.newInstance());
                }else if(clazz.isAnnotationPresent(NBService.class)){
                    /**
                     * 如果@NBService有value的话
                     */
                    String beanName = clazz.getAnnotation(NBService.class).value();
                    if(!"".equals(beanName.trim())){
                        beanName = StringUtil.lowerFirstCase(beanName);
                        ioc.put(beanName , clazz.newInstance());
                        continue;
                    }
                    /**
                     * 如果@NBService的value为空,则根据其实现的接口创建实例
                     */
                    Class<?>[] interfaces = clazz.getInterfaces();
                    for(Class<?> tamp : interfaces){
                        String tampName = StringUtil.lowerFirstCase(tamp.getSimpleName());
                        ioc.put(tampName, clazz.newInstance());
                    }

                }else if(clazz.isAnnotationPresent(NBComponent.class)){
                    String beanName = StringUtil.lowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName,clazz.newInstance());
                }else{
                    continue;
                }
            }
        }catch (Exception e){
            logger.error(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>实例化失败");
            e.printStackTrace();
            throw new RuntimeException("实例化失败");
        }
        logger.debug("实例化的类:{}",ioc.keySet().toString());
        logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>实例化成功");
    }

第一步是判断前一步有没有扫描到类名,如果没有就直接返回

接着我们就可以理解为什么说注解起到的是标记作用了,我们通过类名获该class文件,然后判断它是否被@NBController,@NBComponent标记,如果是,就把它的类名(首字母小写)作为key,实例作为value保存在

类型为hashMap的ioc中。

特殊的是@NBServcie,被它标记的往往是接口实现类,但是我们知道一个类可以实现多个接口,所以我们需要判断,如果注解指明了key值,我们就按照它的值进行保存,如果没有指明,我们需要遍历它实现的所有接口,分别一一保存

猜你喜欢

转载自blog.csdn.net/qq_33543634/article/details/87069845
今日推荐