每日一例 | 手写web服务器:实现全包扫描和简易IOC

共 7615字,需浏览 16分钟

 ·

2021-06-07 11:00

前言

最近写web服务器优点上头,根本停不下来,对,我就是卷王本卷,昨天实现了前端有参方法的调用,趁着这股热劲,今天我们来实现下IOC容器,从原理和实现上来讲,都不难了,因为我们在controllerrequestMapping注解实现的时候已经验证过了,沿着同样的思路搞就行了。好了话不多说,直接开整。

开整

同样是基于我们之前的代码实现,感兴趣的小伙伴可以去看完整代码,文末有项目地址。

Serive注解

写这个注解主要是为了测试,本来要实现Component,一时半会没想起来单词如何拼写,所以就选择了service

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
    String value() default "";
}

注解的内容和前面的都差不多,很简单,就是加了个targetRetention,然后把这个类加在我们的service上即可:

@Service
public class TestService {

    public void helloIoc(String name) {
        System.out.println("hello ioc, " + name);
    }
}

优化包扫描器

之前的包扫描器只能扫描一层目录,这样每层都指定包名就很繁琐,所以我把它简单优化了下,这样只用输入根包名,就可以实现整包扫描。

这里用到了递归,当路径是文件夹时,就会再次调用自己。

 private static void scanPackageToIoc(String packageName, Set<Class> classSet)
            throws IOException, ClassNotFoundException 
{
        logger.info("start to scanPackage, packageName = {}", packageName);
        Enumeration<URL> classes = ClassLoader.getSystemResources(packageName.replace('.''/'));
        while (classes.hasMoreElements()) {
            URL url = classes.nextElement();
            File packagePath = new File(url.getPath());
            if (packagePath.isDirectory()) {
                File[] files = packagePath.listFiles();
                for (File file : files) {
                    String fileName = file.getName();
                    if (file.isDirectory()) {
                        String newPackageName = String.format("%s.%s", packageName, fileName);
                        scanPackageToIoc(newPackageName, classSet);
                    } else {
                        String className = fileName.substring(0, fileName.lastIndexOf('.'));
                        String fullClassName = String.format("%s.%s", packageName, className);
                        classSet.add(Class.forName(fullClassName));
                    }
                }
            } else {
                String className = url.getPath().substring(0, url.getPath().lastIndexOf('.'));
                String fullClassName = String.format("%s.%s", packageName, className);
                classSet.add(Class.forName(fullClassName));
            }
        }
        logger.info("scanPackage end, classSet = {}", classSet);
    }

测试

我们指定个包路径测试下,顺便测试下通过Ioc拿到对象,实现方法调用:

scanPackageToIoc("io.github.syske.boot", classSet);
logger.info("classSet = {}", classSet);
scanRequestMapping(classSet);
logger.info("requestMappingMap = {}", requestMappingMap);
initSyskeBootContent(classSet);
logger.info("contentMap = {}", contentMap);
Object o = contentMap.get("io.github.syske.boot.service.TestService");
if (o instanceof TestService) {
    ((TestService)o).helloIoc("云中志");
}

包扫描完后,生成一个classset集合;

通过scanRequestMapping方法从class集合中拿出controller的类,并生成requestMapping和方法的集合;

通过initSyskeBootContent方法从class集合中拿出service的类,并创建实例,放进contentMap,这样在你需要实例的时候,直接通过全类名(包名 + 类名)就可以拿到,然后执行你想要执行的方法即可。

看下效果:

883  [main] INFO  i.g.s.b.h.SyskeBootContentScanHandler - scanRequestMapping end, requestMappingMap = {/sayHello2=public java.lang.String io.github.syske.boot.controller.TestController.test(java.lang.String,java.lang.String), /sayHello=public java.lang.String io.github.syske.boot.controller.TestController.testName(java.lang.String), /test2=public java.lang.String io.github.syske.boot.controller.Test2Controller.test2(), /test=public java.lang.String io.github.syske.boot.controller.TestController.testRequstMapping()} 
883  [main] INFO  i.g.s.b.h.SyskeBootContentScanHandler - requestMappingMap = {/sayHello2=public java.lang.String io.github.syske.boot.controller.TestController.test(java.lang.String,java.lang.String), /sayHello=public java.lang.String io.github.syske.boot.controller.TestController.testName(java.lang.String), /test2=public java.lang.String io.github.syske.boot.controller.Test2Controller.test2(), /test=public java.lang.String io.github.syske.boot.controller.TestController.testRequstMapping()} 
899  [main] INFO  i.g.s.b.h.SyskeBootContentScanHandler - contentMap = {io.github.syske.boot.service.TestService=io.github.syske.boot.service.TestService@2812cbfa} 
hello ioc, 云中志

方法完美被执行,想法实现,打完收工。

总结

又是看起来复杂、写起来不难的一次需求,但是通过这样的方式,能让你更深入的理解springioc原理,当然原理可能会有差异,但是也大同小异,再退一步来说,就算不一样,面试的时候,面试官问你懂不懂Ioc底层原理,你也可以大胆地我自己做过类似于Ioc东西,这一点就很牛皮了。

最近内卷这个词特别火,所有的平台都在讨论,但是IT这个行业不早都在内卷了吗?面试造火箭,进门拧螺丝,太卷了。

前几天看到一个段子,说是一个公司招司机,面试官问司机,你知道汽车的启动过程吗?能大概说一下吗?然后司机巴拉巴拉说了一大堆:

段子原文地址,有兴趣的小伙伴自己去看:

https://blog.csdn.net/dfskhgalshgkajghljgh/article/details/106457745


下面是项目的开源仓库,有兴趣的小伙伴可以去看看,如果有想法的小伙伴,我真心推荐你自己动个手,自己写一下,真的感觉不错:

https://github.com/Syske/syske-boot
- END -


浏览 36
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报