手写Spring IOC

首先我们聊聊小编目前对spring核心功能的理解。
Spring启动时最核心的功能我认为是扫描所有Bean,并将Bean注入到Bean容器中也就是我们所说的Ioc容器之一,实现依赖倒置,并且在进行类扫描时将标注有AutoWire注解的成员变量注入实例,然后再将Ioc容器中标注有Controller注解的类注入到另外一个容器中,以供外部访问某个接口时可以查找到接口的实例方法(实际处理方法)。注入到容器的过程中包含很多后置处理器对Bean进行处理。

  • 我们首先应该知道Spring在启动时要获取它自己的配置文件,那么这个配置文件的路径应该在哪获取呢?路径就配置在web.xml里(笔者找了半天源码才发现的)。

在这里插入图片描述

  • 以下是笔者自己配置的一个bean扫描路径 在这里插入图片描述
  • 接下来我们看具体实现。在扫描之前 我们想一想,Spring实现了请求的路由 处理 返回等功能,那我们肯定是要接口前端请求的。所以我们的实现类里首先要集成HttpServlet。去实现它的 doGet 和 doPost方法。并且在init方法里去实现我们上面说的 依赖注入等逻辑。

在这里插入图片描述
在这里插入图片描述

  • 我们在上面那个init方法里实现了刚刚描述的具体功能。
接下来我们主要一起看一下 ioc容器 依赖注入和HandlerMapping.
  • 首先是ioc容器。
    classNames是所有被扫描出来的类。
    我们把所有符合要求的类放入到IOC容器中。
    我们所谓的ioc容器其实就是一个Map。
   private void doInstance() {
        if (classNames.isEmpty()) {
            return;
        }
        try {
            for (String className : classNames) {
                Class <?> clazz = Class.forName(className);
                Annotation[] annotations = clazz.getAnnotations();
                for (Annotation annotation : annotations) {
                    System.out.println(annotation.toString());
                }
                if (clazz.isAnnotationPresent(KXYController.class)) {
                    //默认别名
                    String salis = toLowerFirstCase(clazz.getSimpleName());
                    Object instance = clazz.newInstance();
                    ioc.put(salis, instance);
                } else if (clazz.isAnnotationPresent(KXYService.class)) {
                    //默认别名
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    Object instance = clazz.newInstance();

                    //自定义命名
                    if (!"".equals(clazz.getAnnotation(KXYService.class).value())) {
                        beanName = clazz.getAnnotation(KXYService.class).value();
                    }

                    ioc.put(beanName, instance);

                    //以类型命名
                    for (Class <?> i : clazz.getInterfaces()) {
                        if (ioc.containsKey(i.getName())) {
                            throw new Exception("");
                        }
                        ioc.put(i.getName(), instance);
                    }
                } else {
                    continue;
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }**
  • 接下来我们对IOC容器中的类进行处理,这里主要处理带有AutoWire注解的Field。将其注入具体实现的方法
   private void doAutoWired() {
        if (ioc.isEmpty()) {
            return;
        }
        ioc.forEach((k, v) -> {
            Field[] declaredFields = v.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                if (!field.isAnnotationPresent(KXYAutowire.class)) {
                    continue;
                }

                KXYAutowire annotation = field.getAnnotation(KXYAutowire.class);
                String beanName = annotation.value().trim();
                if ("".equals(beanName)) {
                    beanName = field.getType().getName();
                }
                try {
                    field.setAccessible(true);
                    field.set(v, ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });
    }
  • 最后我们再入住HandlerMapping
   private void initHandlerMaping() {
        ioc.forEach((String k, Object v) -> {
            Class <?> aClass = v.getClass();

            if(aClass.isAnnotationPresent(KXYController.class)){
                Method[] methods = aClass.getDeclaredMethods();
                KXYRequestMapping mapping = aClass.getAnnotation(KXYRequestMapping.class);
                String urll = "";
                if(mapping != null){
                    urll = mapping.value() == null ? "":mapping.value();
                }
                for (Method method : methods) {

                    StringBuffer url = new StringBuffer();
                    if(urll.length() > 0){
                        url.append("/"+urll);
                    }
                    if(!method.isAnnotationPresent(KXYRequestMapping.class)){continue;}
                    url.append(method.getAnnotation(KXYRequestMapping.class).value());
                    method.setAccessible(true);
                    handleMapping.put(url.toString().replaceAll("//+","/"), method);
                    System.out.println("11111111" +url.toString()+","+method);
                }
            }
        });
    }

至此我们的初始化已完成。接下来我们看当一个访问请求来的时候,我们应该做什么?

  • 当一个请求过来的时候时候首先它应该进Servlet的doGet或doPost方法。然后我们再调用doDispath()方法进行处理。具体实现方法如下。
private void doDispath(HttpServletRequest req, HttpServletResponse resp) throws InvocationTargetException, IllegalAccessException {
        String servletPath = req.getRequestURI().replaceAll("//+","/");
        String projectName = req.getServletContext().getServletContextName();
        servletPath.replace(projectName+"//","");
        Method method = (Method) handleMapping.get("/qq/as");
        String classNameSalis = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        method.invoke(ioc.get(classNameSalis),req,resp);
    }

这样我们就实现了一个简单的Spring的基础功能。
源码地址:https://github.com/magopudding/springsimp
如有错误欢迎同学们批评指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

云下牧羊人

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

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

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

打赏作者

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

抵扣说明:

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

余额充值