核心类、常用注解与RESTfulDispatcherServletDispatcherServlet全称org.springframework.web.servlet.DispatcherServlet。DispatcherServlet并不直接处理请求，它只负责根据请求的信息把请求转发给合适的处理器，然后由处理器来执行实际的处理过程并生成响应，它是Spring MVC框架的入口点，它将所有这些步骤组合在一起，使得开发者可以更轻松地构建Web应用程序并处理客户端请求。每个Spring MVC应用程序通常只有一个。我们可以在web.xml里进行配置： springmvc org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:springCtx.xml 1 springmvc /springmvc/* 上面例子中所有以/springmvc开头的请求都会被名称为springmvc的DispatcherServlet处理。另外，在上述代码中，有两个可选项 和 。 如果元素的值为1，则启动程序的时候会立刻加载Servlet；如果元素不存在，则在第一个请求时加载。 存在并配置了路径，则会根据路径寻找配置文件，否则将导WEB-INF下寻找servletName-servlet.xml命名形式的文件。ViewResolverViewResolver即视图解析器，我们可以在springCtx.xml文件中配置它。回顾之前的springCtx.xml<?xml version="1.0" encoding="UTF-8"?> 再来看看之前的controllerpackage top.cairbin.test7.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 TestController implements Controller{ @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { // 创建ModelAndView对象 ModelAndView mav = new ModelAndView(); // 向模型对象中添加数据 mav.addObject("message", "Hello Spring"); // 设置逻辑视图名 mav.setViewName("/WEB-INF/views/hello.jsp"); // 返回ModelAndView对象 return mav; } }我们在控制器中每次都要指定jsp格式文件和/WEB-INF/views/路径是一件很头疼的事，所以可以修改视图解析器的配置来帮助我们操作。 <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".jsp" /> @Controller、@RequestParam与映射相关的注解@Controller在程序中我们可以使用@Controller来声明这是一个控制器用于依赖注入，而无需去实现Controller接口。@RequestMapping@RequestMapping注解可以很方便地让我们设置类或者方法对应的URL路径，只需要将它们写在类或者方法上面即可例如[...] @Controller @RequestMapping("/myController") class MyController{ [...] }[...] @Controller @RequestMapping("/myController") class MyController{ [...] @RequestMapping("/myFunc1") public MyType MyFunc1([...]){ [...] } //还可以指定路径和请求类型(例如GET) @RequestMapping(path="/myFunc2", method=RequestMethod.GET) public MyType MyFunc2([...]){ [...] } //还有URI模式，路径可随参数变化。 //比如请求某些文章，都有各自ID，它们对应的方法都是这个，路径仅有id不一样，返回对应内容，就可以这样写，此时这里的param对应文章id。 //还可以指定路径和请求类型(例如GET) @RequestMapping(path="/{param}}", method=RequestMethod.GET) public MyType MyFunc3(@PathVariable ParamType param, [...]){ doForParam(param); [...] } }需要注意@RequestMapping有两种属性可以指定映射路径，分别是value和pathvalue被指定会覆盖默认值，path也一样，二者也都能匹配占位符。实际上path就是value的别名，没有区别。另外使用占位符的URL路径需要对参数添加注解@PathVariable组合注解为了更方便地指定Http请求方法类型，Spring也为我们定义了一些组合注解。@GetMapping匹配Get请求@PostMapping匹配Post请求@PutMapping匹配Put请求@DeleteMapping匹配Delete请求@PatchMapping匹配Patch请求至于这些请求是干什么用的，分别对应什么场景，因为并不属于主要内容，此处不过多描述，请查阅HTTP协议文档。（尽管如此，你必须对这些方法及HTTP语义有一定了解）@RequestParam@RequestParam注解为请求指定参数，用法如下。这是Controller里的一个方法@RequestMapping("/hello") public ModelAndView showMessage(@RequestParam(value = "name", required = false, defaultValue = "Spring") String name) { ModelAndView mv = new ModelAndView("hello");//指定视图 mv.addObject("message", message); mv.addObject("name", name); return mv; }实践与test7一样，创建webapp类型的maven项目top.cairbin.test8。（如果不会请返回上一节）在pom.xml里引入依赖文件。 javax.servlet javax.servlet-api provided 3.1.0 org.springframework spring-core 5.3.23 org.springframework spring-web 5.3.23 org.springframework spring-webmvc 5.3.23 在web.xml里进行配置<?xml version="1.0" encoding="UTF-8"?> springmvc org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:springCtx.xml 1 springmvc / 在src/main/resources里创建springCtx.xml配置文件这里与test7就不一样了，我们指定jsp文件的前后缀，并利用自动扫描来帮助我们完成控制器以及其他类的注入。<?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" 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"> <context:component-scan base-package="top.cairbin.test8" /> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".jsp" /> 请注意自动扫描的路径与你项目是否一致。另外，因为分层设计，我们在之后肯定不会仅在controller层进行依赖注入，在其他层也会，我们创建的包名都是top.cairbin.test8.xxx，也就是说都是top.cairbin.test8的子包，所以这里指定top.cairbin.test8进行扫描即可。在WEB-INF文件夹下创建views文件夹，并在views下创建hello.jsp文件<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ page isELIgnored="false"%> hello Hello, ${name}! 接下来创建top.cairbin.test8.controller包，包下创建类MyController，并将控制器映射到/hello路径下，将sayHello方法映射到/hello/sayHello/{name}路径，其中{name}是一个占位符，随用户提供的参数而定，让hello.jsp模板渲染，并显示hello,后面跟这个参数的值。package top.cairbin.test8.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; @Controller @RequestMapping("/hello") public class MyController { @GetMapping(path="/sayHello/{name}") public ModelAndView sayHello( @PathVariable String name) throws Exception { //指定视图 ModelAndView mv = new ModelAndView("hello"); //向视图中添加所要展示或使用的内容，将在页面中使用 mv.addObject("name", name); return mv; } }我们来运行一下，右键项目->Run as...->Run on server选择你的Tomcat服务器并运行，这里有问题同样返回上一节。浏览器访问http://localhost:8080/test8/hello/sayHello/CairBin我们来看看在URL里不用占位符的方式传参是怎么样的。在MyController里添加一个方法@GetMapping(path="/sayHello2") public ModelAndView sayHello2( String name) throws Exception { //指定视图 ModelAndView mv = new ModelAndView("hello"); //向视图中添加所要展示或使用的内容，将在页面中使用 mv.addObject("name", name); return mv; }在浏览器访问http://localhost:8080/test8/hello/sayHello2我们发现并没有任何数据。换这个URL试试http://localhost:8080/test8/hello/sayHello2?name=cairbinamazing，竟然有了。实际上这对应两种不同的风格。你可能迷茫了，我到底以什么方式传递参数，接下来将具体说明。RESTful与RPC风格请容许我从网上找一张图片学过网络的都知道，TCP/IP模型的应用层实际上对应OSI的应用层、表示层和会话层。对于我们WEB使用的HTTP协议来讲，它是应用层协议。但是在上古年代，人们还在为网络世界里不同机器之间的通信方式而头疼，而最基础的方式是通过socket编程来实现的，不过这个难度有些大，人们迫切需要一个对新手友好且简单的方式。于是，1984年，有位大佬叫 Bruce Jay Nelson，发表了一篇论文叫做 Implementing Remote Procedure Calls，定义了一种标准叫RPC。RPC希望客户端可以像调用本地接口一样来调用服务端，RPC模式分为三层，RPCRuntime 负责最底层的网络传输，Stub 处理客户端和服务端约定好的语法、语义的封装和解封装，这些调用远程的细节都被这两层搞定了，用户和服务器这层就只要负责处理业务逻辑，调用本地 Stub 就可以调用远程。也就是说RPC可以基于TCP/UDP传输层协议传输，也可以使用它们的上层协议HTTP。也正如此HTTP更关注服务提供方，对于客户端怎么调用是不关心的；而对于RPC还要满足更多的东西，也就是需要客户端接口和服务端保持一致（客户端可以像调用本地接口一样来调用服务端）。这种API接口的设计风格就被称为RPC风格。而对于REST，中文描述表述性状态传递，实际上主要关注资源定位，REST是把服务端方法写好，客户端不想知道方法，只想获取资源，这与HTTP语义天然一致。所以从设计上看，RPC是面向方法的，REST是面向资源的。总的来说，RPC通常是服务器和服务器之间的通信，比如和中间件的通信，MQ、分布式缓存、分布式数据库等等。而REST通常是面向客户端的（一般是浏览器）。使用场景不一样。因此你在设计Web的时候两种接口风格实际上都会用到，而对于浏览器来讲，我们为方便实现客户端与服务器的缓存等功能，还是尽量用RESTful风格比较好。但是RESTful，它毕竟只是一种风格，或者说是一种建议，即使你不按照它来设计或者说“接口不够RESTful”也符合规范，但是我们为了尽量靠拢HTTP语义，还是尽量去这样做。接下来我们对比两种风格差异，分别以CRUD为例。RPChttp://127.0.0.1/article/query?id=1 查询文章(GET) http://127.0.0.1/article/add 新增文章(POST) http://127.0.0.1/article/update 更新文章(POST) http://127.0.0.1/article/delete?id=1 删除文章(GET或POST) RESTfulhttp://127.0.0.1/article/1 查询文章(GET) http://127.0.0.1/article 新增文章(POST) http://127.0.0.1/article 更新文章(PUT) http://127.0.0.1/article/1 删除文章(DELETE)
JSP语法入门前提在前一节中我们已经写过JSP的代码了，这一节将单独介绍JSP一些基础语法。当然，你可以跳过这一节，当后面有代码不太理解的时候再回来阅读。中文编码问题如果中文乱码，看看JSP是否是以UTF8的方式编码，使用此编码在JSP文件最上方需要添加下面内容<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>脚本程序脚本程序是包含任意Java语句、变量、方法或表达式。形式如下：<% 脚本程序代码片段 %>我们借助上一节的top.cairbin.test7，稍微对hello.jsp改造下看看效果。<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@page isELIgnored="false" %> Hello SpringMvc <% out.println("Hello JSP"); %> 按照上述逻辑来讲应该会在页面显示一个h1的标题，内容为Hello JSP我们访问http://localhost:8080/test7/hello来看看，果然如此。哦，对了，在写语句的时候别忘了那该死的分号。你也可以选择以下等价的XML语句（仍是在JSP文件里写，这里对其他部分省略，用[...]表示）[...] <jsp:scriptlet> out.println("Hello JSP"); </jsp:scriptlet> [...]你大致已经知道JSP书写方式了，为了简洁，在接下来的介绍中除非特殊我们将不再给出演示结果，而是仅介绍用法，请自行在项目中进行测试。JSP表达式表达式先被转化成String，然后被插入到该出现的地方。由于表达式的值会被转化成String，所以您可以在一个文本行中使用表达式而不用去管它是否是HTML标签。形式如下<%= 这里写jsp表达式，注意前面那个等号 %>它也有等价的XML形式<jsp:expression> 表达式 </jsp:expression>示例，注意这里不能用分号结尾 日期 <%= (new java.util.Date()).toLocaleString() %> JSP声明JSP声明可以声明一个或多个变量、方法等供后面代码使用。JSP声明形式<%! 这里写声明 %>等价的XML<jsp:declaration> 声明的代码片段 </jsp:declaration>示例<%! int num = 114514; %>JSP注释<%-- 我就是JSP注释，此处不会被页面展示，也不会被浏览器看到 --%> 你可以在JSP文件中使用JSP注释和HTML注释，但应当注意JSP注释与HTML有些不同，JSP注释不会被发送到浏览器，而HTML注释会被发送至浏览器并且能在浏览器查看源代码功能中看到！！！在HTML注释中不要写敏感信息！！！JSP指令形式如下<%@ 指令 属性="值" %>经常用到的JSP指令标签有：<%@ page ... %>定义页面依赖属性，比如脚本语言、error页、缓存需求、编码等等<%@ include ... %>包含其他文件<%@ taglib ... %>引入标签库的定义，可以自定义标签对于page常用属性有：language声明当前页面脚本的语言，默认为javaextends指定jsp编译成servlet之后所需要继承的类或者所实现的接口import用于导入当前脚本中可能使用到的其他包里面的类info包含jsp的信息，一般作为当前jsp文件的说明用。可以通过getServletInfo()来获取errorPage指定当前jsp文件发生错误时，自动调用改属性值指定的jsp文件。如果不指定当前属性值，当发生错误时，会抛出异常信息给客户。contentType指定生成网页的文件格式和编码字符集isErrorPage用于指定当前jsp文件是否为错误处理jsp文件JSP行为JSP行为使用XML语法结构控制servlet引擎。它需要严格遵守XML标准：<jsp:行为名称 属性="值" />jsp:include用于在当前页面中包含静态或动态资源jsp:useBean寻找和初始化一个JavaBean组件jsp:setProperty设置JavaBean组件的值jsp:getProperty将JavaBean组件的值插入到output中jsp:plugin用于在生成HTML页面中包含Apple和JavaBean对象jsp:element动态创建一个XML元素jsp:forward从一个JSP文件向另一个文件传递一个包含用户请求的request对象jsp:attribute定义动态创建的XML元素属性jsp:body定义动态创建的XML元素的主体jsp:text用于封装模板数据JSP隐式对象JSP有九个无需额外声明或初始化的对象:request: HttpServletRequest类的实例，代表 HTTP 请求的对象，包含客户端发送到服务器的信息，如表单数据、URL参数等。response: HttpServletResponse类的实例，代表 HTTP 响应的对象，用于向客户端发送数据和响应。out: JspWriter类的实例，用于向客户端输出文本内容的对象，通常用于生成HTML。session: HttpSession类的实例，代表用户会话的对象，可用于存储和检索用户特定的数据，跨多个页面。application: ServletContext类的实例，代表 Web 应用程序的上下文，可以用于存储和检索全局应用程序数据。config: ServletConfig类的实例，包含有关当前 JSP 页面的配置信息，例如初始化参数。pageContext: PageContext类的实例，提供对JSP页面所有对象以及命名空间的访问page: 类似于 Java 类中的 this 关键字，代表当前 JSP 页面的实例，可以用于调用页面的方法。exception: exception 类的对象，代表发生错误的 JSP 页面中对应的异常对象，用于处理 JSP 页面中的异常情况，可用于捕获和处理页面中发生的异常。控制流语句判断语句<%! int num=114514; %> <% if(num==114514) { %> 114514 <% }else{ %> 不是114514 <% } %>switch语句switch语句与判断语句不一样，switch语句整个都被包裹在<% %>之中当然，别忘了break。<%! int num=114514; %> <% switch(num){ case 114514: out.println("114514"); break; default: out.println("不是114514"); } %>for语句<% for(int fontSize=1;fontSize<=5;fontSize++){ %> <font color="blue" size="<%= fontSize %>"> fontSize = <%= fontSize %> <%}%>输入出结果如下while与do...while语句while语句与do...while语句类似，这里仅演示while语句<%! int fontSize=1; %> <% while(fontSize<=5){ %> <font color="blue" size="<%= fontSize %>"> fontSize = <%= fontSize %> <% fontSize+=1; %> <%}%>JSP字面量JSP定义了如下几个字面量布尔类型boolean整型int浮点型float字符串string，单引号包裹或者双引号包裹NullJSP运算符JSP支持Java所有逻辑和算术运算符，这里不详细介绍。
Spring MVC项目创建什么是Spring MVCSpring MVC是Spring内置的，实现了Web MVC设计模式的框架。它解决了Web开发过程中很多的问题，例如参数接收、表单验证等。另外它采用松散耦合可插拔组件等结构，具有相对较高的灵活性和扩展性。Spring MVC官方文档可参考 docs.spring.io。Spring MVC执行流程按顺序可分为如下流程用户请求被DispatcherServlet进行拦截处理DispatcherServlet收到请求调用HandlerMappingHandlerMapping找到具体的处理器，生成处理器对象及处理器拦截器，再一起返回给DispatcherServletDispatcherServlet调用HandlerAdapterHandlerAdapter经过适配调用具体的处理器Controller执行完成返回ModelAndView对象HandlerAdapter将ModelAndView返回给DispatcherServletDispatcherServlet将ModelAndView传给ViewResloverViewReslover解析ModelAndView后返回View（给DispatcherServletDispatcherServlet根据View进行渲染DispatcherServlet响应View给用户通过上面流程可知，程序员需要配置DispatcherServlet，并开发View和Controller/Handler。项目创建即然如此，我们就来创建一个Spring MVC项目。打开Eclipse，创建一个Maven项目（想必经过前面学习已经很熟了），项目名称top.cairbin.test7。但是需要注意，创建时请勾选下图的Add project(s) to working set在Select an Archetype这一步需要注意，我们不再用之前的maven-archetype-quickstart，而是maven-archetype-webapp，如下图所示创建完成后项目目录结构大概如下我们还需要引入Spring MVC的依赖包以及Servlet，这你很清楚应该在pom.xml里配置 javax.servlet javax.servlet-api provided 3.1.0 org.springframework spring-core 5.3.23 org.springframework spring-web 5.3.23 org.springframework spring-webmvc 5.3.23 最终效果看起来是这样 4.0.0 top.cairbin test7 war 0.0.1-SNAPSHOT test7 Maven Webapp http://maven.apache.org junit junit 3.8.1 test javax.servlet javax.servlet-api provided 3.1.0 org.springframework spring-core 5.3.23 org.springframework spring-web 5.3.23 org.springframework spring-webmvc 5.3.23 test7 相比于以往的项目，你会发现webapp已经为我们创建了一个resources资源文件夹，并且src/main下多了一个webapp目录。webapp它一般存放前端相关的文件。而webapp目录下有一个WEB-INF，里面有一个默认生成的web.xml配置文件。还记得我们刚才提到的Spring MVC执行流程吗，用户请求会被DispatcherServlet拦截，它叫前端控制器，而这个XML文件就是用来对它进行设置的。我们对web.xml进行修改<?xml version="1.0" encoding="UTF-8"?> springmvc org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:springCtx.xml 1 springmvc / 注意 classpath:springCtx.xml 这一段引入了一个文件，是用来配置Controller映射信息的。我们再来看看，webapp下还有一个index.jsp文件，它正是你的前端代码文件，它的内容如下 Hello World! 你可能对此表示疑惑，这不就是一个HTML文件，现实一个标题，标题内容为Hello World!吗。没错，但是对于传统的HTML来讲页面是静态的，而我们的JSP文件则能动态获取后端一些内容。有了View来实现USL前台还不够，我们还要实现USL后台，接下来我们在src/main/java下创建包top.cairbin.test7.controller，并在这个包下创建控制器类TestControllerpackage top.cairbin.test7.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 TestController implements Controller{ @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { // 创建ModelAndView对象 ModelAndView mav = new ModelAndView(); // 向模型对象中添加数据 mav.addObject("message", "Hello Spring"); // 设置逻辑视图名 mav.setViewName("/WEB-INF/views/hello.jsp"); // 返回ModelAndView对象 return mav; } }还记得刚才说的 classpath:springCtx.xml 吗，我们创建了控制器，还要告诉Spring MVC控制器与View的映射关系。springCtx.xml这个文件并不存在我们项目中，需要手动添加。在resources资源文件夹里创建它。<?xml version="1.0" encoding="UTF-8"?> 请务必注意 这一段的包名与你控制器一致。它告诉Spring MVC，将这个控制器映射到/hello路径上去，一会你就能在浏览器通过http://ip:port/项目名/hello的形式去访问它。在WEB-INF里创建views文件夹，请注意是WEB-INF下创建。并在这个viewsview文件夹里新建jsp文件hello.jsp<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@page isELIgnored="false" %> Hello SpringMvc ${message} 我们在这里使用${...}的形式来使用后端提供的接口，以此获取后端返回的对象。那么问题来了，我们这个项目该怎么运行呢？显然对于Web项目我们需要Http服务器。还记得我们在配置环境的时候安装的Tomcat9吗，现在终于要到了使用它的时候了！另外，我们注意之前编写的TestController这个控制器，它需要导入的包中可能有两个报错。import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;如果出现这种情况，我们右键这个项目，打开属性找到Java Build Path选项的Libraries选项卡，点击Add Library...选择Server Runtime选项，然后下一步选择之前配置好的服务器（就是在环境配置那篇文章里的）保存并退出。接下来我们尝试运行它，与以往不同的是，我们需要Run As里面的Run on server如果你第一次这样做，会弹出个窗口选择Apache里对应的服务器，我这里是Tomcat9.0然后点击完成。很显然，你的浏览器已经打开了，说明你的项目跑成功了。并且在Eclipse的Package Explorer中多了个Servers文件夹。我们在浏览器访问的是index.jsp所渲染的页面在地址栏输入http://localhost:8080/test7/hello访问控制器和hello.jsp所渲染的页面问题在这一节其实有很多坑，一是Tomcat版本在10.0以上的话，包名变化问题。另外如果当初配置环境的时候，Eclipse没选Web选项，这里有些配置是没有的。上面这些问题请参考环境配置和此教程的问题模块。如果你项目菜单里的Run as没有Run on server选项的话需要如下操作打开项目属性搜索project facets，勾选Dynamic Web Module选项保存并退出。接下来如果操作不当的话可能导致Servers文件夹下配置文件里有多个context，这时候需要打开server.xml进行就该找到如下片段<Context>标签，把重复的删除