手写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容器