帮助初级程序员学习spring原理,手写简单版spring框架


前言

本篇文章主要是帮助初级程序员去了解spring的基本原理。


一、手写简化版spring的基本思路

  1. 基本配置

    配置web.xml。(DispatcherServlet)
    	
    设定init-param。(配置contentConfigLocation="application.properties")
    
    设定url-pattern。(/*)
    
    配置Annotation。(@Controller @Service @Autowired @RequestMapping等)
    
  2. 初始化

    调用 init 方法。(加载配置文件)
    
    IoC容器初始化。(使用Map<String, Object>)
    
    扫描相关类。(scanPackage="com.itman.demo")
    
    创建对象并实例化保存至容器中。(通过反射机制将类实例化并保存至容器中)(IoC)
    
    依赖注入。(扫描IoC容器中的实例,给没有赋值的属性赋值)(DI)
    
    初始化HandlerMapping。(将Url和Method进行一对一关联映射保存到Map<String, Method>中)
    
  3. 运行

    调用doPost()/doGet()。(Web容器调用doPost/doGet方法,获取request/response对象)
    
    匹配HandlerMapping。(从request对象中获取请求的路径,找到其对应的Method)
    
    反射调用method.invoker()。(利用反射调用并返回结果)
    
    结果返回给浏览器。
    

二、代码结构

1.pom.xml

maven jar包依赖
maven插件依赖

2.基础业务代码结构

基础业务类

  1. DemoController.java

    在这里插入图片描述

  2. IDemoService.java

    在这里插入图片描述

  3. DemoServiceImpl.java

    在这里插入图片描述


3.框架原理代码实现

  1. 代码结构

在这里插入图片描述
2. web.xml

在这里插入图片描述

  1. 配置文件application.properties

    在这里插入图片描述

  2. 自定义的JmDispatcherServlet.java(核心代码)

package com.itman.mvc.framework.servlet;

import com.itman.mvc.framework.annotation.JmAutowired;
import com.itman.mvc.framework.annotation.JmController;
import com.itman.mvc.framework.annotation.JmRequestMapping;
import com.itman.mvc.framework.annotation.JmService;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

public class JmDispatcherServlet extends HttpServlet {

    private static final long serialVersionUID = 600112243475039630L;

    private Map<String, Object> ioc = new HashMap<>();

    // 配置文件对象
    private Properties properties = new Properties();

    // 所有类的全类名集合
    private List<String> classNames = new ArrayList<>();

    // 处理器映射
    private Map<String, Method> handlerMapping = new HashMap<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 6.调用
        try {
            doDispatch(req, resp);
        } catch (Exception e) {

            resp.getWriter().write("500 Exception, Message: " + Arrays.toString(e.getStackTrace()));
        }
    }

    /**
     * 方法调用
     * @param req 请求对象
     * @param resp 返回对象
     */
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {

        // 获取请求路径
        String url = req.getRequestURI();

        // 获取项目路径
        String contextPath = req.getContextPath();

        url = url.replaceAll(contextPath, "").replaceAll("/+", "/");

        if (!this.handlerMapping.containsKey(url)){

            resp.getWriter().write("404 Error, Request Method Is Not Exist, Request Url: " + url);
        }

        // 获取方法
        Method method = this.handlerMapping.get(url);

        // 获取请求参数
        Map<String, String[]> parameterMap = req.getParameterMap();

        // 获取方法所在的类名
        String beanName = formatJavaBeanName(method.getDeclaringClass().getSimpleName());

        Object[] objs = new Object[parameterMap.size() + 2];

        objs[0] = req;
        objs[1] = resp;

        int i = 2;

        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {

            objs[i] = entry.getValue()[0];
            i++;
        }

        method.invoke(ioc.get(beanName), objs);
    }

    @Override
    public void init(ServletConfig config) throws ServletException {

        // 1.加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));

        // 2.扫描相关类
        doScanner(properties.getProperty("scanPackage"));

        // 3.实例化扫描的类,并且缓存到IoC容器中
        doInstance();

        // 4.完成依赖注入(DI)
        doAutowired();

        // 5.初始化HandlerMapping
        doInitHandlerMapping();

        System.out.println("Jm Spring framework is init!");
    }

    /**
     * 加载配置文件
     * @param contextConfigLocation web.xml中配置的配置文件属性
     */
    private void doLoadConfig(String contextConfigLocation) {

        if (null == contextConfigLocation || "".equals(contextConfigLocation) || "".equals(contextConfigLocation.replaceAll(" ", ""))
                || contextConfigLocation.length() <= 0){

            throw new RuntimeException("Not Set Config Location!");
        }

        // 获取配置文件的文件流
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);

        if (null == is){

            throw new RuntimeException("Config File Is Not Found!");
        }

        // 读取配置
        try {

            properties.load(is);
        } catch (IOException e) {

            e.printStackTrace();
        } finally {

            try {

                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 扫描相关类
     * @param scanPackage 扫描的包路径
     */
    private void doScanner(String scanPackage) {

        // 获取包的文件夹路径
        String classUrl = scanPackage.replaceAll("\\.", "/");

        // 获取包URL
        URL url = this.getClass().getClassLoader().getResource(classUrl);

        if (null == url){

            throw new RuntimeException("Get Class Url Error!");
        }

        // 获取包文件数据
        File classFile = new File(url.getFile());

        // 获取包路径下的所有文件数据
        File[] files = classFile.listFiles();

        if (null == files){

            return;
        }

        for (File file : files){

            if (file.isDirectory()){    // 如果是文件夹,继续获取文件夹下类的全类名

                doScanner(scanPackage + "." + file.getName());
            } else {

                if (!file.getName().endsWith(".class")) {   // 只扫描java类

                    continue;
                }

                // 获取包文件下类的全类名
                String className = scanPackage + "." + file.getName().replace(".class", "");

                // 保存类的全类名
                classNames.add(className);
            }

        }
    }

    /**
     * 类实例化(IoC)
     */
    private void doInstance() {

        if (classNames.isEmpty()){

            return;
        }

        try {

            for (String className : classNames){

                Class<?> clazz = Class.forName(className);

                if (clazz.isAnnotationPresent(JmController.class)){ // 实例化controller

                    // 获取类名
                    String beanName = formatJavaBeanName(clazz.getSimpleName());

                    // 实例化类
                    Object instance = clazz.newInstance();

                    ioc.put(beanName, instance);
                } else if (clazz.isAnnotationPresent(JmService.class)){ // 实例化service

                    // 获取类名
                    String beanName = formatJavaBeanName(clazz.getSimpleName());

                    // 获取自定义的类名
                    JmService jmService = clazz.getAnnotation(JmService.class);
                    if (!"".equals(jmService.value())){
                        beanName = jmService.value();
                    }

                    // 实例化类
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);

                    // 根据接口实例化
                    for (Class<?> c : clazz.getInterfaces()){

                        if (ioc.containsKey(c.getName())){

                            throw new RuntimeException(c.getName() + "Is Exist!");
                        }

                        ioc.put(c.getName(), instance);
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 依赖注入(DI)
     */
    private void doAutowired() {

        if (ioc.isEmpty()){

            return;
        }

        for (Map.Entry<String, Object> entry : ioc.entrySet()){

            // 获取类中所有字段
            Field[] fields = entry.getValue().getClass().getDeclaredFields();

            for (Field field : fields){

                if (!field.isAnnotationPresent(JmAutowired.class)){

                    continue;
                }

                JmAutowired jmAutowired = field.getAnnotation(JmAutowired.class);
                String beanName = jmAutowired.value().trim();
                if ("".equals(beanName)){
                    beanName = field.getType().getName();
                }

                // 设置访问权限,暴力访问
                field.setAccessible(true);

                try {
                    field.set(entry.getValue(), ioc.get(beanName));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 初始化映射
     */
    private void doInitHandlerMapping() {

        if (ioc.isEmpty()){

            return;
        }

        for (Map.Entry<String, Object> entry : ioc.entrySet()){

            Class<?> clazz = entry.getValue().getClass();

            if (!clazz.isAnnotationPresent(JmController.class)) {

                continue;
            }

            String baseUrl = "";
            if (clazz.isAnnotationPresent(JmRequestMapping.class)) {

                JmRequestMapping jmRequestMapping = clazz.getAnnotation(JmRequestMapping.class);

                baseUrl = jmRequestMapping.value();
            }

            // 针对所有的 public 方法
            for (Method method : clazz.getMethods()) {

                if (!method.isAnnotationPresent(JmRequestMapping.class)) {

                    continue;
                }

                JmRequestMapping jmRequestMapping = method.getAnnotation(JmRequestMapping.class);
                String url = ("/" + baseUrl + "/" + jmRequestMapping.value()).replaceAll("/+", "/");

                handlerMapping.put(url, method);

                System.out.println("Handler Mapping url: " + url + ", method: " + method);
            }
        }
    }

    /**
     * 格式化 java 类名, 第一个字母小写
     * @param simpleName 类名
     * @return 格式化后的类名
     */
    private String formatJavaBeanName(String simpleName) {

        char[] charArray = simpleName.toCharArray();

        charArray[0] += 32;

        return String.valueOf(charArray);
    }
}

  1. 相关注解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
6. 运行结果

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210627155521274.png)

3.面试(spring容器中的bean是不是线程安全的)

通过上面的简单版spring框架,可以了解到bean是怎么得到的。
  • Spring中的Bean是从哪里得到的。(IoC容器中得到的)
  • IoC容器中的Bean是从哪里得到的。(通过反射机制得到的)
  • 反射机制的Bean是从哪里得到的。(通过读取配置文件得到的)
  • 配置文件中的Bean是从哪里得到的。(是程序员开发的)

因此,总结得到Spring容器中的Bean与Spring容器无关,Spring容器只是负责管理,所以spring容器中的bean不一定是线程安全的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值