2021.1.7 是正式开始学本课的日子,这一课讲解springMVC基础知识。本课为录播课因此学习起来会相对简单,我将跟着课程内容做笔记,并且尽量自己做出演示来验证知识点。
本文和本文内一切内容,均来自我对于开课吧Java企业级分布式架构师010期课程的学习笔记,并在我自身的理解上整理而成。
学习方式为先根据视频理解讲义,再根据讲义做笔记,最后看视频完善笔记的模型进行。
我近年来就是一直在进行在这个框架下搭建的项目的开发,因此有一定的熟练度,但是虽然能完成工作任务却缺少扎实的基础知识,所以特别来学习一下。
当前使用java开发的大多数都是web应用,这些web应用几乎全是用B/S架构开发的。
在B/S架构中,应用系统的标准三层架构为:表现层、业务层、持久层。
javaEE制定了一套规范(Servlet),去进行BS结构的处理。
MVC是模型(model)- 视图(view)-控制器(controller)的缩写,是一种用于设计编写Web应用程序表现层的模式。
Model
View
Controller
图:
创建一个maven工程,添加mvc、jstl、servlet-api依赖
创建maven工程的步骤跟最开始的Mybatis方式相同,主要区别在于相关的依赖文件。
本地调试需要准备一个环境,我采用的是Tomcat6.0
参考链接:Java企业级分布式架构师010期课程第一章-第一讲-学习笔记
pom代码:
<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.0modelVersion>
<groupId>com.baogroupId>
<artifactId>HelloSpringMVCartifactId>
<packaging>warpackaging>
<version>0.0.1-SNAPSHOTversion>
<name>HelloSpringMVC Maven Webappname>
<url>http://maven.apache.orgurl>
<properties>
<spring.version>4.3.0.RELEASEspring.version>
properties>
<dependencies>
<dependency>
<groupId>com.google.firebasegroupId>
<artifactId>firebase-adminartifactId>
<version>6.10.0version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.5version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.5version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>4.0.1version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jsp-apiartifactId>
<version>2.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>3.8.1version>
<scope>testscope>
dependency>
dependencies>
<build>
<finalName>HelloSpringMVCfinalName>
build>
project>
<web-app
version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>Archetype Created Web Applicationdisplay-name>
<servlet>
<servlet-name>dispatcherservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springContext.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>dispatcherservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springContext.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
web-app>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.bao"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/value>
property>
<property name="suffix">
<value>.jspvalue>
property>
bean>
beans>
处理器开发方式有多种:
企业项目中常用注解方式举例如下:
@Controller
public class HelloSpringController {
String message = "Welcome to Spring MVC!";
@RequestMapping("/hello")
public ModelAndView showMessage(@RequestParam(value = "name", required = false, defaultValue = "Spring") String name) {
ModelAndView mv = new ModelAndView("hellospring");//指定视图
System.out.println("test_1");
//向视图中添加所要展示或使用的内容,将在页面中使用
mv.addObject("message", message);
mv.addObject("name", name);
return mv;
}
@RequestMapping("/bey")
public ModelAndView showMessage2(@RequestParam(value = "name", required = false, defaultValue = "Spring") String name) {
System.out.println("test_2");
ModelAndView mv = new ModelAndView("hellospring");//指定视图
//向视图中添加所要展示或使用的内容,将在页面中使用
mv.addObject("message", message+"2");
mv.addObject("name", name);
return mv;
}
}
可以采用注解修饰或者不使用注解修饰,本次重点介绍注解方式
@ResponseBody注解的作用:
@RequestBody注解和@ResponseBody注解的作用相反,@RequestBody注解是处理请求参数的http消息转换的。
MappingJacksonHttpMessageConverter
application/json;charset=utf-8
response.getWriter()
方法将JSON格式的字符串写回给调用者StringHttpMessageConverter
text/plain;charset=utf-8
response.getWriter()
方法将String格式的字符串写回给调用者JSP示例代码
<h3>演示JSON格式数据参数绑定和返回值处理h3>
<h3>
<a href="${pageContext.request.contextPath}/responsebody/returnString">测试String类型返回值a>
h3>
<h3>
<a href="${pageContext.request.contextPath}/responsebody/returnPOJO">测试POJO类型返回值a>
h3>
<h3>
<a href="${pageContext.request.contextPath}/restController/returnString">测试String类型返回值a>
h3>
<h3>
<a href="${pageContext.request.contextPath}/restController/returnPOJO">测试POJO类型返回值a>
h3>
Controller代码
方法1:@ResponseBody
@RequestMapping("responsebody")
@Controller
public class ResponseBodyController {
@RequestMapping(
value = "returnString",
produces = "text/plain;charset=utf-8"
)
@ResponseBody
public String returnString() {
return "return some";
}
@RequestMapping(
value = "returnPOJO"
)
@ResponseBody
public Person returnPOJO() {
Person person = new Person();
person.setId(12);
person.setName("bob");
return person;
}
}
方法2:@RestController
@RequestMapping("restcontroller")
@RestController //等效于Controller和Response的结合
public class MyTestRestController {
@RequestMapping(
value = "returnString",
produces = "text/plain;charset=utf-8"
)
public String returnString() {
return "return some";
}
@RequestMapping(
value = "returnPOJO"
)
public Person returnPOJO() {
Person person = new Person();
person.setId(12);
person.setName("bob");
return person;
}
}
参数绑定就是将请求参数串中的value值获取之后,再进行类型转换,然后将类型转换后的值赋给Controller类中方法的形参。这个过程就是参数绑定。
参数绑定需要两步:
默认是key/value格式,比如:http://xxx?id=1&type=301
以String类型表现的各种值
Controller类中的方法参数,比如简单类型、POJO类型、集合类型等。
默认内置了24种参数解析组件(ArgumentResolver)
Controller方法形参中可以随时添加如下类型的参数(Servlet API支持),处理适配器会自动处理并进行赋值。
http请求参数的【key】和controller方法的【形参名称】一致
应用场景为当http请求参数的【key】和controller方法的【形参名称】不一致时,需要使用@RequestParam注解,也就是使用了注解就一定可以绑定,一定满足要求。
@RequestParam注解属性
TTP Status 400 – Required Integer parameter `xxx` is not present
要求表单中【参数名称】和Controller方法中的【POJO形参的属性名称】保持一致。
通过HTTP请求批量传递简单类型数据的情况,Controller方法中可以使用String[]
或者pojo的String[]
属性接收(两种方法任选其一),但是不能使用List集合接收。
批量传递的参数请求,最终要使用List
来接收,那么这个List必须放在另一个POJO类中。
为方便理解先介绍一下HTTP
超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上,是互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII形式给出;而消息内容则具有一个类似MIME的格式。
通过浏览器可以访问到的所有资源都是web资源,web资源分为静态资源和动态资源:
约束客户端和服务端之间传输web资源时的格式。
目前有1.0版本和1.1版本。
1.1版本的优势在于可以一个连接传输多个web资源。
因此建议使用1.1版本。
HTTP协议由两部分组成:请求协议信息和响应协议信息。
HTTP请求协议信息由三部分组成:请求行、请求头、请求体
(行头体)
HTTP响应协议信息也是由三部分组成:状态行、响应头、响应体(响应正文)。
(行头体)
GET和POST都属于请求行中的8种请求方法中的一种。
区别在于:
满足REST设计风格,即满足REST的约束条件和原则的应用程序或设计就是RESTful。
每发出一个HTTP请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生”状态转化”。而这种转化是建立在表现层之上的,因此是表现层状态转化。
具体说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应4种基本操作:GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源。
RESTful的示例:
URL-PATTERN:设置为/,方便拦截RESTful请求
<servlet-mapping>
<servlet-name>dispatcherservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
@PathVariable:可以解析出来URL中模板变量({id})
比如这样的URL:http://localhost:8080/HelloSpringMVC/index/Person/1/LISI
Controller:
@RequestMapping(
"{id}/{name}"
)
@ResponseBody
public Person selectPersonById(@PathVariable Integer id,@PathVariable String name) {
RESTful服务中一个重要的特性就是一种资源可以有多种表现形式,在SpringMVC中可以使用ContentNegotiatingManager这个内容管理器开实现这种方式。
内容协商的方式有三种:
不过现在RESTful响应的数据一般都是JSON格式,所以一般也不使用内容协商管理器,直接使用@ResponseBody注解将数据按照JSON格式返回。
如果在DispatchServlet中设置url-pattern为/则必须对静态资源进行访问处理。
之前是这么设置的:
<servlet-mapping>
<servlet-name>dispatcherservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
所以需要在springmvc.xml(我的是springContext.xml)文件中,使用mvc:resources
标签,如下:
SpringMVC会吧mapping映射到ResourceHttpRequestHandler,这样静态资源在经过DispatcherServlet转发时就可以找到对应的Handler了。
SpringMVC的拦截器主要是针对特定处理器进行拦截的。
在SpringMVC中定义一个Interceptor非常简单,主要有4种方式:
实现SpringMVC的HandlerInterceptor接口:
public class MyInterceptor implements HandlerInterceptor{
//Handler执行前调用
//应用场景:登录认证、身份授权
//返回值为true表示放行,为false表示不放行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// TODO 自动生成的方法存根
return false;
}
//进入Handler开始执行,并且在返回ModelAndView之前调用
//应用场景:对ModelAndView对象进行操作,可以把公共模型数据传到前台,可以统一指定试图
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO 自动生成的方法存根
}
//执行完Handler之后调用
//应用场景:统一异常处理、统一日志处理
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO 自动生成的方法存根
}
}
SpringMVC拦截器是绑定在HandlerMapping中的,即:如果某个HandlerMapping中配置拦截,则该HandlerMapping映射成功的Handler会使用该拦截器。
SpringMVC的全局拦截器配置,其实是把配置的拦截器注入到每个已初始化的HandlerMapping中了。
配置模板:
<mvc:interceptors>
bean>
bean>
<mvc:interceptor>
<mvc:mapping path="/console/**"/>
bean>
mvc:interceptor>
mvc:interceptors>
当配置了多个拦截器时,在springmvc.xml配置文件中配置的越靠前,优先级越高(从上至下排列优先级)
拦截器对访问的请求URL进行登录拦截校验。
分析如下:
略,用于进行登录活动
用于进行登录活动,和退出登录活动
public class LoginInterceptor implements HandlerInterceptor{
//Handler执行前调用
//应用场景:登录认证、身份授权
//返回值为true表示放行,为false表示不放行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//获取请求的URI
String URI = request.getRequestURI();
System.out.println("URI");
//1.如果URI是公开的地址,采取放行
if(URI.indexOf("login")>-1)
return true;
//2.如果用户session存在,则放行。
String uname = (String)request.getSession().getAttribute("username");
if(uname!=null && !uname.equals(""))
return true;
//其他情况都一律跳转到登录界面
//response.sendRedirect("/login.jsp");
response.sendRedirect("/hellospring.jsp"); //login没做拿个其他页面临时替换一下
return false;
}
//进入Handler开始执行,并且在返回ModelAndView之前调用
//应用场景:对ModelAndView对象进行操作,可以把公共模型数据传到前台,可以统一指定试图
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO 自动生成的方法存根
}
//执行完Handler之后调用
//应用场景:统一异常处理、统一日志处理
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO 自动生成的方法存根
}
}
<mvc:interceptors>
bean>
bean>
bean>
<mvc:interceptor>
<mvc:mapping path="/console/**"/>
bean>
mvc:interceptor>
mvc:interceptors>
2021.1.21日至此基础的SpringMVC知识已经学习完毕,接下来的时间里,我会花几周来巩固第一期学习计划的知识,也顺便过一下年。
本次学习花费了非常久的时间,为了不影响学习热情,部分知识点我选择了跳过,这些跳过的知识点一般不会在日常工作中出现,因此在剩下的巩固时间里我会对于这部分进行补充,具体的会再发布一篇作为SpringMVC了解篇来供后续学习。