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

Posted IT_Holmes

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写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中运行查看结果。

以上是关于手写SpringMVC,实现SpringMVC容器,加底层@RequestMapping的映射的主要内容,如果未能解决你的问题,请参考以下文章

手写spring+springmvc+mybatis框架篇springIOC容器

如何手写一个简易版SpringMVC

springMVC学习笔记手写springmvc

从0手写实现SpringMVC框架

Spring之手写SpringMVC5个注解(之IOC,DI优化)了解三级缓存

带你手写一个SpringMVC框架(有助于理解springMVC)