你只会说MVC模型是什么但是不会实现?今天带你走通Web、Servlet、MVC、SpringMVC。代码演示很清晰
文章目录
- HTTP请求和HTTP响应
- 从0手写一个Web服务器,看看能有多累人
- 使用Servlet实现一个服务器,看看多简单
- Serlvet的创建
- Servlet的运行
- Servlet的其他问题
- Servlet这么爽,我们简单地探索一下它的原理
- JSP跟Servlet合作啦,我们来看一下他们能干点啥?
- MVC框架其实你已经学完了
- SpringMVC
- 从应用入手,我们看看SpringMVC是怎样执行的
- 从原理入手,我们看看SpringMVC是怎样执行的
- 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,就可以访问了
-
运行Maven命令mvn clean package,在target目录下得到一个hello.war文件,这个文件就是我们编译打包后的Web应用程序。
-
普通的Java程序是通过启动JVM,然后执行main()方法开始运行。但是Web应用程序有所不同,我们无法直接运行war文件,必须先启动Web服务器,再由Web服务器加载我们编写的HelloServlet,这样就可以让HelloServlet处理浏览器发送的请求。
-
无论使用哪个服务器,只要它支持Servlet API 5.0(因为我们引入的Servlet版本是5.0),我们的war包都可以在上面运行。这里我们选择使用最广泛的开源免费的Tomcat服务器。
-
把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.
- 在浏览器输入http://localhost:8080/hello/即可看到HelloServlet的输出:
Servlet的其他问题
- 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
版本,这一点针对不同项目要特别注意。
- 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
中,我们可以从数据库读取User
、School
等信息,然后,把读取到的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.HttpJspBaseimplements 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
必须基于Servlet
跟 MVC
屏幕前的读者可能有一部分已经做过SpringBoot项目了,你们可能会根据自己SpringBoot项目经历结合网上的SpringMVC组件讲解去理解SpringMVC,我个人认为不要这么做,因为SpringBoot你知道的,简化了很多东西。最好从一个原始的SpringMVC
项目开始,下面就给你们带来一个完整的SpringMVC原始实例。
从应用入手,我们看看SpringMVC是怎样执行的
- 导包包,导个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>
- 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>
- 为我们自定义的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/beanshttp://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>
- 创建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");}
}
- 创建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
。- 最后
DispatcherServlet
将view
返回给浏览器
从原理入手,我们看看SpringMVC是怎样执行的
其实你又已经学完了SpringMVC原理了,因为上面已经从实例出发,一步步跟你讲解了SpringMVC的运行原理,我相信如果我在这里单独讲原理你是绝对不会看第二遍,甚至第一遍就粗略看一下而已。所以恭喜你,整个Web服务,你已经入门
了。
SpringMVC常用注解
参考文章
这里根据自己需求记录笔记,我这里就只记录自己不熟悉的注解好了
@RequestMapping
RequestMapping注解有六个属性
- value:指定请求的实际地址
- method: 指定请求的method类型, GET、POST、PUT、DELETE等
- consumes:指定处理请求的提交内容类型
- produces:指定返回的内容类型
- params: 指定request中必须包含某些参数值是,才让该方法处理
- headers:指定request中必须包含某些指定的header值,才能让该方法处理
@PathVariable
@RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET) public String getLogin(@PathVariable("userId") String userId, @PathVariable("roleId") String roleId){}
@RequestParam
@RequestMapping(value="/dept/get/{id}",method = RequestMethod.GET) public String getLogin(@RequestParam(value = "id") int id, @PathVariable("roleId") String roleId){}
相关文章:
你只会说MVC模型是什么但是不会实现?今天带你走通Web、Servlet、MVC、SpringMVC。代码演示很清晰
文章目录HTTP请求和HTTP响应从0手写一个Web服务器,看看能有多累人使用Servlet实现一个服务器,看看多简单Serlvet的创建Servlet的运行Servlet的其他问题Servlet这么爽,我们简单地探索一下它的原理JSP跟Servlet合作啦,我们来看一下他…...
C++中邻接矩阵、邻接表、链式前向星具体用法及讲解
图论在提高组中几乎占据半壁江山,而今天要讲的就是如何存储一个图一.邻接矩阵原理要建立一个图,根本的要素就是边和点而想要让计算机存储边和点就需要用到一些数据结构邻接矩阵是最简单的他使用了一个二维数组,来表示一个图假设数组名为map那…...
appium的安装详解
安装appium 爬虫手机APP需要实现自动化,所以要使用appnium来实现点击,输入,滑动等操作。由于appnium的安装较为繁琐,所以特意整理一篇文章来展示安装的详细过程过程中。 安装appnium共有3个步骤 安装 Android SDK安装 JDK安装 …...
STM32之 串口
串口通信串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方 式的扩展接口。串行接口(Serial Interface)是指数据一位一位地顺序传送。其特点是通信线路简 单,只…...
CSDN 编程竞赛三十三期题解
竞赛总览 CSDN 编程竞赛三十三期题解:比赛详情 (csdn.net) 竞赛题解 题目1、奇偶排序 给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数(奇数和偶数的顺序根据输入的数字顺序排列)。 第七期竞赛…...
逆向练习之 mingyue.exe wp
目录 一.查壳 二.主函数 三.operate函数 四.storage函数及4618和4620指针功能的解释 五.judge函数 六.求解flag 七.其他--ida字符识别问题 一.查壳 64位无壳 二.主函数 1.这里的pointer_4618和4620是两个相邻的八字节内存单元,其中4620是字符串链表表头head 2.puts和s…...
LeetCode 热题 HOT 100 Java 题解 -- Part 3
练习地址 Part 1 : https://blog.csdn.net/qq_41080854/article/details/128829494 Part 2 : https://blog.csdn.net/qq_41080854/article/details/129278336 LeetCode 热题 HOT 100 Java 题解 -- Part 376. 最佳买卖股票时机含冷冻期77. 戳气球78. 零钱兑换79. 打家劫舍 III…...
QML键盘事件
在QML中,当有一个按键按下或释放时,会产生一个键盘事件,将其传递给获得有焦点的QML项目(讲focus属性设置为true,则获得焦点)。 按键处理的基本流程: Qt接收密钥操作并生成密钥事件。如果 QQuic…...
跨域问题怎么解决
解决跨域,原因:域名不同,域名相同端口不同;二级域名不同 什么是跨域? 就是两个项目之间通讯,如果访问的域名与ajax访问的地址不一致情况,默认情况浏览器有一个安全机制。 postman不一定能测试…...
微服务网关Gateway和Zuul的区别
spring-cloud-Gateway是spring-cloud的一个子项目。而zuul则是netflix公司的项目,只是spring将zuul集成在spring-cloud中使用而已。 因为zuul2.0连续跳票和zuul1的性能表现不是很理想,所以催生了spring团队开发了Gateway项目。 Zuul: 使用的…...
专访华西二院吴邦华:隐私计算+AI全栈技术,构筑智慧医院建设的坚实数据底座|爱分析访谈
从IT时代步入DT时代,医疗大数据成为智慧医院建设的重要驱动力。经过多年信息化系统建设,很多医院已经积累了大量的医疗数据资源,但由于各业务系统间数据孤岛化严重、系统架构落后、数据缺乏深度治理等问题存在,导致现有数据深度及…...
《C++ Primer Plus》第18章:探讨 C++ 新标准(6)
可变参数模板 可变参数模板(variadic template)让您能够创建这样的模板函数和模板类,即可接收可变数量的参数。这里介绍可变参数模板函数。例如,假设要编写一个函数,它可接受任意数量的参数,参数的类型只需…...
.Net Core中使用是SQL Server的邮件发送功能
.Net Core中使用是sqlserver的邮件发送功能准备需求启用SQL Server的电子邮件功能检查和测试在.net Core中调用在sqlsrver的管理中有一个数据库邮件功能,再此可以使用sqlserver来自动发送一些邮件,但是有一些需要插入附件的邮件则需要使用程序代码来解决,下面就是使用C#来调用s…...
Nginx优化服务和防盗链
Nginx优化服务和防盗链一、长连接1、修改主配置文件2、测试3、在主配置文件添加4、验证二、Nginx第三方模块1、开源的echo模块2、查看是否成功3、加echo模块步骤4、网页测试验证三、搭建虚拟主机1、编译安装好nginx后,对主配置文件进行修改2、创建文件3、验证四、防…...
B树与B+树
认识了解MySQL中的B树B树引出什么是B树什么是B树B树的优点B树引出 在MySQL中,如果我们设置了主键, 那么对于该列表中的数据就有了一个索引,插入表中数据的主键值不能重复,而且不能为空. 那当我们插入数据的时候, 它是如何通过索引来判断主键值是否重复的呢? 我们想到它肯定是…...
QEMU网络配置
文章目录1. 前言2. 测试环境3. 配置步骤3.1 host 配置3.1.1 检查 host 对 TUN/TAP 和 网桥的支持情况3.1.2 网桥一端的建立:创建网桥设备,并添加 host 网卡到网桥3.1.3 网桥另一端的建立:TUN/TAP 配置3.2 guest 端的配置4. 参考链接1. 前言 …...
windows安装tomcat
这里写自定义目录标题tomcat官网下载安装包并解压环境变量配置启动tomcat访问http://localhost:8080/修复启动出现乱码问题tomcat官网下载安装包并解压 环境变量配置 系统环境变量新增: 变量名:CATALINA_HOME 变量值:tomcat的安装目录 编辑…...
刷题记录:牛客NC23051华华和月月种树 树链剖分+离线加点
传送门:牛客 题目描述: 华华看书了解到,一起玩养成类的游戏有助于两人培养感情。所以他决定和月月一起种一棵树。因为华华现在也是信息学高手了,所以他们种的树是信息学意义下的。 华华和月月一起维护了一棵动态有根树,每个点有一个权值。刚…...
年薪20W软件测试工程师必备的6大技能(建议收藏)
软件测试 随着软件开发行业的日益发展,岗位需求量和行业薪资都不断增长,想要入行的人也是越来越多,但不知道从哪里下手,今天,就给大家分享一下,软件测试行业都有哪些必会的方法和技术知识点,作…...
【存储】RAID2.0+、多路径技术、磁盘可靠性技术
RAID2.0RAID 2.0技术RAID技术发展RAID 2.0软件逻辑对象RAID 2.0基本原理硬盘域Storage Pool & TierDisk Group(DG)LD(逻辑磁盘)Chunk(CK)Chunk Group(CKG)ExtentGrainVolume &am…...
Vue 2
文章目录1. 简介2. 第一个Vue程序3. 指令3.1 判断循环3.2 操作属性3.3 绑定事件3.4 表单中数据双向绑定3.5 其他内置指令3.6 自定义指令4. 组件4.1 全局注册4.2 局部注册4.3 组件通讯4.4 单文件组件5. 组件插槽5.1 单个插槽5.2 具名插槽5.3 作用域插槽6. 内置组件6.1 component…...
Ubuntu 安装 Docker Engine
【参考】Install Docker Engine on Ubuntu | Docker Documentation: https://docs.docker.com/engine/install/ubuntu/ 【参考】Docker CE 镜像源站-阿里云开发者社区 https://developer.aliyun.com/article/110806 【规范】模仿 Docker 文档,Ubuntu, Docker 首字母…...
SpringBoot入门 - 添加内存数据库H2
上文我们展示了通过学习经典的MVC分包结构展示了一个用户的增删查改项目,但是我们没有接入数据库;本文将在上文的基础上,增加一个H2内存数据库,并且通过Spring 提供的数据访问包JPA进行数据查询。准备知识点在介绍通过Spring JPA接…...
高质量数字化转型创新发展大会暨中国信通院“铸基计划”年度会议成功召开
2023年3月3日,由中国信通院主办的高质量数字化转型创新发展大会暨中国信通院“铸基计划”年度会议在北京成功召开。本次大会深度展示了中国信通院在数字化领域的工作成果,并全面展望了2023年行业的数字化发展趋势。同时,大会发布了中国信通院…...
2023年如何通过软考初级程序员?
初级的考试难度不大,稍微有点编程基础,认真备考应该没什么大问题。 先清楚大纲: 高效备考!理清考点,针对性复习 科目一:综合知识 75道单项选择题,1题1分,时长150分钟;…...
视频自动播放的实现与问题解决
一、前言 页面加载一个视频并且自动播放,这个需求看起来非常简单,实现起来感觉也非常简单;但是,实际做起来还是有几处容易产生问题的地方卡住进度。本文讨论基于Vue3的项目在实现页面加载视频后的自动播放遇到的几个问题。 二、页面实现 页面实现非常简单。在页面上放置一个…...
ThreadLocal 理解及面试
一、ThreadLocal 引用关系 图解关系说明: 每个线程拥有自己的 ThreadLocalMap 属性;ThreadLocalMap 的存储结构为 Entry[] 数组;Entry的Key是ThreadLocal类型且弱引用指向ThreadLocal对象,Value是我们自己定义的泛型值对象&#…...
巾帼绽芬芳 一起向未来(中篇)
编者按:为了隆重纪念纪念“三八”国际妇女节113周年,快来与你全方位、多层次分享交流“三八”国际妇女节的前世今生。分上篇(节日简介、节日发展和节日意义)、中篇(节日活动宗旨和世界各国庆祝方式)和下篇&…...
Qt学习2-Qt Creator新建项目小tips(哔站视频学习记录)
放送两个小tips: 1、MinGW和MSVC的区别 QT学习笔记(二):QT MinGW 和 MSVC 编译方式_Leon_Chan0的博客-CSDN博客 2、如何安装QT对应版本的MSVC (1)问题描述:Qt5.12.8支持MSVC2015和MSVC2017,但是系统安装的是Visual…...
React-高阶组件
认识高级组件 高阶函数的维基百科定义:至少满足以下条件之一 1、接受一个或多个函数作为输入; 2、输出一个函数; JavaScript中比较常见的 filter、map、reduce 都是高阶函数 那么说明是高阶组件呢? 高阶组件的英文是 Higher-Order Components,简称为 HOC;官方的…...
大连精美网站制作/百度代发排名
北京时间 2019 年 10 月 9 日,微软发布了全新的 VS Code Python 插件,带来了众多更新!其中,最大的更新就要属万众期待的 Jupyter Notebook 原生支持了!我们来看看这次 Jupyter Notebook 的原生支持带来了哪些好用的功能…...
wordpress免费模版安装/aso苹果关键词优化
java.io类InputStreamjava.io.InputStream所有已实现的接口:直接已知子类:public abstract classInputStreamextendsObjectimplementsCloseable此抽象类是表示字节输入流的所有类的超类。需要定义InputStream子类的应用程序必须总是提供返回下一个输入字…...
网站 空间转移/网站收录查询代码
1990年,一年期的存款基准利率是10.08% ,现在一年期的存款基准利率是1.5% 你觉得这个利率低,其实其他国家更低。目前,英国央行基准利率为0.75%,瑞士央行基准利率为-0.75%,日本央行基准利率只有-0.1%。 从这些…...
中企动力做网站的优势/网站快速排名公司
在Android编程的过程中,如果在Activity中某个操作会运行比较长的时间,比如:下载文件。这个时候如果在主线程中直接下载文件,会造成Activity卡死的现象;而且如果时间超过5秒,会有ANR报错。在这种情况下&…...
用6数字域名做网站的是/深圳网站提升排名
用 64bit 的 Python 调用 32bit 的 Dll 会出错 转载于:https://www.cnblogs.com/blog-3123958139/p/5574501.html...
合肥做网站优化/互联网营销师是干什么
4.1 Scala数组 数组是最常用的收集一系列元素的结构数组的声明与其他数值类型声明类似定长数组声明 val A new Array [T] (N) val Anew Array [Int] (10)数组声明时若不给出值,会被初始化,初始化后同样能对数组成员进行赋值 数组成员初始化,…...