手写springmvc 框架1.0版

Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的Spring MVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts 2(一般老项目使用)等。 

springmvc工作流程图

搭建项目

pom 依赖

项目启动配置jetty插件

自定义注解

核心类 GpDispatcherServlet 

package com.gupao.servlet.mvcframework;

import com.gupao.servlet.mvcframework.annotation.*;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

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.annotation.Annotation;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

import java.net.URL;

import java.util.*;

public class GpDispatcherServlet extends HttpServlet {

private static final Loggerlogger = LoggerFactory.getLogger(GpDispatcherServlet.class);

    //保存配置文件扫描到的内容

    private Properties contextConfig = new Properties();

    //保存扫描到的类

    private List classNameList = new ArrayList();

    //ioc容器

    private Map ioc = new HashMap();

    //保存url对应的mapping

//    private Map handMapping = new HashMap();

// 为啥不用map handmapping 本身的功能url methond对应 单一职责原则

    private List<handMapping> = new ArrayList();

    @Override

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws              ServletException,     IOException {

    doPost(req, resp);

    }

     @Override

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

//调用 运行阶段

        try {

          doDispatch(req, resp);

        } catch (Exception e) {

            e.printStackTrace();

            resp.getWriter().write("500 exception " + Arrays.toString(e.getStackTrace()));

        }

}

private void doDispatch(HttpServletRequest req, HttpServletResponse response)throws Exception{

        HandMapping handMapping = getHandler(req);

        if (handMapping ==null) {

        response.getWriter().write("404 Not Found");

         return;

        }

//获得形参列表

        Class [] paramTypes = handMapping.getParamTyps();

        Object [] paramValues = new Object[paramTypes.length];

        Map stringMap = req.getParameterMap();

        for (Map.Entry param : stringMap.entrySet()) {

            String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "")

            .replaceAll("\\s", ",");

            if (!handMapping.paramIndexMapping.containsKey(param.getKey())) {continue;}

                int index = handMapping.paramIndexMapping.get(param.getKey());

               paramValues[index] = convert(paramTypes[index], value);

        }

        //请求参数是否有httpservletRequest httpservletResponse

        if (handMapping.paramIndexMapping.containsKey(HttpServletRequest.class.getName())) {

            int reqIndex = handMapping.paramIndexMapping.get(HttpServletRequest.class.getName());

            paramValues[reqIndex] = req;

        }

if (handMapping.paramIndexMapping.containsKey(HttpServletResponse.class.getName())) {

            int respIndex = handMapping.paramIndexMapping.get(HttpServletResponse.class.getName());

            paramValues[respIndex] = response;

        }

        Object object = handMapping.method.invoke(handMapping.controller, paramValues);

        if (object ==null) {return;}

            response.getWriter().write(object.toString());

    }

private HandMapping  getHandler (HttpServletRequest req) {

    if (handMapping.isEmpty()) {return null;}

        String url = req.getRequestURI().replaceAll("/gupaospring", "");//绝对路径这里暂时写死

        //处理成相对路径

        String contextPath = req.getContextPath();

        logger.info("contextPath : [{}]", contextPath);

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

        for (HandMapping handMapping : this.handMapping) {

                if (handMapping.getUrl().equals(url)) {

                    return handMapping;

          }

}

            return null;

    }

    //http基于string传输

    private Object convert(Class type, String value) {

        if (Integer.class == type) {

            return Integer.valueOf(value);

        } else if (Double.class  == type) {

            return Double.valueOf(value);

        }

            return value;

    }

    //配置阶段 初始化阶段

    @Override

    public void init(ServletConfig config)throws ServletException {

            //加载配置文件 模板模式

        logger.info("access init method.......");

        doLoadConfig(config.getInitParameter("contextConfigLocation"));

        //扫描相关类

        doScanner(contextConfig.getProperty("scanPackage"));

        //初始化相关类放到Ioc容器中

        doInstance();

        //完成依赖注入

        doAutowired();

        //初始化handmapping

        initHandMapping();

        logger.info("gp srpingframework complete");

    }

private void initHandMapping() {

if (ioc.isEmpty()) {return;}

for (Map.Entry entry :ioc.entrySet()) {

            Class clazz = entry.getValue().getClass();

          if (!clazz.isAnnotationPresent(GpController.class)) {continue;}

                String baseUrl ="";

          if (clazz.isAnnotationPresent(GpRequestMapping.class)) {

                GpRequestMapping requestMapping = clazz.getAnnotation(GpRequestMapping.class);

                baseUrl = requestMapping.value().trim();//类上的url;

          }

    //默认获取所有的public方法

     for (Method method : clazz.getMethods()) {

        if (!method.isAnnotationPresent(GpRequestMapping.class)) {continue;}

                GpRequestMapping requestMapping = method.getAnnotation(GpRequestMapping.class);

                String url = (baseUrl +"/" + requestMapping.value().trim()).replaceAll("/+", "/");

//                handMapping.put(url, method);

                this.handMapping.add(new HandMapping(url, method, entry.getValue()));

                System.out.println("url:" + url +"method:" + method +"controller:" + entry.getValue());

            }

}

}

//自动注入

    private void doAutowired() {

        if (ioc.isEmpty()) {return;}

        for (Map.Entry entry :ioc.entrySet()) {

            //declared 所有的特定的 字段 包括private protected 等字段

            Field [] fields = entry.getValue().getClass().getDeclaredFields();

            for (Field field : fields) {

                if (!field.isAnnotationPresent(GpAutowried.class)) {continue;}

                GpAutowried autowried = field.getAnnotation(GpAutowried.class);

                String beanName = autowried.value().trim();

                if ("".equals(beanName)) {

                    //接口类型 作为key 到ioc容器中取值

                    beanName = field.getType().getName();

                }

                //如果是public 意外的修饰符 强制赋值

                field.setAccessible(true);

                //反射动态给字段赋值

                try {

                    field.set(entry.getValue(), ioc.get(beanName));

                } catch (IllegalAccessException e) {

                        e.printStackTrace();

                }

        }

    }

}

    //注入到ioc容器中去

    private void doInstance() {

        if (classNameList.isEmpty()) {return;}

        try {

                for (String className :classNameList) {

                        Class clazz =  Class.forName(className);

                        if (clazz.isAnnotationPresent(GpController.class)) {

                            Object instance = clazz.newInstance();

                            String beanName = toLowerFirstCase(clazz.getSimpleName());

                                ioc.put(beanName, instance);

                } else if (clazz.isAnnotationPresent(GpService.class)) {

                    GpService service = clazz.getAnnotation(GpService.class);

                    //beanName 为空

                    String beanName = service.value();

                    Object instance = clazz.newInstance();

                    if ("".equals(service.value())) {

                        beanName = toLowerFirstCase(clazz.getSimpleName());

                    }

                        ioc.put(beanName, instance);

                    //接口名称

                    for (Class i : clazz.getInterfaces()) {

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

                                throw new Exception("the" + i.getName() +"is exits");

                            }

                                ioc.put(i.getName(), instance);

                    }

        } else {

                    continue;

                }

    }

} catch (Exception e) {

        e.printStackTrace();

       }

}

    private String toLowerFirstCase(String simpleName) {

            char [] chars = simpleName.toCharArray();

        //加32是因为大小写字母的ascii相差32 大写字母的asci小于小写字母的ascII

            chars[0] +=32;

        return String.valueOf(chars);

    }

private void doScanner(String scanPackage) {

            //scanPackage=com.gupao.servlet包路径 变成文件路径

        //classpath

        URL url =  this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));

        File classpath =new File(url.getFile());

        for (File file : classpath.listFiles()) {

            if (file.isDirectory()) {

                    doScanner(scanPackage +"." + file.getName());

            } else {

                    if (!file.getName().endsWith(".class")){continue;}

                    String className = (scanPackage +"." + file.getName().replace(".class", ""));

                    classNameList.add(className);

            }

    }

}

private void doLoadConfig(String contextConfigLocation) {

        //直接从类路径下 spring主配置文件的路径 读取放到properties中

        //保存到内存中scanPackage=com.gupao.servlet

        logger.info("acccess doLoadConfig method .......");

        InputStream inputStream =null;

        inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);

        try {

                contextConfig.load(inputStream);

        } catch (IOException e) {

            e.printStackTrace();

        } finally {

        if (inputStream !=null) {

            try {

                    inputStream.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

        }

    }

}

//保存一个url和method对应关系handMapping策略模式

//解决多个参数问题不然参数容易错位

    public class HandMapping {

        private String url;

        private Method method;

        private Object controller;

        private Class [] paramTyps;

        //形参列表

        private Mapparam IndexMapping;

        public StringgetUrl() {

            return url;

        }

        public void setUrl(String url) {

            this.url = url;

        }

        public MethodgetMethod() {

            return method;

        }

        public void setMethod(Method method) {

            this.method = method;

        }

        public ObjectgetController() {

                return controller;

        }

    public void setController(Object controller) {

                this.controller = controller;

        }

     public Class[]getParamTyps() {

            return paramTyps;

        }

    public void setParamTyps(Class[] paramTyps) {

            this.paramTyps = paramTyps;

        }

        public HandMapping(String url, Method method, Object controller) {

                this.url = url;

                this.method = method;

                this.controller = controller;

                paramTyps = method.getParameterTypes();

                paramIndexMapping =new HashMap();

                putParamIndexMapping(method);

        }

    private void putParamIndexMapping(Method method) {

            Annotation[][] pa = method.getParameterAnnotations();

            for (int i =0; i < pa.length; i++) {

                        for (Annotation a : pa[i]) {

                            if (ainstanceof GpRequestParam) {//拿到参数注解的值

                                    String paramName = ((GpRequestParam) a).value();

                                    if (!"".equals(paramName.trim())) {//一个key对应一个数组 二维数组

                                        paramIndexMapping.put(paramName, i);

                                    }

                        }

                }

        }

            Class[] paramType =  method.getParameterTypes();

                for (int i =0; i < paramType.length; i++) {

                        Class type = paramType[i];

                        if (type == HttpServletRequest.class || type == HttpServletResponse.class) {

                            paramIndexMapping.put(type.getName(), i);

                    }

            }

    }

    }

}

看DemoAction方法

package com.gupao.servlet.action;

import com.gupao.servlet.mvcframework.annotation.GpAutowried;

import com.gupao.servlet.mvcframework.annotation.GpController;

import com.gupao.servlet.mvcframework.annotation.GpRequestMapping;

import com.gupao.servlet.mvcframework.annotation.GpRequestParam;

import com.gupao.servlet.service.IDemoService;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

@GpController

@GpRequestMapping(value ="/demo")

public class DemoAction {

private static final Loggerlogger = LoggerFactory.getLogger(DemoAction.class);

    @GpAutowried

    private IDemoServicedemoService;

    @GpRequestMapping(value ="/query")

    public void query(HttpServletRequest request, HttpServletResponse response,

                      @GpRequestParam("name") String name) {

                        logger.info("access query query");

        try {

                response.getWriter().write(name);

        } catch (IOException e) {

                e.printStackTrace();

        }

}

@GpRequestMapping(value ="/add")

public void add(HttpServletRequest request, HttpServletResponse response,

                        @GpRequestParam("a") Integer a,

                        @GpRequestParam("b") Integer b) {

                logger.info("access add add");

        try {

                    response.getWriter().write("a + b = " +  (a + b));

        } catch (IOException e) {

                    e.printStackTrace();

        }

}

@GpRequestMapping(value ="/sub")

public void sub(HttpServletRequest request, HttpServletResponse response,

                    @GpRequestParam("a") Double a,

                    @GpRequestParam("b")  Double b) {

            logger.info("access add sub");

        try {

                response.getWriter().write("a - b = " +  (a - b));

         } catch (IOException e) {

                e.printStackTrace();

        }

}

@GpRequestMapping(value ="/remove")

public String remove(@GpRequestParam("id") Integer id) {

        logger.info("access add remove");

        return "id = " + id;

    }

}

测试:query 方法 存在当前参数

不存在当前参数 报异常

add 方法

sub方法

remove方法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354