手写SpringMVC,实现SpringMVC容器,加底层@RequestMapping的映射

1. 准备阶段


第一步:导包导依赖。

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.itholmes</groupId>
  <artifactId>SpringMVC_Source2</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>SpringMVC_Source2 Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.8.2</version>
      <scope>test</scope>
    </dependency>


    <!--Servlet-->
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <!--Mybatis-->
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.6</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.6</version>
    </dependency>

    <!--Spring-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.15</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.22</version>
    </dependency>

  </dependencies>
  <build>
    <finalName>SpringMVC_Source2</finalName>
  </build>
</project>

第二步:构建spring-mvc.xml文件。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       ">

    <context:component-scan base-package="com.itholmes.controller"/>

</beans>

web.xml文件配置:

  • 这里的servlet-class是自定义的DispatcherServlet,并不是SpringMvc的,这里要手动实现。
  • 启动级别保证为1,项目启动就创建。
<?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>springmvc</servlet-name>
    <servlet-class>com.itholmes.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <!-- 启动级别是1,说明当前类要跟着项目启动而启动。 -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

</web-app>

2. 手动实现SpringMVC容器和@RequestMapping注解映射


第一步:创建Handler类文件,用来存储反射三要素的对象。

  • 因为是存储对象,所以放到pojo层或者bean层。
package com.itholmes.pojo;

import java.lang.reflect.Method;

public class Handler {
	
	private Object obj;
	private Method method;
	private Object[] args;
	
	public Object getObj() {
		return obj;
	}
	public void setObj(Object obj) {
		this.obj = obj;
	}
	public Method getMethod() {
		return method;
	}
	public void setMethod(Method method) {
		this.method = method;
	}
	public Object[] getArgs() {
		return args;
	}
	public void setArgs(Object[] args) {
		this.args = args;
	}
	
}

第二步:创建DispatcherServlet类文件。

  • 仔细阅读注释内容!
  • 主要是通过反射获取到注解信息,最后获取注解的value值。让value和反射三要素创建的对象完成映射关系。再经过doget或dopost方法拿到值返回前端。
package com.itholmes;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.itholmes.pojo.Handler;

//模拟实现SpringMVC容器,底层@RequestMapping的映射
public class DispatcherServlet extends HttpServlet{
	
	Map<String,Handler> ioc_final = new HashMap<String, Handler>();
	
	/*
	  	<init-param>
      		<param-name>contextConfigLocation</param-name>
      		<param-value>classpath:spring-mvc.xml</param-value>
    	</init-param>	
    	在init中完成上面的内容:
	 */
	@Override
	public void init() throws ServletException {
		//完成initparam对应的servletConfig配置
		ServletConfig config = getServletConfig();
		String initParameter = config.getInitParameter("contextConfigLocation");
		boolean contains = initParameter.contains(":");
		String trim = null;
		if(contains) {
			String[] split = initParameter.split(":");
			trim = split[1].trim();
		}else {
			trim = initParameter.trim();
		}
		//创建SpringMVC的ioc1容器
		ClassPathXmlApplicationContext ioc1 = new ClassPathXmlApplicationContext(trim);
		//遍历ioc1中的类的名字
		String[] names = ioc1.getBeanDefinitionNames();
		//根据每个name从容器中获取出对应的对象
		for (String name : names) {
			//ioc1容器中每一个对应的对象
			Object obj = ioc1.getBean(name);
			Class<? extends Object> c = obj.getClass();
			//判断有没有Controller这个注解
			Controller controller = c.getAnnotation(Controller.class);
			if(controller!=null) {
				//说明有@Controller注解
				//获取类的RequestMapping映射
				//获取出全部方法,看看那个方法上面有@RequestMapping
				Method[] methods = c.getDeclaredMethods();
				for (Method method : methods) {
					RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
					if(requestMapping!=null) {
						//说明有@RequestMapping注解,因为注解本身就有String[] values()的内容,所以这里我们可以直接调用。
						String[] values = requestMapping.value();
						//遍历多个path建立映射关系
						for (String value : values) {
							//调用反射方法的三要素:method.invoke(obj,args)
							//因为这里要设计到三个内容映射,method方法,obj对象,args参数。我们可以通过面向对象创建对象来存储。
							Handler handler = new Handler();
							handler.setObj(obj);
							handler.setMethod(method);
							//args参数就忽略了。
							
							//将映射关系注入到ioc_final的容器中。
							ioc_final.put(value, handler);
							System.out.println();
						}
					}else {
						//说明没有@RequestMapping注解
					}
					
				}
			}else {
				//说明没有@Controller注解
			}
		}
		System.out.println();
	}
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//接受每次请求
		//获取到每次的请求,调用对应的操作
		String uri = req.getRequestURI();//获取uri路径,包含项目名
		//去除项目名
		int indexOf = uri.indexOf("/",1);
		String substring = uri.substring(indexOf);
		//从ioc_final中拿到对应的映射三要素
		Handler handler = ioc_final.get(substring);
		
		if(handler==null) {
			//说明容器中没有该路径,返回404
			resp.getWriter().write("404");
			return;
		}
		
		//之后就是调用对应的映射方法就可以了
		Object invoke = null;
		try {
			invoke = handler.getMethod().invoke(handler.getObj(), handler.getArgs());
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//最后将结果返回
		resp.getWriter().write(invoke.toString());
	}
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doGet(req, resp);
	}
}

第三步:创建两个类,用来验证验证。

  • 注意必须在spring-mvc.xml中让他们扫描到这两个类。

MyController类:

package com.itholmes.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Controller
public class MyController {
	@RequestMapping({"/aa","/bb","/cc"})
	public String getController() {
		System.out.println("进入MyController");
		return "MyController";
	}
}

MyController2类:

package com.itholmes.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MyController2 {
	@RequestMapping("/a222")
	public String getController2() {
		System.out.println("进入MyController2");
		return "MyController2";
	}
}

第四步:放到tomcat中运行查看结果。
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xupengboo

你的鼓励将是我创作最大的动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值