【SpringMVC学习】今天带你走通Web、Servlet、MVC、SpringMVC。


参考文章:廖雪峰入门Servlet
参考文章:MVC
参考文章:SpringMVC

HTTP请求和HTTP响应

HTTP请求,这是通过Socket.InputStream()进来的信息

GET / HTTP/1.1
Host: www.sina.com.cn
User-Agent: Mozilla/5.0 xxx
Accept: */*
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8

HTTP响应,这是通过Socket.OutputStream()出去的信息

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 21932
Content-Encoding: gzip
Cache-Control: max-age=300

<html>...网页数据...

从0手写一个Web服务器,看看能有多累人

这么多代码也太棒了吧!童鞋,这只是打印了一个HelloWorld,这么多代码会不会过分复杂了?

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8080); // 监听指定端口
        System.out.println("server is running...");
        for (;;) {
            Socket sock = ss.accept();
            System.out.println("connected from " + sock.getRemoteSocketAddress());
            Thread t = new Handler(sock);
            t.start();
        }
    }
}

class Handler extends Thread {
    Socket sock;

    public Handler(Socket sock) {
        this.sock = sock;
    }

    public void run() {
        try (InputStream input = this.sock.getInputStream()) {
            try (OutputStream output = this.sock.getOutputStream()) {
                handle(input, output);
            }
        } catch (Exception e) {
            try {
                this.sock.close();
            } catch (IOException ioe) {
            }
            System.out.println("client disconnected.");
        }
    }

	private void handle(InputStream input, OutputStream output) throws IOException {
	    System.out.println("Process new http request...");
    	var reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
  	  var writer = new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8));
    	// 读取HTTP请求:
    	boolean requestOk = false;
    	String first = reader.readLine();
    	if (first.startsWith("GET / HTTP/1.")) {
    	    requestOk = true;
    	}
    	for (;;) {
        	String header = reader.readLine();
	        if (header.isEmpty()) { // 读取到空行时, HTTP Header读取完毕
    	        break;
        	}
        	System.out.println(header);
    	}
    	System.out.println(requestOk ? "Response OK" : "Response Error");
    	if (!requestOk) {
        	// 发送错误响应:
       	   writer.write("HTTP/1.0 404 Not Found\r\n");
 	       writer.write("Content-Length: 0\r\n");
 	       writer.write("\r\n");
      	   writer.flush();
  	  	}else {
        	// 发送成功响应:
	        String data = "<html><body><h1>Hello, world!</h1></body></html>";
        	int length = data.getBytes(StandardCharsets.UTF_8).length;
        	writer.write("HTTP/1.0 200 OK\r\n");
        	writer.write("Connection: close\r\n");
        	writer.write("Content-Type: text/html\r\n");
        	writer.write("Content-Length: " + length + "\r\n");
        	writer.write("\r\n"); // 空行标识Header和Body的分隔
        	writer.write(data);
        	writer.flush();
    	}
	}
}

在这里插入图片描述

使用Servlet实现一个服务器,看看多简单

Serlvet的创建

这就写完了?对的,我已经通过pw.writer将<h1>Hello, world!</h1>输出了,剩下的HTTP响应部分让Serlvet帮我补齐就行了。

@WebServlet(urlPatterns = "/")
public class HelloServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // 设置响应类型:
        resp.setContentType("text/html");
        // 获取输出流:
        PrintWriter pw = resp.getWriter();
        // 写入响应:
        pw.write("<h1>Hello, world!</h1>");
        // 最后不要忘记flush强制输出:
        pw.flush();
    }
}

因此,在JavaEE平台上,处理TCP连接,解析HTTP协议这些底层工作统统扔给现成的Web服务器去做,我们只需要把自己的应用程序跑在Web服务器上。为了实现这一目的,JavaEE提供了Servlet API,我们使用Servlet API编写自己的Servlet来处理HTTP请求,Web服务器实现Servlet API接口,实现底层功能
在这里插入图片描述

当然,你要别人帮你干活,那肯定要指定一下谁来帮你干活,所以还要简单的配置一下Serlvet

pom.xml文件

<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.itranswarp.learnjava</groupId>		<!--可修改-->
    <artifactId>web-servlet-hello</artifactId>		<!--可修改-->
    <packaging>war</packaging>			<!--打包类型不是jar,而是war,表示Java Web-->
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>5.0.0</version>
            <scope>provided</scope>		<!--表示编译时使用,但不会打包到.war文件中,因为运行期web服务器本身已经提供了Servlet API相关的jar包-->
        </dependency>
    </dependencies>

    <build>
        <finalName>hello</finalName>	<!--可修改:Web App叫hello-->
    </build>
</project>

Servlet的运行

省流:工程打包后,放入tomcat的webapps,启动tomcat,就可以访问了

  1. 运行Maven命令mvn clean package,在target目录下得到一个hello.war文件,这个文件就是我们编译打包后的Web应用程序。

  2. 普通的Java程序是通过启动JVM,然后执行main()方法开始运行。但是Web应用程序有所不同,我们无法直接运行war文件,必须先启动Web服务器,再由Web服务器加载我们编写的HelloServlet,这样就可以让HelloServlet处理浏览器发送的请求。

  3. 无论使用哪个服务器,只要它支持Servlet API 5.0(因为我们引入的Servlet版本是5.0),我们的war包都可以在上面运行。这里我们选择使用最广泛的开源免费的Tomcat服务器。

  4. 把hello.war复制到Tomcat的webapps目录下,然后切换到bin目录,执行startup.sh或startup.bat启动Tomcat服务器:

$ ./startup.sh
Using CATALINA_BASE: …/apache-tomcat-10.1.x
Using CATALINA_HOME: …/apache-tomcat-10.1.x
Using CATALINA_TMPDIR: …/apache-tomcat-10.1.x/temp
Using JRE_HOME: …/jdk-17.jdk/Contents/Home
Using CLASSPATH: …/apache-tomcat-10.1.x/bin/bootstrap.jar:…
Tomcat started.

  1. 在浏览器输入http://localhost:8080/hello/即可看到HelloServlet的输出:
    在这里插入图片描述

Servlet的其他问题

  1. Servlet版本问题
  • 要务必注意servlet-api的版本。4.0及之前的servlet-api由Oracle官方维护,引入的依赖项是javax.servlet:javax.servlet-api,编写代码时引入的包名为:import javax.servlet.*;
  • 而5.0及以后的servlet-api由Eclipse开源社区维护,引入的依赖项是jakarta.servlet:jakarta.servlet-api,编写代码时引入的包名为:import jakarta.servlet.*;
  • 对于很多仅支持Servlet 4.0版本的框架来说,例如Spring 5,我们就只能使用javax.servlet:4.0.0版本,这一点针对不同项目要特别注意。
  1. Servlet路径问题

上述项目中,为啥路径是/hello/而不是/?

  • 因为一个Web服务器允许同时运行多个Web App,而我们的Web App叫hello,因此,第一级目录/hello表示Web App的名字,后面的/才是我们在HelloServlet中映射的路径。
  • 省流:第一个\是给Tomcat中全部项目的,第二个\是才是给hello项目的

Servlet这么爽,我们简单地探索一下它的原理

整体的一个交互就是zei个样子的啦

Web Server:让Servlet运行起来的Web服务器,例如Tomcat。
Servlet Container:Servlet容器就是用来装Servlet的。
Servlet:以init、service、destroy为生命周期
在这里插入图片描述

在走进来看看Servlet Container中的Serlvet,一个工程有多个Class,每个Class就是一个Serlvet。像这里的就有三个Serlvet啦。

@WebServlet(urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
    ...
}

@WebServlet(urlPatterns = "/signin")
public class SignInServlet extends HttpServlet {
    ...
}

@WebServlet(urlPatterns = "/")
public class IndexServlet extends HttpServlet {
    ...
}

并且通过Dispatcher来选择路径指定的Servlet
在这里插入图片描述
我们来看看一个Servlet里面会有什么,拿HelloServlet看看,只列出了常用方法

@WebServlet(urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
	//在 Servlet 的生命期中,仅执行一次 init() 方法。它是在服务器装入 Servlet 时执行的。
	init();
	//缺省服务: 如果 HTTP 请求方法为 GET,则缺省情况下就调用 doGet() ,否则调用doPost()
	service(HttpServletRequest req, HttpServletResponse resp);
	//执行动作,doGet/doPost二选一
	doGet(HttpServletRequest req, HttpServletResponse resp);
	//执行动作,doGet/doPost二选一
	doPost(HttpServletRequest req, HttpServletResponse resp);
	//destroy() 方法仅执行一次,即在服务器停止且卸装Servlet 时执行该方法。
	destroy()
    }
}

什么,你还想看doGet/doPost做了什么动作?自己去上面看实例吧

JSP跟Servlet合作啦,我们来看一下他们能干点啥?

省流:JSP就是Servlet,通过Servlet + JSP分离前后端代码,实际上是将一个Servlet任务分给了两个Servlet去完成。
好了好了,上面我们小打小闹完成了HelloWord,现在我们要大打大脑,把整个html网页输出。就像把www.hao123.com那个网页输出那样。我们来看看代码。

@WebServlet(urlPatterns = "/")
public class HelloServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // 设置响应类型:
        resp.setContentType("text/html");
        // 获取输出流:
        PrintWriter pw = resp.getWriter();
        // 写入响应:
        pw.write("<html>");
		pw.write("<body>");
		pw.write("<h1>Welcome, " + name + "!</h1>");
		pw.write("</body>");
		pw.write("</html>");

        // 最后不要忘记flush强制输出:
        pw.flush();
    }
}

其实能看我这篇文章的童鞋也大概知道前端后端的区别了,上述这段代码明显就是前端跟后端整合在一起了,这样好吗?这样不好。那就把它给分开咯。

下面换一个实例来演示如何使用JSP+Servlet将前后端分离。

省流:下面这个实例就是通过一个forward()函数,从Servlet跳转到JSP,完成前后端分离
首先编写两个JavaBean

public class User {
    public long id;
    public String name;
    public School school;
}

public class School {
    public String name;
    public String address;
}

UserServlet中,我们可以从数据库读取UserSchool等信息,然后,把读取到的JavaBean先放到HttpServletRequest中,再通过forward()传给user.jsp处理

@WebServlet(urlPatterns = "/user")
public class UserServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 假装从数据库读取:
        School school = new School("No.1 Middle School", "101 South Street");
        User user = new User(123, "Bob", school);
        // 放入Request中:
        req.setAttribute("user", user);
        // forward给user.jsp:
        req.getRequestDispatcher("/WEB-INF/user.jsp").forward(req, resp);
    }
}

user.jsp中,我们只负责展示相关JavaBean的信息,不需要编写访问数据库等复杂逻辑:

<%@ page import="com.itranswarp.learnjava.bean.*"%>
<%
    User user = (User) request.getAttribute("user");
%>
<html>
<head>
    <title>Hello World - JSP</title>
</head>
<body>
    <h1>Hello <%= user.name %>!</h1>
    <p>School Name:
    <span style="color:red">
        <%= user.school.name %>
    </span>
    </p>
    <p>School Address:
    <span style="color:red">
        <%= user.school.address %>
    </span>
    </p>
</body>
</html>

我们在浏览器访问http://localhost:8080/user,请求首先由UserServlet处理,然后交给user.jsp渲染
在这里插入图片描述

从上面这个例子不难看出项目整个运行流程

  • 需要展示的User被放入HttpServletRequest中以便传递给JSP,因为一个请求对应一个HttpServletRequest,我们也无需清理它,处理完该请求后HttpServletRequest实例将被丢弃;
  • user.jsp放到/WEB-INF/目录下,是因为WEB-INF是一个特殊目录,Web Server会阻止浏览器对WEB-INF目录下任何资源的访问,这样就防止用户通过/user.jsp路径直接访问到JSP页面;
  • JSP页面首先从request变量获取User实例,然后在页面中直接输出,此处未考虑HTML的转义问题,有潜在安全风险。

这样,我们就把从数据库获取数据的业务逻辑留给了Serlvet后台,把输出HTML网页的任务交给了JSP前端。

思考一下,为什么我们能够将一个前后端Servlet分成后端Servlet+前端JSP,其实给人的感觉就是Java跟C语言一起跑一个项目,有点不可思议。其实不是这样的,因为JSP就是Servlet。

我们现在将JSP转化成Servlet,其实就是这样:

<html>
<head>
    <title>Hello World - JSP</title>
</head>
<body>
    <%-- JSP Comment --%>
    <h1>Hello World!</h1>
    <p>
    <%
         out.println("Your IP address is ");
    %>
    <span style="color:red">
        <%= request.getRemoteAddr() %>
    </span>
    </p>
</body>
</html>

JSP对应Servlet

package org.apache.jsp;
import ...

public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent,
               org.apache.jasper.runtime.JspSourceImports {

    ...

    public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
        throws java.io.IOException, javax.servlet.ServletException {
        ...
        out.write("<html>\n");
        out.write("<head>\n");
        out.write("    <title>Hello World - JSP</title>\n");
        out.write("</head>\n");
        out.write("<body>\n");
        ...
    }
    ...
}

是不是看傻了?我再给你看一个东西,我把JSP文件放在tomcat\webapps下

<span style="font-size:14px;"><%@ page contentType="text/html; charset=gb2312" language="java" %>  
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0 Transitional//EN”>  
<HTML>  
<HEAD>  
<TITLE>第一个JSP页面</TITLE>  
</HEAD>  
    <BODY>  
    <%for(int i=0;i<10;i++)  
    {  
        out.println(i);  
        %>  
    <br/>  
        <%}%>  
    </BODY>  
</HTML>

在这里插入图片描述
在这里插入图片描述
所以我们最后的一个总结就是

JSP就是Servlet,通过Servlet + JSP分离前后端代码,实际上是将一个Servlet任务分给了两个Servlet去完成。

MVC框架其实你已经学完了

在这里插入图片描述
Of course,I am 确定,童鞋们真的已经学完了MVC,不信你们看一张图。
在这里插入图片描述
按照鄙人浅薄的项目经历(大二)来说,你以后做的项目确实就是差不过是这样子的流程了,只不过在业务处理那块会复杂一些,把后端业务处理分成controller、Service、Mapper三层架构。

接下来就要开始SpringMVC学习啦!太快了受不了?那你自己去一旁休息一下吧,哥哥是老司机,要带其他童鞋上高速了,dududu~

SpringMVC

前言
我曾经遇到一篇很牛逼的文章,上来直接跟我说SpringMVC组件,看的我两眼发光。我使劲背,又使劲忘,背忘背忘背忘背忘背忘背忘…,最后都忘了背了。所以如果你不看我前面对Servlet精彩的讲解,那你就先不要看这里,因为要讲SpringMVC必须基于ServletMVC

屏幕前的读者可能有一部分已经做过SpringBoot项目了,你们可能会根据自己SpringBoot项目经历结合网上的SpringMVC组件讲解去理解SpringMVC,我个人认为不要这么做,因为SpringBoot你知道的,简化了很多东西。最好从一个原始的SpringMVC项目开始,下面就给你们带来一个完整的SpringMVC原始实例。

从应用入手,我们看看SpringMVC是怎样执行的

  1. 导包包,导个jar包!
<!--测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>
<!--日志-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.21</version>
</dependency>
<!--J2EE-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
<!--mysql驱动包-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.35</version>
</dependency>
<!--springframework-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.github.stefanbirkner</groupId>
    <artifactId>system-rules</artifactId>
    <version>1.16.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>
<!--其他需要的包-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.4</version>
</dependency>
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>
  1. Spring MVC 应用时需要在 web.xml 中部署 DispatcherServlet。

其中我们需要重点注意的是:DispatcherServlet会怎样寻找我们定义的Servlet的配置文件。如果我们使用了<init-param>标签指定了路径,那就使用该路径来查找我们的Servlet配置文件,如果没有就按照默认路径应用程序的 WEB-INF 目录下查找我们的Servlet配置文件。

Servlet 是 DispatcherServlet 类型,它就是 Spring MVC 的入口,并通过 <load-on-startup>1</load-on-startup> 配置标记容器在启动时就加载此 DispatcherServlet,即自动启动。然后通过 servlet-mapping 映射到“/”,即 DispatcherServlet 需要截获并处理该项目的所有 URL 请求。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
    <display-name>springMVC</display-name>
    <!-- 部署 DispatcherServlet -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--配置DispatcherServlet会去哪里找我们自定义的Servlet-->
        <!--如果没有init-param这个标签,那就开始使用默认路径扫描-->
        <init-param>
        	<param-name>contextConfigLocation</param-name>
        	<param-value>classpath:springmvc-servlet.xml</param-value>
    	</init-param>
        <!-- 表示容器再启动时立即加载servlet -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!-- 处理所有URL -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  1. 为我们自定义的Servlet创建配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- LoginController控制器类,映射到"/login" -->
    <bean name="/login"
          class="net.biancheng.controller.LoginController"/>
    <!-- LoginController控制器类,映射到"/register" -->
    <bean name="/register"
          class="net.biancheng.controller.RegisterController"/>
</beans>
  1. 创建Controller
package controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class LoginController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest arg0,
            HttpServletResponse arg1) throws Exception {
        return new ModelAndView("/WEB-INF/jsp/register.jsp");
    }
}
package controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class RegisterController implements Controller {

    public ModelAndView handleRequest(HttpServletRequest arg0,
            HttpServletResponse arg1) throws Exception {
        return new ModelAndView("/WEB-INF/jsp/login.jsp");
    }
}
  1. 创建View

index.jsp:这个文件不要不要不要放在WEB-INF中,还记得我说过不能直接访问WEB-INF里面的内容吗?

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    未注册的用户,请
    <a href="${pageContext.request.contextPath }/register"> 注册</a><br /> 已注册的用户,去
    <a href="${pageContext.request.contextPath }/login"> 登录</a></body>
</html>

WEB-INF 下创建 jsp 文件夹,将 login.jsp 和 register.jsp 放到 jsp 文件夹下

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    登录页面!
</body>
</html>

register.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
<body>
    注册页面!
</body>
</html>
</head>

将 springmvcDemo 项目部署到 Tomcat 服务器。首先访问 index.jsp 页面。

在下图所示的页面中,当用户单击“注册”超链接时,根据 springmvc-servlet.xml 文件中的映射将请求转发给 RegisterController 控制器处理,处理后跳转到 /WEB-INF/jsp 下的 register.jsp 视图。同理,当单击“登录”超链接时,控制器处理后转到 /WEB-INF/jsp下的 login.jsp 视图。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

那么我们来看一下总的流程是什么,顺道用到了SpringMVC的哪些组件

  • 通过web.xml注册DispatcherServlet,并向HandlerMapping发送请求
  • HandlerMapping根据请求返回Controller执行链
  • 通过HandlerAdapter完成Controller的对接。因为SpringMVC中的Handler可以是任意形式,只要能处理请求就可以,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这HandlerAdapter要做的事情。
  • 接下来就是执行controller里面的方法,并且返回一个ModelAndView对象
  • ModelAndView就是指定的.jsp,我们需要解析然后返回给view
  • 最后DispatcherServletview返回给浏览器

在这里插入图片描述

从原理入手,我们看看SpringMVC是怎样执行的

其实你又已经学完了SpringMVC原理了,因为上面已经从实例出发,一步步跟你讲解了SpringMVC的运行原理,我相信如果我在这里单独讲原理你是绝对不会看第二遍,甚至第一遍就粗略看一下而已。所以恭喜你,整个Web服务,你已经入门了。

补充:SpringMVC拦截器

1. 实现HandlerInterceptor接口
preHandle:预处理回调方法,实现处理器的预处理(如登录检查),第三个参数为响应的处理器返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;

postHandle:后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。

afterCompletion:整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中preHandle返回true的拦截器才会执行afterCompletion。

public class MyHandlerInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("MyHandlerInterceptor->preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        System.out.println("MyHandlerInterceptor->postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("MyHandlerInterceptor->afterCompletion");
    }

}

2. 创建Controller示例

@Controller
@RequestMapping("/index")
public class LoginControl {
    @RequestMapping(value = "/login")
    public String login(){
        System.out.println("LoginControl->login");
        return  "login";
    }
    @RequestMapping(value = "/test")
    @ResponseBody
    public String test(){
        System.out.println("LoginControl->test");
        return "test";
    }
}

3. 配置拦截器

 <!-- 配置拦截器:-->
    <mvc:interceptors>
        <!-- 可以配置多个拦截器  也可以配置bean 拦截器 拦截所有请求 -->
        <mvc:interceptor>
            <!-- 只拦截该路径 -->
            <mvc:mapping path="/**/login"/>

            <bean class="com.lucas.controller.MyHandlerInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

在这里插入图片描述

补充:SpringMVC常用注解

@RequestMapping

value: 指定接收的路径
method: 接收什么请求(get repost…)
params: 对参数的限制
headers: 浏览器的请求头
@RequestMapping(value={"/add","/add2","/add3"},
        method = RequestMethod.GET, // 接收的请求方法
        params = {"!name","address=beijing","age!=22"}, // 参数不能含有name,必须有address=beijing且age!=22
        headers = {"User-Agent=Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)"})
public String testA(Model model, User user){
    return "success";
}

@RequestParam

name: 浏览器中的key
defaultValue: 默认值
required: 参数是否必须传
//http://localhost:8080/springMvc_01/testB?i=&name
@RequestMapping(value = "testB")
public String testB(Model model, @RequestParam(defaultValue = "0") int i, @RequestParam(required = false, defaultValue = "没有值", name = "name") String userName){
    model.addAttribute("message", i + userName);
    return "success";
}

@RequestHeader
获取请求头信息

@RequestMapping("testC")
public String test(Model model, @RequestHeader(name = "cache-control") String cacheControl){
    // 向前端传回数据, request域
    model.addAttribute("message", "请求被成功响应");
    System.out.println(cacheControl);
    //最终到视图解析器中加上前缀和后缀
    return "success";
}

@CookieValue
获取Cookie

@RequestMapping("/add03")
public String addPerson03(Model model,@CookieValue(name = "JSESSIONID") String cookie){
    System.out.println("cookie="+cookie);
    model.addAttribute("message","访问成功");
    return "success";
}

@RequestBody
接收前端传过来的json对象

前端必须传来的是json格式的字符串
必须发送post请求
@RequestMapping("/add04")
@ResponseBody
public String addPerson04(@RequestBody User user){
    System.out.println(user);
    return "success";
}

@ResponseBody
向前端发送 jackson对象,需要导入依赖 jackson-databind

@RequestMapping("/getPersonById")
@ResponseBody
public User getPersonById(){
    User user = new User();
    user.setName("wy");
    user.setAge(12);
    return user;
}

@PathVariable
取出请求中占位符对应的参数

@RequestMapping("/deletePersonById/{id}/{page}")
public String deletePersonById(Model model,@PathVariable("id") int id,@PathVariable("page") String page){

    System.out.println("id="+id);
    model.addAttribute("message","删除成功");
    return page;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值