手写简易SpringMVC框架

本文详细介绍了SpringMVC框架的核心概念、优点以及运行流程,包括DI和AOP的应用,以及通过示例展示了如何在SpringMVC中实现控制器和URL映射。
摘要由CSDN通过智能技术生成

重新认识SpringMVC框架

1. Spring框架图

在这里插入图片描述

2. Spring框架

  • Spring 核心功能
    • DI(Dependency Injection, 依赖注入)
    • AOP(Aspect Oriented Programming, 面向切面编程)
  • 框架优点
    • 轻量级,没有侵入性,默认单例;
    • 使用 IoC 更加容易组合对象之间的关系;
    • 面向接口编程,降低耦合;
    • AOP 可以更加容易的进行功能扩展;

3. 如何理解SpringMVC

(1)MVC是什么?
MVC 是模型(model)、视图(view)、控制器(controller)的缩写,它是一个设计模式。
(2)Spring是什么?
面向接口的编程思想,解决的是业务逻辑层和其他各层的松耦合问题。(可简单理解为IOC + AOP

4. SpringMVC运行流程

在这里插入图片描述

手写SpringMVC理论基础

在这里插入图片描述

思路整理如下

  • 1、读取配置
  • 2、初始化
    • 加载配置文件
    • 扫描用户配置包下的类
    • 通过反射机制实例化包下的类,并且放到 IOC 容器中
    • 初始化 HandlerMapping
  • 3、运行
    • 异常拦截
    • 获取请求传入的参数并处理参数
    • 通过初始化完的 handlerMapping 中拿出 url 对应的方法名,反射调用

代码实现

1. web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <servlet>
    <servlet-name>StudyMVC</servlet-name>
    <servlet-class>com.ming.mvc.servlet.MyDispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath*:application.properties</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>StudyMVC</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

2. pom文件引入servlet

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
</dependency>

3. 注解类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
   String value() default "";
}

@Target(value = {ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
   String value() default "";
}

@Target(value = {ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
   String value();
}

4. MyDispatcherServlet

package com.ming.mvc.servlet;
import com.ming.mvc.annotation.Controller;
import com.ming.mvc.annotation.RequestMapping;
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.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class MyDispatcherServlet extends HttpServlet {
   private Properties properties = new Properties();
   private List<String> classNames = new ArrayList<String>();
   private Map<String, Object> ioc = new HashMap<String, Object>();
   private Map<String, Method> handlerMapping = new HashMap<String, Method>();
   private Map<String, Object> controllerMap = new HashMap<String, Object>();

   @Override
   public void init(ServletConfig config) throws ServletException {
      // 1、加载配置文件
      doLoadConfig(config.getInitParameter("contextConfigLocation").replaceAll("classpath\\*:", ""));
      // 2、扫描的类
      doScanner(properties.getProperty("scanPackage"));
      // 3、拿到扫描的类后,通过反射机制实例化,并放到ioc容器中
      doInstance();
      // 4、初始化handlerMapping(url{login} ->  method{login()})
      initHandlerMapping();
   }

   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      System.out.println("欢迎使用手写的SpringMVC框架");
      this.doPost(req, resp);
   }

   @Override
   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      try {
         doDispatch(req, resp);
      } catch (Exception e) {
         e.printStackTrace();
         resp.getWriter().write("500!! Server Exception");
      }
   }

   private void doLoadConfig(String location) {
      System.out.println("这是调用了doLoadConfig方法");
      InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(location);
      try {
         properties.load(inputStream);
      } catch (IOException e) {
         e.printStackTrace();
      } finally {
         try {
            inputStream.close();
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
   }

   private void doScanner(String packageName) {
      System.out.println("这是调用了doScanner方法");
      URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
      File dir = new File(url.getFile());
      for (File file : dir.listFiles()) {
         if (file.isDirectory()) {
            doScanner(packageName + "." + file.getName());
         } else {
            String className = packageName + "." + file.getName().replace(".class", "");
            classNames.add(className);
            System.out.println("Spring容器扫描到类有:" + packageName + "." + file.getName());
         }
      }
   }

   private void doInstance() {
      System.out.println("这是调用了doInstance方法");
      for (String className : classNames) {
         Class<?> clazz = null;
         try {
            clazz = Class.forName(className);
            if (clazz.isAnnotationPresent(Controller.class)) {
               ioc.put(toLowerFirstWord(clazz.getSimpleName()), clazz.newInstance());
            } else {
               continue;
            }
         } catch (Exception e) {
            e.printStackTrace();
            continue;
         }
      }
   }

   private void initHandlerMapping(){
      System.out.println("这是调用了initHandlerMapping方法");
      try {
         for (Map.Entry entry : ioc.entrySet()){
            Class<?> clazz = entry.getValue().getClass();
            if (!clazz.isAnnotationPresent(Controller.class)){
               continue;
            }
            String baseUrl = "/spring-mvc-demo/";
            if (clazz.isAnnotationPresent(RequestMapping.class)){
               RequestMapping annotation = clazz.getAnnotation(RequestMapping.class);
               baseUrl = baseUrl + annotation.value();
            }
            Method[] methods = clazz.getMethods();
            for (Method method : methods){
               if (!method.isAnnotationPresent(RequestMapping.class)){
                  continue;
               }
               RequestMapping annotation = method.getAnnotation(RequestMapping.class);
               String url = annotation.value();
               url = (baseUrl + "/" + url).replaceAll("/+", "/"); // 解析路径有有多个"//"转换为"/"
               handlerMapping.put(url, method);
               controllerMap.put(url, clazz.newInstance());
               System.out.println(url + "," + method);
            }
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
      if (handlerMapping.isEmpty()){
         return;
      }

      String url = req.getRequestURI();
      String contextPath = req.getContextPath();
      url.replace(contextPath, "").replaceAll("/+","/");
      if (!this.handlerMapping.containsKey(url)){
         resp.getWriter().write("404 NOT FOUND!");
         return;
      }
      Method method = this.handlerMapping.get(url);
      // 获取方法的参数列表
      Class<?>[] parameterTypes = method.getParameterTypes();
      // 获取请求的参数
      Map<String, String[]> parameterMap = req.getParameterMap();
      Object[] paramValues = new Object[parameterTypes.length];
      for (int i = 0; i < parameterTypes.length; i++) {
         // 根据参数名称,做某些处理
         String requestParam = parameterTypes[i].getSimpleName();
         if (requestParam.equals("HttpServletRequest")){
            paramValues[i] = req;
            continue;
         }
         if (requestParam.equals("HttpServletResponse")){
            paramValues[i] = resp;
            continue;
         }
         if (requestParam.equals("String")){
            for (Map.Entry<String, String[]> param : parameterMap.entrySet()){
               String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
               paramValues[i] = value;
            }
         }
      }

      try {
         method.invoke(this.controllerMap.get(url), paramValues);
      } catch (Exception e){
         e.printStackTrace();
      }
   }

   private String toLowerFirstWord(String name){
      char[] charArray = name.toCharArray();
      charArray[0] += 32;
      return String.valueOf(charArray);
   }
}

5. application.properties

scanPackege=com.xquant.mvc

6. Controller测试

package com.ming.mvc.controller;
import com.ming.mvc.annotation.Controller;
import com.ming.mvc.annotation.RequestMapping;
import com.ming.mvc.annotation.RequestParam;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Controller
@RequestMapping("/study")
public class StudyController {
   @RequestMapping("/doTest")
   public void test1(HttpServletRequest request, HttpServletResponse response,
                 @RequestParam("param") String param) {
      System.out.println(param);
      try {
         response.getWriter().write("doTest method success! param:" + param);
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

7. 测试

在这里插入图片描述

"C:\Program Files\Java\jdk1.8.0_151\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:62439,suspend=y,server=n -Dmaven.multiModuleProjectDirectory=F:\dev_code\springboot\spring-mvc-demo -DWORKING_PATH=F:\dev_code\springboot\spring-mvc-demo\src\main "-Dmaven.home=C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\plugins\maven\lib\maven3" "-Dclassworlds.conf=C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\plugins\maven\lib\maven3\bin\m2.conf" "-Dmaven.ext.class.path=C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\plugins\maven\lib\maven-event-listener.jar" -javaagent:C:\Users\1\.IntelliJIdea2019.3\system\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\plugins\maven\lib\maven3\boot\plexus-classworlds-2.6.0.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\lib\idea_rt.jar" org.codehaus.classworlds.Launcher -Didea.version2019.3.1 -s E:\apache-maven-3.0.4\conf\settings.xml jetty:run
Connected to the target VM, address: '127.0.0.1:62439', transport: 'socket'
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------< org.example:spring-mvc-demo >---------------------
[INFO] Building spring-mvc-demo 1.0-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] >>> jetty-maven-plugin:7.6.15.v20140411:run (default-cli) > test-compile @ spring-mvc-demo >>>
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ spring-mvc-demo ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ spring-mvc-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ spring-mvc-demo ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory F:\dev_code\springboot\spring-mvc-demo\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ spring-mvc-demo ---
[INFO] No sources to compile
[INFO]
[INFO] <<< jetty-maven-plugin:7.6.15.v20140411:run (default-cli) < test-compile @ spring-mvc-demo <<<
[INFO]
[INFO]
[INFO] --- jetty-maven-plugin:7.6.15.v20140411:run (default-cli) @ spring-mvc-demo ---
[INFO] Configuring Jetty for project: spring-mvc-demo
[INFO] webAppSourceDirectory not set. Defaulting to F:\dev_code\springboot\spring-mvc-demo\src\main\webapp
[INFO] Reload Mechanic: automatic
[INFO] Classes = F:\dev_code\springboot\spring-mvc-demo\target\classes
[INFO] Context path = /spring-mvc-demo
[INFO] Tmp directory = F:\dev_code\springboot\spring-mvc-demo\target\tmp
[INFO] Web defaults = org/eclipse/jetty/webapp/webdefault.xml
[INFO] Web overrides =  none
[INFO] web.xml file = file:/F:/dev_code/springboot/spring-mvc-demo/src/main/webapp/WEB-INF/web.xml
[INFO] Webapp directory = F:\dev_code\springboot\spring-mvc-demo\src\main\webapp
[INFO] jetty-7.6.15.v20140411
[INFO] No Transaction manager found - if your webapp requires one, please configure one.
[INFO] started o.m.j.p.JettyWebAppContext{/spring-mvc-demo,file:/F:/dev_code/springboot/spring-mvc-demo/src/main/webapp/},file:/F:/dev_code/springboot/spring-mvc-demo/src/main/webapp/
这是调用了doLoadConfig方法
这是调用了doScanner方法
这是调用了doScanner方法
Spring容器扫描到类有:com.ming.mvc.annotation.Controller.class
Spring容器扫描到类有:com.ming.mvc.annotation.RequestMapping.class
Spring容器扫描到类有:com.ming.mvc.annotation.RequestParam.class
这是调用了doScanner方法
Spring容器扫描到类有:com.ming.mvc.controller.HelloController.class
Spring容器扫描到类有:com.ming.mvc.controller.StudyController.class
这是调用了doScanner方法
Spring容器扫描到类有:com.ming.mvc.servlet.DispatcherServlet.class
这是调用了doInstance方法
这是调用了initHandlerMapping方法
/spring-mvc-demo/study/doTest,public void com.ming.mvc.controller.StudyController.test1(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,java.lang.String)
/spring-mvc-demo/study/login,public void com.ming.mvc.controller.StudyController.login(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,java.lang.String,java.lang.String) throws javax.servlet.ServletException,java.io.IOException
/spring-mvc-demo/,public void com.ming.mvc.controller.HelloController.hello(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws javax.servlet.ServletException,java.io.IOException
[WARNING] !RequestLog
[INFO] Started SocketConnector@0.0.0.0:8001
[INFO] Started Jetty Server
欢迎使用手写的SpringMVC框架
  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值