手写模拟SpringMvc源码

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 手写模拟SpringMvc源码

MVC框架

MVC是一种设计模式(设计模式就是日常开发中编写代码的一种好的方法和经验的总结)。模型(model)-视图(view)-控制器(controller),三层架构的设计模式。用于实现前端页面的展现与后端业务数据处理的分离。


Spring MVC的主要组件

前端控制器 DispatcherServlet

Spring的MVC框架是围绕DispatcherServlet来设计的,它用来处理所有的HTTP请求和响应。此模块不需要程序员开发。

作用:接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。

处理器映射器HandlerMapping

此功能不需要程序员开发。

作用:根据请求的URL来查找Handler

处理器适配器HandlerAdapter

作用:进行视图的解析,根据视图逻辑名解析成真正的视图(view)

视图View

需要程序员开发jsp。View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)

fe910a073f8282fabb5b0cf274539cb5.png

用户发送请求至前端控制器DispatcherServlet;

DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;

处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;

DispatcherServlet 调用 HandlerAdapter处理器适配器;

HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器);

Handler执行完成返回ModelAndView;

HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;

DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;

ViewResolver解析后返回具体View;

DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)

DispatcherServlet响应用户。

手写模拟SpringMvc源码

spring mvc调用到Controller执行的原理

通过加载配置文件web.xml进而加载spring-mvc.xml。

根据配置文件给定的目录来扫描整个项目。

扫描所有加了@Controller注解的类。

当扫描到加了@Controller注解的类之后遍历里面所有的方法。

拿到方法对象之后 解析方法上面是否加了@RequestMapping注解。

定义一个Map集合把@RequstMapping的value 与方法对象绑定起来,即Map<String,Method>。

定义一个Map把声名该方法的类的对象绑定起来,即Map<String,Object>。

拦截到请求之后拿到请求的URI。

处理请求,例如执行指定方法,返回字符串或跳转到相应视图。

目录

2def37b06d8c4020252a13c04b352ce3.png

导入依赖

 <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
    </dependency>
    <!--       解析xml文件-->
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.9</version>
    </dependency>

代码

DispatcherServlet类

public class DispatcherServlet extends HttpServlet {
    private ApplicationContext applicationContext;
    private List<MyHandle> myHandleList=new ArrayList<>();
    @Override
    public void init() throws ServletException {
        String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
        System.out.println(contextConfigLocation);
        applicationContext = new ApplicationContext(contextConfigLocation);
        applicationContext.refresh();
        initHandleMappinng(applicationContext);
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        excuteDispatch(req,resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    public void initHandleMappinng(ApplicationContext applicationContext){
        if(applicationContext.beanDefinitionConcurrentHashMap.size()==0){
            throw new RuntimeException("Spring容器为空");
        }
        for (Map.Entry<String, BeanDefinition> stringBeanDefinitionEntry : applicationContext.beanDefinitionConcurrentHashMap.entrySet()) {
            Class clazz = stringBeanDefinitionEntry.getValue().getClazz();
            for (Method declaredMethod : clazz.getDeclaredMethods()) {
                boolean annotationPresent = declaredMethod.isAnnotationPresent(RequestMapping.class);
                if(annotationPresent==true){
                    String value = declaredMethod.getAnnotation(RequestMapping.class).value();
                    MyHandle myHandle=new MyHandle(value,declaredMethod,clazz);
                    myHandleList.add(myHandle);
                }
            }
        }
    }
    public void excuteDispatch(HttpServletRequest request,HttpServletResponse response){
        MyHandle handle = getHandle(request);
        if(handle==null){
            try {
                response.getWriter().print("404");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        else {
            Method method = handle.getMethod();
            Class<?>[] parameterTypes = method.getParameterTypes();
            Object[] params=new Object[parameterTypes.length];
            Map<String, String[]> parameterMap = request.getParameterMap();
            for (Map.Entry<String, String[]> stringEntry : parameterMap.entrySet()) {
                String key = stringEntry.getKey();
                String value = stringEntry.getValue()[0];
                int i = GetRequestParams(method, key);
                if(i>=0)
                    params[i]=value;
                else {
                    //反射获取的是arg0,官方这里用的不是反射机制
                }
            }
            try {
                Object invoke = method.invoke(handle.getClazz().newInstance(), params);
                PrintWriter writer = response.getWriter();
                writer.print(invoke);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public MyHandle getHandle(HttpServletRequest request){
        String requestURI = request.getRequestURI();
        for (MyHandle myHandle : myHandleList) {
            if(myHandle.getUrl().equals(requestURI))
                return myHandle;
        }
        return null;
    }
    public int GetRequestParams(Method method,String name){
        Parameter[] parameters = method.getParameters();
        for (int i=0;i<parameters.length;i++) {
            boolean annotationPresent = parameters[i].isAnnotationPresent(RequestParam.class);
            if(annotationPresent){
                String value = parameters[i].getAnnotation(RequestParam.class).value();
                if(value.equals(name))
                    return i;
            }
        }
        return -1;
    }
}

XmlPaser类

public class XmlPaser {
    public static String getbasePackage(String xml){
        SAXReader saxReader=new SAXReader();
        InputStream inputStream = XmlPaser.class.getClassLoader().getResourceAsStream(xml);
        try {
            Document document = saxReader.read(inputStream);
            Element rootElement = document.getRootElement();
            Element componentScan = rootElement.element("component-scan");
            Attribute attribute = componentScan.attribute("base-package");
            String text = attribute.getText();
            return text;
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return "";
    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <display-name>Application</display-name>
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>com.example.demo.springmvc.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

代码下载地址:https://download.csdn.net/download/qq_43649937/87558006

相关文章
|
XML 前端开发 Java
源码分析系列教程(05) - 手写SpringMVC
源码分析系列教程(05) - 手写SpringMVC
42 0
|
前端开发 Java 应用服务中间件
手写简单版SpringMVC
手写简单版SpringMVC
51 0
|
5月前
|
XML 前端开发 Java
深入理解SpringMVC工作原理,像大牛一样手写SpringMVC框架
对于SpringMVC相信诸位并不陌生,这是Java开发过程中使用最频繁的框架,在你的项目中可能不一定用MyBatis,但绝对会使用SpringMVC,因为操作数据库还有Hibernate、JPA等其他ORM框架选择,但SpringMVC这个框架在其领域中,可谓是独领风骚
|
7月前
|
XML Java 数据库连接
探秘MyBatis:手写Mapper代理的源码解析与实现
探秘MyBatis:手写Mapper代理的源码解析与实现
95 1
|
7月前
|
前端开发 Java 应用服务中间件
SpringMvc拦截器和手写模拟SpringMvc工作流程源码详解
MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分。 M: Model,模型层,指工程中的JavaBean,作用是处理数据。 JavaBean分为两类: 1.实体类Bean:专门存储业务数据的,如Student User等 2.业务处理Bean:指Service或Dao对象,专门用于处理业务逻辑和数据访问。
|
设计模式 Java Spring
用300行代码手写1个Spring框架,麻雀虽小五脏俱全
为了解析方便,我们用application.properties来代替application.xml文件,具体配置内容如下:
47 0
|
Java 对象存储 Spring
手写Spring基础源码(1)
手写Spring基础源码(1)
|
存储 设计模式 XML
【Spring系列】- 手写模拟Spring框架
上次已经学习了Java的设计模式,接下来就先来学习一下如何手写模拟简易的Spring,通过动手实践,才会更好的了解spring底层原理,今天就简单的模拟Spring容器是如何创建,bean又是如何注入的。
213 0
【Spring系列】- 手写模拟Spring框架
|
JSON Java fastjson
Springboot 最细节全面的接口传参接参介绍,总有你喜欢的一种方式
Springboot 最细节全面的接口传参接参介绍,总有你喜欢的一种方式
1068 0
Springboot 最细节全面的接口传参接参介绍,总有你喜欢的一种方式
手写springmvc框架版本2
手写springmvc框架版本2
手写springmvc框架版本2