springMVC学习心得及手写springMVC简单实现

springMVC学习心得及手写springMVC简单实现

Spring 是一个企业级开发框架,为解决企业级项目开发过于复杂而创建的,框架的主要优势之一就是分层架构,允许开发者自主选择组件。

Spring 的两大核心机制是 IoC(控制反转)和 AOP(面向切面编程),从开发的角度讲,我们使用 Spring 框架就是用它的 IoC 和 AOP。

什么是AOP和IOC

IoC 是典型的工厂模式,通过工厂去注入对象
AOP 是代理模式的体现
  • IOC也叫控制反转 ,控制反转是什么意思呢?反转的的谁? 在传统开发中一半是有调用者创建对象实例,也就是是调用者主动new出来的,那么IOC就是将这个创建对象实例的任务从调用者的手里反转到spring的IOC容器来执行,控制反转主要是反转的这个过程;
    举个例子:
    传统模式:以前去买菜需要自己带篮子,去超市买完自己带回家;
    IOC模式:空手去超市就可以了,篮子超市已经给你准备好了,直接拿来用就好了.
  • 依赖注入(Dependency Injection,DI) 能实现在程序运行中,动态地向某个对象提供它所依赖的其它对象的功能。

SpringMVC的常用组件及功能

  • DispatcherServlet: 前端控制器,是整个流程控制的核心,控制其他组件的执行,统一调度,降低组件之间的耦合性,相当于总指挥。
  • Handler:处理器,完成具体业务逻辑,相当于 Servlet 或 Action。
  • HandlerMapping: DispatcherServlet 接收到请求之后,通过 HandlerMapping 将不同的请求分发到不同的 Handler。
  • HandlerInterceptor:处理器拦截器,是一个接口,如果我们需要做一些拦截处理,可以来实现这个接口。
  • HandlerExecutionChain:处理器执行链,包括两部分内容,Handler 和 HandlerInterceptor(系统会有一个默认的 HandlerInterceptor,如果需要额外拦截处理,可以添加拦截器设置)。
  • HandlerAdapter:处理器适配器,Handler 执行业务方法之前,需要进行一系列的操作,包括表单数据的验证、数据类型的转换、将表单数据封装到 JavaBean 等,这一系列的操作,都是由 HandlerAdapter 来完成,- DispatcherServlet 通过 HandlerAdapter 执行不同的 Handler。
  • ModelAndView:装载了模型数据和视图信息,作为 Handler 的处理结果,返回给 DispatcherServlet。
  • ViewResolver:视图解析器,DispatcherServlet 通过它将逻辑视图解析成物理视图,最终将渲染结果响应给客户端。

Spring MVC 的实现流程

  • 客户端请求被 DispatcherServlet(前端控制器)接收

  • 根据 HandlerMapping 映射到 Handler
    生成 Handler 和 HandlerInterceptor(如果有则生成)

  • Handler 和 HandlerInterceptor 以 HandlerExecutionChain 的形式一并返回给 DispatcherServlet

  • DispatcherServlet 通过 HandlerAdapter 调用 Handler 的方法做业务逻辑处理
    返回一个 ModelAndView 对象给 DispatcherServlet

  • DispatcherServlet 将获取的 ModelAndView 对象传给 ViewResolver 视图解析器,将逻辑视图解析成物理视图 View

  • ViewResolver 返回一个 View 给 DispatcherServlet

  • DispatcherServlet 根据 View 进行视图渲染(将模型数据填充到视图中)

  • DispatcherServlet 将渲染后的视图响应给客户端

  • 在这里插入图片描述
    看到上面的实现原理,可能会有这样的担心,Spring MVC 如此众多的组件开发起来一定很麻烦吧?答案是否定的,Spring MVC 使用起来非常简单,很多组件都由框架提供,作为开发者我们直接使用即可,并不需要自己手动编写代码,真正需要开发者进行编写的组件只有两个:

  • Handler,处理业务逻辑

  • View,JSP 做展示

Spring MVC 底层实现

一个 Spring MVC 框架,相比于 MyBatis 框架,Spring MVC 要简单一些,只需要 XML 解析 + 反射就可以完成,不需要 JDK 动态代理。 强调文本

下面,来手写一下springMVC的简单实现,废话不多说,直接上干货

要自己写框架,必须理解框架的底层原理和运行机制,这部分在上一部分已经讲过

1.MyDispatcherServlet

    首先需要一个前置控制器 DispatcherServlet,作为整个流程的核心,由它去调用其他组
件,共同完成业务。主要组件有两个:一是 Controller,调用其业务方法 Method,执行业务逻辑;
二是 ViewResolver 视图解析器,将业务方法的返回值解析为物理
视图 + 模型数据,返回客户端。
我们自己写框架就按照这个思路来。

初始化工作

  • 根据 Spring IoC 的思路,需要将参与业务的对象全部创建并保存,供流程调用。因此,首先需要创建 Controller 对象,HTTP 请求是通过注解找到对应的 Controller 对象,我们需要将所有的 Controller 与其注解建立关联,很显然,使用 key-value 结构的 Map 集合来保存最合适不过了,这样就模拟了 IoC 容器。

  • Controller 的 Method 也是通过注解与 HTTP 请求映射的。同样地,我们需要将所有的 Method 与其注解建立关联,HTTP 直接通过注解的值找到对应的 Method,这里也用 Map 集合保存。

  • 实例化视图解析器

初始化工作完成,接下来处理 HTTP 请求,业务流程如下:

  • DispatcherServlet 接收请求,通过映射从 IoC 容器中获取对应的 Controller 对象;
  • 根据映射获取 Controller 对象对应的 Method;
  • 调用 Method,获取返回值;
  • 将返回值传给视图解析器,返回物理视图;
  • 完成页面跳转。

创建类

思路捋清楚了,接下来开始写代码,我们需要创建以下类。

  • MyDispatcherServlet:模拟 DispatcherServlet
  • MyController:模拟 Controller 注解
  • MyRequestMapping:模拟 RequestMapping 注解
  • MyViewResolver:模拟 ViewResolver 视图解析器
  • 创建 MyDispatcherServlet,init 方法完成初始化。
    把 Controller 与注解进行关联
    将 Controller 与注解进行关联,保存到 iocContainer 中。哪些 Controller 是需要添加到 iocContainer 中的呢?必须同时满足两点:
    • springmvc.xml 中配置扫描的类
    • 类定义处添加了注解
      注意这两点必须同时满足。

(1)创建 MyController 注解,作用目标为类:

package morin.springmvc.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * controller注解 作用目标为类
 * 作用范围:运行时
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {

    String value() default "";

}

(2)创建 MyRequestMapping 注解,作用目标为类和方法:

package morin.springmvc.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义requestMapping注解,作用目标为类和方法,作用范围为运行时
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {
    String value() default "";
}

(3)创建 MyDispatcherServlet,核心控制器,init 完成初始化工作,doPost 处理 HTTP 请求:

package morin.springmvc.servlet;

import morin.springmvc.annotation.MyController;
import morin.springmvc.annotation.MyRequestMapping;
import morin.springmvc.view.MyViewResolver;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

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.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * MyDispathcerServlet class
 *
 * @author Molrin
 * @date 2018/11/5 0005
 */
public class MyDispathcerServlet extends HttpServlet {
    /**
     * 模拟IDC容器,保存bean对象
     */
    private Map<String, Object> iocContainer = new HashMap<String, Object>();
    /**
     * 保存handler映射
     */
    private Map<String, Method> methods = new HashMap<String, Method>();
    /**
     * 视图解析器
     */
    private MyViewResolver myViewResolver;

    @Override
    public void init(ServletConfig config) throws ServletException {
        //扫描Controller,创建实例对象,并存入iocContainer
        scanController(config);
        //初始化Handler映射
        initHandlerMapping();
        //加载视图解析器
        loadViewResolver(config);
    }

    /**
     * 加载视图解析器
     * @param config 配置
     */
    private void loadViewResolver(ServletConfig config) {
        //创建SAXReader解析xml配置
        SAXReader saxReader = new SAXReader();
        try {
            //解析springMVC.xml
            String path = config.getServletContext().getRealPath("") + "\\WEB-INF\\classes\\" + config.getInitParameter("contextConfigLocation");
            Document document = saxReader.read(path);
            Element root = document.getRootElement();
            Iterator iterator = root.elementIterator();
            while (iterator.hasNext()) {
                Element element = (Element) iterator.next();
                if ("bean".equals(element.getName())) {
                    String className = element.attributeValue("class");
                    Class<?> aClass = Class.forName(className);
                    Object o = aClass.newInstance();
                    //获取方法对象
                    Method setPrefix = aClass.getMethod("setPrefix", String.class);
                    Method setSuffix = aClass.getMethod("setSuffix", String.class);
                    Iterator beanIter = element.elementIterator();
                    //获取property值
                    HashMap<String, String> map = new HashMap<String, String>();
                    while (beanIter.hasNext()) {
                        Element next = (Element) beanIter.next();
                        String name = next.attributeValue("name");
                        String value = next.attributeValue("value");
                        map.put(name, value);
                    }
                    for (String key : map.keySet()) {
                        //反射机制调用set方法,完成赋值
                        if ("prefix".equals(key)) {
                            setPrefix.invoke(o, map.get(key));
                        }
                        if ("suffix".equals(key)) {
                            setSuffix.invoke(o, map.get(key));
                        }
                    }
                    myViewResolver = (MyViewResolver)o;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 初始化handler映射
     */
    private void initHandlerMapping() {
        Set<String> keySet = iocContainer.keySet();
        for (String value : keySet) {
            //获取每个类的class对象
            Class<?> aClass = iocContainer.get(value).getClass();
            //获取该类的方法数组
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                //判断方法是否带有MyRequestMapping注解
                if (method.isAnnotationPresent(MyRequestMapping.class)) {
                    //获取该注解中的值
                    MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
                    String ann = annotation.value().substring(1);
                    //将带有注解的方法存入处理器映射器
                    this.methods.put(ann, method);
                }
            }
        }
    }

    /**
     * 扫描controller
     *
     * @param config 配置
     */
    private void scanController(ServletConfig config) {
        //创建解析xml对象
        SAXReader saxReader = new SAXReader();
        try {
            //获取xml路径
            String path = config.getServletContext().getRealPath("") + "\\WEB-INF\\classes\\" + config.getInitParameter("contextConfigLocation");
            //获取document
            Document document = saxReader.read(path);
            //获取根元素
            Element rootElement = document.getRootElement();
            //获取元素迭代器
            Iterator iterator = rootElement.elementIterator();
            //循环遍历
            while (iterator.hasNext()) {
                //获取单个元素
                Element element = (Element) iterator.next();
                //判断是否有包扫描标签
                if ("component-scan".equals(element.getName())) {
                    //获取扫描的包名
                    String basePackage = element.attributeValue("base-package");
                    //获取basePackage包下的所有类名
                    List<String> classNames = getClassName(basePackage);
                    for (String className : classNames) {
                        //根据全限定名获取class对象
                        Class<?> aClass = Class.forName(className);
                        //判断是否有@Controller注解
                        if (aClass.isAnnotationPresent(MyController.class)) {
                            MyRequestMapping requestMapping = aClass.getAnnotation(MyRequestMapping.class);
                            //获取该注解中的value
                            String value = requestMapping.value().substring(1);
                            //使用instance方法创建Controller对象并放入iocContainer
                            iocContainer.put(value, aClass.newInstance());
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private List<String> getClassName(String basePackage) {
        //创建返回值
        List<String> classNames = new ArrayList<String>();
        //将全限定名中的. 全部替换为/
        String newNames = basePackage.replace(".", "/");
        //获取当前线程的类加载器
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        URL url = contextClassLoader.getResource(newNames);
        if (url != null) {
            File file = new File(url.getPath());
            //获取该目录下的所有文件路径的File对象集合
            getFileName(file.listFiles(), basePackage,classNames);
           
        }
        return classNames;
    }
    private void getFileName(File[] file,String basePackage,List<String> classNames){
        String path;
        if (file != null) {
            for (File file1 : file) {
                if (!file1.isFile()) {
                    File[] files = file1.listFiles();
                    path = basePackage+"." +file1.getName();
                    getFileName(files,path,classNames);
                }else {
                    path = basePackage + "." + file1.getName().replace(".class", "");
                    classNames.add(path);
                }
            }
        }
    }
    @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 {
        //获取请求
        String handlerUri = req.getRequestURI().split("/")[1];
        //获取controller实例
        Object obj = iocContainer.get(handlerUri);
        String methodUri = req.getRequestURI().split("/")[2];
        //获取method实例
        Method method = methods.get(methodUri);
        try {
            //反射机制调用方法
            String value = (String) method.invoke(obj,req.getParameter("sessionId"));
            //视图解析器将逻辑视图转换成物理视图
            String view = myViewResolver.jspMapping(value);
            //页面跳转
            req.getRequestDispatcher(view).forward(req, resp);
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

(4)创建视图解析器 MyViewResolver:

package morin.springmvc.view;

/**
 * MyViewResolver class
 * 视图解析器
 * @author Molrin
 * @date 2018/11/5 0005
 */
public class MyViewResolver {

    /**
     * 视图前缀
     */
    private String prefix;
    /**
     * 视图后缀
     */
    private String suffix;

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }

    public String jspMapping(String value) {
        return prefix+value+suffix;
    }
}

(5)创建 TestController,处理业务请求:

package morin.springmvc.controller;

import morin.springmvc.annotation.MyController;
import morin.springmvc.annotation.MyRequestMapping;
import org.springframework.web.bind.annotation.CookieValue;

/**
 * TestController class
 *
 * @author Molrin
 * @date 2018/11/5 0005
 */
@MyController
@MyRequestMapping("/testController")
public class TestController {

    @MyRequestMapping(value = "/test")
    public String testHello(){
        System.out.println("执行controller");
        System.out.println("success!");
        return "index";
    }
}

(6)springMVC.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置包扫描-->
    <context:component-scan base-package="morin.springmvc"/>
    <!--配置自定义视图解析器-->
    <bean class="morin.springmvc.view.MyViewResolver">
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

(7)web.xml:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <!--配置自定义的DispatcherServlet核心控制器-->
  <servlet>
    <servlet-name>mySpringMVC</servlet-name>
    <servlet-class>morin.springmvc.servlet.MyDispathcerServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>springMVC.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>mySpringMVC</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

(8)测试:
测试

点击此处获取源码

猜你喜欢

转载自blog.csdn.net/qq_41785135/article/details/83781493
今日推荐