我们要清楚DispatcherServlet是怎样拦截到我们在浏览器地址栏中发出的请求,以及通过我们的请求找到控制器类中对应的请求方法,并获取到方法的返回值,通过视图解析器找到对应的视图,最终通过请求转发跳转到我们想要请求的页面中。
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Archetype Created Web Applicationdisplay-name>
<servlet>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath*:applicationContext-springmvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
我们知道在web.xml文件中配置前端控制器,但是内部原理是怎么实现的,下面可以通过一个简单的例子来演示下原理,我们不使用系统提供的前端控制器类,我们自己
新建一个DispatcherServlet类。
使用maven创建一个springmvc-DispatcherServlet-principle的Web项目,只需导入javax.servlet.api的依赖文件:
pom.xml
<!--servlet-api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>compile</scope>
</dependency>
DispatcherServlet类(要继承HttpServlet类)
package com.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* @author 一宿君(CSDN : qq_52596258)
* @date 2021-07-25 15:04:05
*/
public class DispatcherServlet extends HttpServlet {
public DispatcherServlet() {
System.out.println("构造方法");
}
@Override
public void init() throws ServletException {
System.out.println("初始化方法---作用是为了存储通过注解扫描controller类下所有@RequestMapping注解标注的value值(请求名)");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
}
springmvc-config.xml(此文件可以为空,目前我们暂不进行注解配置的操作)
<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:bean="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Archetype Created Web Application</display-name>
<!--配置servlet调度程序-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>com.servlet.DispatcherServlet</servlet-class>
<!--启动tomcat服务器时,默认加载springmvc-config.xml配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:springmvc-config.xml</param-value>
</init-param>
<!--表示容器在启动时立即加载Servlet(此处值越小,加载优先级越高)-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
创建UserControler控制器类
package com.controller;
/**
* @author 一宿君(CSDN : qq_52596258)
* @date 2021-07-25 15:11:26
*/
public class UserController {
//请求到达登录界面
public String toLogin(){
return "login";
}
//请求到达注册界面
public String toRegister(){
return "register";
}
//请求到达删除界面
public String toDelete(){
return "delete";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>这是login.jsp页面</h1>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>这是register.jsp页面</h1>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>这是delete.jsp页面</h1>
</body>
</html>
到这一步我们先将项目部署到tomcat服务器上,先保证运行正常!springmvc.xml
<mvc:annotation-driven/>
<context:component-scan base-package="com.controller"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="suffix" value=".jsp"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
bean>
DispatcherServlet类中init()初始化方法:
/**
* 用于存储请求访问名字
*/
Map<String,String> mappings = new HashMap<>();
@Override
public void init() throws ServletException {
System.out.println("初始化方法---作用是为了存储通过注解扫描controller类下所有@RequestMapping注解标注的value值(请求名)");
mappings.put("toLogin","com.controller.UserController.toLogin");
mappings.put("toRegister","com.controller.UserController.toRegister");
mappings.put("toDelete","com.controller.UserController.toDelete");
}
DispatcherServlet类中的doGet方法
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 获取请求URI
String requestURI = req.getRequestURI();
System.out.println(requestURI);
//2 获取请求URI名称(从最后一个/处,加1截取)
String requestURIName = requestURI.substring(requestURI.lastIndexOf("/") + 1);
System.out.println(requestURIName);
}
当我们得到请求地址名后,再去map集合中进行对比,查看我们事先初始化时(此处是模拟springmvc通过开启注解扫描将@RequestMapping注解标注的方法存入集合中)存入集合中的方法,是否有有我们现在所发出的请求地址名,如果有则进行下一步视图解析操作,如果无,则说明该请求无效。
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 获取请求URI
String requestURI = req.getRequestURI();
System.out.println(requestURI);
//2 获取请求URI名称(从最后一个/处,加1截取)
String requestURIName = requestURI.substring(requestURI.lastIndexOf("/") + 1);
System.out.println(requestURIName);
/**
* 获取到请求名后,与我们实现存在map集合中的键值对比,是否存在
*/
if(mappings.containsKey(requestURIName )){
//获取请求URI名称---在控制类中的所对应的方法的路径
String classPath = mappings.get(requestURIName);
System.out.println(classPath);
//获取方法所在类的路径
String className = classPath.substring(0,classPath.lastIndexOf("." ));
System.out.println(className);
//获取所对应的方法名(从最后一个“.”加1截取)
String methodName = classPath.substring(classPath.lastIndexOf(".") + 1);
System.out.println(methodName);
try {
//通过反射加载到类
Class clz = Class.forName(className);
//创建加载类对象
Object obj = clz.getDeclaredConstructor().newInstance();
//获取对象中的指定请求方法
Method method = clz.getDeclaredMethod(methodName,null);
//执行反射中对应的请求方法,并获取返回值(也即是页面名称)
Object page = method.invoke(obj,null);
System.out.println(page);
req.getRequestDispatcher(page + ".jsp").forward(req,resp);
} catch (Exception e) {
e.printStackTrace();
}
}else {
throw new ServletException("请求的URI不存在!");
}
}
注意上述请求转发request.getRequestDispatcher(page + “.jsp”).forward(req,resp);
一定要交.jsp 后缀名,因为我们在web.xml配置文件中,配置的是< url-pattern>/< /url-pattern>,不会拦截.jsp文件,其余请求以及静态资源都会被当做请求拦截。
我们重启项目,在地址栏输入toLogin
查看控制台:
同理再地址栏输入toRegister和toDelete: