zoukankan      html  css  js  c++  java
  • 手写web服务器:定义Autorwired注解,实现属性自动注入

    前言

    昨天,我们已经解决了post请求的阻塞问题,所以我们今天又可以继续搞事情了,今天我们要实现的也是spring中很核心的注解——Autowired。这个注解想必大家肯定不陌生,在spring项目中,我们经常用它来为我们的属性注入值,实现属性的自动装配。

    好了,话不多说,我们来看具体如何实现.

    实现过程

    定义注解

    这一块就很简单了,前面我们也不止一次写过,这里target指定的是属性

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Autowired {
    }
    

    加到属性上

    直接把@Autowired注解加在我们要注入的属性上

    优化扫描方法

    属性是在类初始化的时候,自动被赋值的,所以我们要调整初始化流程

    private static void initRequestMappingMap() {
            logger.info("start to scanRequestMapping, controllerSet = {}", classSet);
            if (classSet == null) {
                return;
            }
            classSet.forEach(aClass -> {
                Annotation controller = aClass.getAnnotation(Controller.class);
                if (Objects.isNull(controller)) {
                    return;
                }
                Method[] methods = aClass.getDeclaredMethods();
                for (Method method : methods) {
                    RequestMapping annotation = method.getAnnotation(RequestMapping.class);
                    if (Objects.nonNull(annotation)) {
                        requestMappingMap.put(annotation.value(), method);
                    }
                }
                Field[] fields = aClass.getDeclaredFields();
                try {
                    Object o = aClass.newInstance();
                    for (Field field : fields) {
                        Autowired annotation = field.getAnnotation(Autowired.class);
                        if (Objects.nonNull(annotation)) {
                            field.setAccessible(true);
                            field.set(o, contentMap.get(field.getType().getName()));
                        }
                    }
                    contentMap.put(aClass.getName(), o);
                } catch (InstantiationException e) {
                    logger.error("初始controller失败:", e);
                } catch (IllegalAccessException e) {
                    logger.error("初始controller失败:", e);
                }
            });
            logger.info("scanRequestMapping end, requestMappingMap = {}", requestMappingMap);
        }
    

    我们在初始化这里加了一段字段初始化的代码,上面是完整代码,字段初始化只有短短几行:

    Field[] fields = aClass.getDeclaredFields();
    try {
        Object o = aClass.newInstance();
        for (Field field : fields) {
            Autowired annotation = field.getAnnotation(Autowired.class);
            if (Objects.nonNull(annotation)) {
                field.setAccessible(true);
                field.set(o, contentMap.get(field.getType().getName()));
            }
        }
        contentMap.put(aClass.getName(), o);
    } catch (InstantiationException e) {
        logger.error("初始controller失败:", e);
    } catch (IllegalAccessException e) {
        logger.error("初始controller失败:", e);
    }
    

    这里需要注意的是,因为属性是私有的,必须通过getDeclaredFields获取属性值,getFields方法是没办法拿到私有属性的;

    另外一个需要注意的点是,私有属性必须通过setAccessible设置为可访问才可以,否则会报错:

    因为字段赋值是基于对象实例的,所以我们要先创建类的实例:

    Object o = aClass.newInstance()
    

    然后通过field.set给属性赋值,这里赋值要通过IOC容器拿到赋值对象的实例,所以被赋值属性的实例必须先初始化,否则会有问题。

    同时,我们把带有@Autowired注解的类的实例也存进了IOC容器,这样在后面调用controller对用mapping方法的时候,我们直接从ioc容器中拿出来即可:

    Object o = contentMap.get(declaringClass.getName());
    Object invoke = method.invoke(o, parameters);
    

    这是因为如果你在调用的时候再去创建实例,这时候属性也要赋值,否则会报错的,所以初始化的时候直接创建实例是比较合理的方式。

    测试

    浏览器调用下试下:

    可以看到,我们调用的时候,service已经有值了,方法调用完成后,结果正常返回:

    总结

    好了,今天的内容到这里就结束了。在上面的内容中,我们展示了@Autowired注解的定义、具体的应用,以及Ioc对于Autowired注解的处理过程,最后我们经过测试,结果与预期一致,当然具体springboot是如何实现的,还需要进一步的研究和探讨,我这里分享的是自己的实现思路,感兴趣的小伙伴可以自己动手试下。

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

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

  • 相关阅读:
    《Spring_Four》第二次作业 基于Jsoup的大学生考试信息展示系统开题报告
    《Spring_Four》第一次作业:团队亮相
    4.11jsp
    4.7jsp
    3.17jsp
    3.24jsp
    3.10jsp
    3.4软件测试
    回文串
    博客园第二次作业
  • 原文地址:https://www.cnblogs.com/caoleiCoding/p/14866531.html
Copyright © 2011-2022 走看看