手把手写Spring框架

 更新时间:2021年08月23日 14:49:18   作者:Mr.Bean-Pig  
Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架

这部分目标是MVC!

主要完成3个重要组件:

HandlerMapping:保存URL映射关系

HandlerAdapter:动态参数适配器

ViewResolvers:视图转换器,模板引擎

SpringMVC核心组件执行流程:

在这里插入图片描述

在这里插入图片描述

相对应的,用以下几个类来实现上述的功能:

在这里插入图片描述

初始化阶段

在DispatcherServlet这个类的init方法中,将mvc部分替换为initStrategies(context):

在这里插入图片描述

并且调用初始化三个组件的方法。

分别完善这几个方法:

private void initViewResolvers(PigApplicationContext context) {
        //模板引擎的根路径
        String tempateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(tempateRoot).getFile();
        File templateRootDir = new File(templateRootPath);
        for (File file : templateRootDir.listFiles()) {
            this.viewResolvers.add(new PIGViewResolver(tempateRoot));
        }
    }
    private void initHandlerAdapters(PigApplicationContext context) {
        for (PIGHandlerMapping handlerMapping : handlerMappings) {
            this.handlerAdapters.put(handlerMapping,new PIGHandlerAdapter());
        }
    }
    private void initHandlerMappings(PigApplicationContext context) {
        if(context.getBeanDefinitionCount() == 0){ return; }
        String [] beanNames = context.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            Object instance = context.getBean(beanName);
            Class<?> clazz = instance.getClass();
            if(!clazz.isAnnotationPresent(PIGController.class)){ continue; }
            String baseUrl = "";
            if(clazz.isAnnotationPresent(PIGRequestMapping.class)){
                PIGRequestMapping requestMapping = clazz.getAnnotation(PIGRequestMapping.class);
                baseUrl = requestMapping.value();
            }
            //默认只获取public方法
            for (Method method : clazz.getMethods()) {
                if(!method.isAnnotationPresent(PIGRequestMapping.class)){continue;}
                PIGRequestMapping requestMapping = method.getAnnotation(PIGRequestMapping.class);
                //   //demo//query
                String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*",".*")).replaceAll("/+","/");
                Pattern pattern = Pattern.compile(regex);
                handlerMappings.add(new PIGHandlerMapping(pattern,instance,method));
                System.out.println("Mapped " + regex + "," + method);
            }
        }
    }

全局变量:

private List<PIGHandlerMapping> handlerMappings = new ArrayList<PIGHandlerMapping>();

private Map<PIGHandlerMapping,PIGHandlerAdapter> handlerAdapters = new HashMap<PIGHandlerMapping, PIGHandlerAdapter>();

private List<PIGViewResolver> viewResolvers = new ArrayList<PIGViewResolver>();

HandlerMapping与HandlerAdapter是一一对应的关系。

运行阶段

整体思路:

在这里插入图片描述

getHandler就是通过请求拿到URI,并与handlerMappings中存好的模板进行匹配:

    private PIGHandlerMapping getHandler(HttpServletRequest req) {
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath,"").replaceAll("/+","/");
        for (PIGHandlerMapping handlerMapping : this.handlerMappings) {
            Matcher matcher = handlerMapping.getPattern().matcher(url);
            if(!matcher.matches()){continue;}
            return handlerMapping;
        }
        return null;
    }

HandlerAdapter

HandlerAdapter的handle方法负责反射调用具体方法。需要匹配参数,那么需要先保存好实参和形参列表。

形参列表:编译后就能拿到值

Map<String,Integer> paramIndexMapping = new HashMap<String, Integer>();
        //提取加了PIGRequestParam注解的参数的位置
        Annotation[][] pa = handler.getMethod().getParameterAnnotations();
        for (int i = 0; i < pa.length; i ++){
            for (Annotation a : pa[i]) {
                if(a instanceof PIGRequestParam){
                    String paramName = ((PIGRequestParam) a).value();
                    if(!"".equals(paramName.trim())){
                        paramIndexMapping.put(paramName,i);
                    }
                }
            }
        }
        //提取request和response的位置
        Class<?> [] paramTypes = handler.getMethod().getParameterTypes();
        for (int i = 0; i < paramTypes.length; i++) {
            Class<?> type = paramTypes[i];
            if(type == HttpServletRequest.class || type == HttpServletResponse.class){
                paramIndexMapping.put(type.getName(),i);
            }
        }

实参列表:要运行时才能拿到值

 Map<String,String[]> paramsMap = req.getParameterMap();
        //声明实参列表
        Object [] parameValues = new Object[paramTypes.length];
        for (Map.Entry<String,String[]> param : paramsMap.entrySet()) {
            String value = Arrays.toString(paramsMap.get(param.getKey()))
                    .replaceAll("\\[|\\]","")
                    .replaceAll("\\s","");
            if(!paramIndexMapping.containsKey(param.getKey())){continue;}
            int index = paramIndexMapping.get(param.getKey());
            parameValues[index] = caseStringVlaue(value,paramTypes[index]);
        }
        if(paramIndexMapping.containsKey(HttpServletRequest.class.getName())){
            int index = paramIndexMapping.get(HttpServletRequest.class.getName());
            parameValues[index] = req;
        }
        if(paramIndexMapping.containsKey(HttpServletResponse.class.getName())){
            int index = paramIndexMapping.get(HttpServletResponse.class.getName());
            parameValues[index] = resp;
        }

最后反射

在这里插入图片描述

总结:

在这里插入图片描述

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • Spring中ApplicationEventPublisher发布订阅模式的实现

    Spring中ApplicationEventPublisher发布订阅模式的实现

    本文主要介绍了Spring中ApplicationEventPublisher发布订阅模式的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Java实现HDFS文件上传下载

    Java实现HDFS文件上传下载

    这篇文章主要为大家详细介绍了Java实现HDFS文件上传下载,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • SparkStreaming整合Kafka过程详解

    SparkStreaming整合Kafka过程详解

    这篇文章主要介绍了SparkStreaming整合Kafka过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-01-01
  • Java 如何安全的发布对象

    Java 如何安全的发布对象

    这篇文章主要介绍了Java 如何安全的发布对象,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2021-01-01
  • 浅谈JVM系列之JIT中的Virtual Call

    浅谈JVM系列之JIT中的Virtual Call

    什么是Virtual Call?Virtual Call在java中的实现是怎么样的?Virtual Call在JIT中有没有优化?所有的答案看完这篇文章就明白了。
    2021-06-06
  • Java中分割字符串的两种方法实例详解

    Java中分割字符串的两种方法实例详解

    这篇文章主要介绍了Java中分割字符串的两种方法,一种是java.lang.String 的 split() 方法,,另外一种是用String Tokenizer类。文中的每种方法都给出了详细的示例代码,相信对大家的理解和学习具有一定的参考借鉴价值,有需要的朋友们下面来一起看看吧。
    2016-12-12
  • maven项目中<scope>provided</scope>的作用及说明

    maven项目中<scope>provided</scope>的作用及说明

    这篇文章主要介绍了maven项目中<scope>provided</scope>的作用及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Java中的内部类你了解吗

    Java中的内部类你了解吗

    这篇文章主要为大家介绍了Java的内部类,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • Spring整合MyBatis图示过程解析

    Spring整合MyBatis图示过程解析

    这篇文章主要介绍了Spring整合MyBatis图示过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • Java中支持可变参数详解

    Java中支持可变参数详解

    那个可变参数的就是个数组,你传多少个参数都被放到那个数组里面。这样方便了程序员,因为如果不确定要传的参数的个数的话,我们要写带1个参数的,带2个参数,带3个参数的,这样很麻烦。 该进后的这个方法,我们只要写一个函数就好,可以传任意个参数。
    2015-05-05

最新评论