手写模拟SpringMVC框架

本文详细介绍了如何从零开始手写一个简单的SpringMVC框架,涵盖了项目结构、POM配置、流程图、自定义注解、前端控制器创建、Spring容器初始化以及请求映射的实现。通过这个过程,读者可以深入理解SpringMVC的工作原理和内部机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

本篇内容为图灵学院VIP上课笔记,补充了老师留的注入参数,分别是直接注入,@RequestParam,@RequestBody三种方式。

需要JAVA基础知识扎实,熟练使用反射,自定义注解等。

项目的所有代码我就不贴了,要全部代码的可以私聊我。比较重要的地方我会贴上。

项目结构图

在这里插入图片描述

pom文件及说明

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.baiqi</groupId>
  <artifactId>baiqi_mvc</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>baiqi_mvc Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

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

    <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.5</version>
    </dependency>



    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.1</version>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.7.0</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          <compilerArgs>
            <arg>-parameters</arg>
          </compilerArgs>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
    </plugins>

  </build>
</project>

最下面的plugin的parameters是为了解决反射获取参数是arg0用的。需要在idea的setting里进行设置
在这里插入图片描述

流程图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

创建控制层、业务层代码(Controller、Service、准备自定义注 解)、准备SpringMvc核心配置文件

在这里插入图片描述在这里插入图片描述
自定义注解
在这里插入图片描述
比如AutoWired注解

/*
@Retention注解表示Annotation的保留策略
RetentionPolicy.Class:运行时不保留,不可以通过反射读取。
        RetentionPolicy.RUNTIME:运行是保留,可以通过反射读取。
        RetentionPolicy.SOURCE:丢弃。
        *
        */
@Target(value= ElementType.FIELD)
@Retention(value= RetentionPolicy.RUNTIME)
public @interface AutoWired {
       String value();
}

自定义注解很基本我就不多说了。不会的看B站狂神说的课程。

前端控制器

准备前端控制器 ,创建一个Servlet,同时在web.xml文件中声明该前端控制器
创建Spring容器,通过DOM4J解析springmvc的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>
  <display-name>Archetype Created Web Application</display-name>


    <!--配置前端控制器-->
  <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>com.springmvc.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!--Web服务器一旦启动,Servlet就会实例化创建对象,然后初始化(预备创建对象)-->
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

前端控制器

创建Spring容器,通过DOM4J解析springmvc的XML文件

扫描springmvc中的控制器以及service类并实例化对象放入容器中

SpringMVC容器WebApplicationContext

package com.springmvc.context;

import com.springmvc.annotation.AutoWired;
import com.springmvc.annotation.Controller;
import com.springmvc.annotation.Service;
import com.springmvc.xml.XmlPaser;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 白起老师
 * springMvc容器
 */
public class WebApplicationContext {

    //classpath:springmvc.xml
    String contextConfigLocation;

    //定义集合  用于存放 bean 的权限名|包名.类名
    List<String> classNameList = new ArrayList<String>();

    //创建Map集合用于扮演IOC容器:  key存放bean的名字   value存放bean实例
    public Map<String,Object> iocMap = new ConcurrentHashMap<>();

    public WebApplicationContext() {
    }

    public WebApplicationContext(String contextConfigLocation) {
           this.contextConfigLocation = contextConfigLocation;
    }

    /**
     * 初始化Spring容器
     */
    public void onRefresh(){

        //1、进行解析springmvc配置文件操作  ==》 com.baiqi.controller,com.baiqi.service
         String pack = XmlPaser.getbasePackage(contextConfigLocation.split(":")[1]);

         String[] packs = pack.split(",");
         //2、进行包扫描
         for(String pa : packs){
             excuteScanPackage(pa);
         }

         //3、实例化容器中bean
        executeInstance();

         //4、进行 自动注入操作
        executeAutoWired();
    }

    //进行自动注入操作
    public void executeAutoWired(){

        try {
            //从容器中 取出  bean  ,然后判断 bean中是否有属性上使用 AutoWired,如果使用了搞注解,就需要进行自动注入操作
            for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
                //获取容器中的bean
                Object bean = entry.getValue();
                //获取bean中的属性
                Field[] fields = bean.getClass().getDeclaredFields();
                for (Field field : fields) {
                    if(field.isAnnotationPresent(AutoWired.class)){
                        //获取注解中的value值|该值就是bean的name
                        AutoWired autoWiredAno =  field.getAnnotation(AutoWired.class);
                        String beanName = autoWiredAno.value();
                        //取消检查机制
                        field.setAccessible(true);
                        field.set(bean,iocMap.get(beanName));

                    }
                }


            }
        }catch(Exception e){
            e.printStackTrace();
        }

    }

    /**
     * 实例化容器中的bean
     */
    public void executeInstance(){

        try{
            // com.baiqi.controller.UserController      com.baiqi.service.impl.UserServiceImpl
            for (String className : classNameList) {

                Class<?> clazz =   Class.forName(className);

                if(clazz.isAnnotationPresent(Controller.class)){
                    //控制层 bean

                    String beanName = clazz.getSimpleName().substring(0,1).toLowerCase()+ clazz.getSimpleName().substring(1);
                    iocMap.put(beanName,clazz.newInstance());

                }else if(clazz.isAnnotationPresent(Service.class)){
                    //Service层  bean
                    Service serviceAn = clazz.getAnnotation(Service.class);
                   String beanName = serviceAn.value();
                    iocMap.put(beanName,clazz.newInstance());
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }


    }

    /**
     * 扫描包
     */
    public void excuteScanPackage(String pack){
        //   com.baiqi.controller   ==> com/baiqi/controller
        URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\\.", "/"));
        String path = url.getFile();
        // /com/bruce/service
        File dir=new File(path);
        for(File f:dir.listFiles()){
            if(f.isDirectory()){
                //当前是一个文件目录  com.baiqi.service.impl
                excuteScanPackage(pack+"."+f.getName());
            }else{
                //文件目录下文件  获取全路径   UserController.class  ==> com.baiqi.controller.UserController
                String className=pack+"."+f.getName().replaceAll(".class","");
                classNameList.add(className);
            }
        }
    }

}

实现容器中对象的注入,比如将服务层对象注入至控制层

建立请求映射地址与控制器以及方法之间的映射关系

package com.springmvc.servlet;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.springmvc.annotation.*;
import com.springmvc.context.WebApplicationContext;
import com.springmvc.handler.MyHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author tianzhen
 */
public class DispatcherServlet extends HttpServlet {

    //指定SpringMvc容器
    private WebApplicationContext webApplicationContext;

    /***
     * 创建集合  用于存放  映射关系    映射地址  与  控制器.方法,用于发送请求直接从该集合中进行匹配
     */
    List<MyHandler>  handList = new ArrayList<>();

    @Override
    public void init() throws ServletException {

        //1、加载初始化参数   classpath:springmvc.xml
       String  contextConfigLocation =  this.getServletConfig().getInitParameter("contextConfigLocation");

        //2、创建Springmvc容器
        webApplicationContext = new WebApplicationContext(contextConfigLocation);

        //3、进行初始化操作
        webApplicationContext.onRefresh();

        //4、初始化请求映射关系   /findUser   ===》控制器.方法
        initHandlerMapping();
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

          //进行请求分发处理
          doDispatcher(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              this.doPost(request,response);
    }

    /***
     * 进行请求分发处理
     * @param req
     * @return
     */
    public void  doDispatcher(HttpServletRequest req, HttpServletResponse resp){
            //根据用户的请求地址  /findUser   查找Handler|Controller
            MyHandler myHandler = getHandler(req);
            try{
                if(myHandler == null){
                    resp.getWriter().print("<h1>404 NOT  FOUND!</h1>");
                }else{
                    //调用处理方法之前 进行参数的注入
                    List<Object> args=new ArrayList<>();
                    //当有RequestParam注解时
                    Method method = myHandler.getMethod();
                    Class<?> clazz = method.getParameterTypes()[0];
                    Object o = clazz.newInstance();
                    for (Parameter parameter : method.getParameters()) {
                        if (parameter.isAnnotationPresent(RequestParam.class)) {
                            String param=parameter.getAnnotation(RequestParam.class).value();
                            args.add(req.getParameter(param));
                        }else if(parameter.isAnnotationPresent(RequestBody.class)){
                            //从request输入流里获取json字符串
                            BufferedReader reader = new BufferedReader(new InputStreamReader(req.getInputStream()));
                            StringBuffer buffer = new StringBuffer();
                            String line = " ";
                            while ((line = reader.readLine()) != null){
                                buffer.append(line);
                            }
                            ObjectMapper objectMapper = new ObjectMapper();
                            //json串转为map
                            Map<String,Object> map = objectMapper.readValue(buffer.toString(), Map.class);
                            for (String str : map.keySet()) {
                                System.out.println(str);
                                //获取所有字段数组,对参数对象进行实例化
                                Field[] fields = clazz.getDeclaredFields();
                                for (Field field : fields) {
                                    field.setAccessible(true);
                                    if (str.equals(field.getName())) {
                                        field.set(o, map.get(str));
                                    }
                                }
                            }
                            args.add(o);
                        }else {
                            //当无注解,直接注入时,方法2
                            args.add(req.getParameter(parameter.getName()));
                        }

                    }
                    //调用目标方法
                    Object result = myHandler.getMethod().invoke(myHandler.getController(),args.toArray());
                    
                    if(result instanceof String){
                        //跳转JSP
                        String viewName=(String)result;
                        // forward:/success.jsp
                        if(viewName.contains(":")){
                            String viewType=viewName.split(":")[0];
                            String viewPage=viewName.split(":")[1];
                            if(viewType.equals("forward")){
                                req.getRequestDispatcher(viewPage).forward(req,resp);
                            }else{
                                // redirect:/user.jsp
                                resp.sendRedirect(viewPage);
                            }
                        }else{
                            //默认就转发
                            req.getRequestDispatcher(viewName).forward(req,resp);
                        }
                    }else{
                        //返回JSON格式数据
                        if(method.isAnnotationPresent(ResponseBody.class)){
                            //将返回值转换成 json格式数据
                            ObjectMapper objectMapper = new ObjectMapper();
                            String json = objectMapper.writeValueAsString(result);
                            resp.setContentType("text/html;charset=utf-8");
                            PrintWriter writer = resp.getWriter();
                            writer.print(json);
                            writer.flush();
                            writer.close();

                        }
                    }

                }
            }catch(Exception e){
               e.printStackTrace();
        }


    }

    /***
     * 获取请求对应的handler,根据用户请求查找对应的Handler
     * @param req
     * @return
     */
    public MyHandler getHandler(HttpServletRequest req) {
        // /findUser
        String requestURI = req.getRequestURI();
        for (MyHandler myHandler : handList) {
            //从容器的Handle取出URL  和  用户的请求地址进行匹配,找到满足条件的Handler
            if (myHandler.getUrl().equals(requestURI)) {
                return myHandler;
            }
        }
        return null;
    }

    /**
     * 初始化请求映射关系
     */
    public void  initHandlerMapping(){
        for (Map.Entry<String, Object> entry : webApplicationContext.iocMap.entrySet()) {
            //获取bean的class类型
            Class<?> clazz =  entry.getValue().getClass();
            if(clazz.isAnnotationPresent(Controller.class)){
                //获取bean中所有的方法,为这些方法建立映射关系
                Method[] methods =  clazz.getDeclaredMethods();
                for (Method method : methods) {
                    if(method.isAnnotationPresent(RequestMapping.class)){
                        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                        //获取注解中的值   /findUser
                        String url = requestMapping.value();

                        //建立  映射地址  与  控制器.方法
                        MyHandler myHandler = new MyHandler(url,entry.getValue(),method);
                        handList.add(myHandler);
                    }
                }
            }
        }
    }
}

封装控制器

package com.springmvc.handler;


import java.lang.reflect.Method;

/**
 * @BelongsProject: SpringMvc
 * @Description: TODO
 */

public class MyHandler {

	//请求URL地址
    private String url;
    //后台控制器
    private Object controller;
    //控制器中指定的方法
    private Method method;
    
    
    
	public MyHandler() {
		super();
	}
	
	
	public MyHandler(String url, Object controller, Method method) {
		super();
		this.url = url;
		this.controller = controller;
		this.method = method;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public Object getController() {
		return controller;
	}

	public void setController(Object controller) {
		this.controller = controller;
	}

	public Method getMethod() {
		return method;
	}

	public void setMethod(Method method) {
		this.method = method;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值