1.概述
- SpringMVC是spring的一部分,能够使用spring的IOC和AOP。
- SpringMVC强化注解的使用,在控制器,Service,Dao都可以使用注解。所谓SpringMVC的注解式开发是指在代码中通过对类与方法的注解,便可完成处理器在springMVC容器的注册。
- springMVC的核心Servlet是DispactcherServlet。DispatcherServlet是框架中的一个Servlet对象,负责接收请求,响应处理请求结果。它也叫做前端控制器(front controller),其父类是一个HttpServlet。
springMVC核心对象DispatcherServlet的作用:
1. 在servlet中的init()方法中,创建spring MVC的容器对象。例如
WebApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:springmvc.xml");
2. 作为Servlet接受请求
- SpringMVC是一种容器,用于创建控制器对象、管理控制器对象。
2.快速入门
示例:用户提交一个请求,服务端处理器在接收到这个请求后,给出一个欢迎消息,在响应页面中显示该消息。总体步骤如下:
1. 新建Maven web应用
2. 加入web依赖
spring MVC框架依赖和Servlet依赖
3. 声明springMVC核心对象DispatcherServlet
4. 创建一个jsp,发起请求
5. 创建一个普通类,作为控制器使用(代替之前的Servlet)
在类的上面加入@Controller注解
在类中定义方法,方法的上面加入@RequestMapping注解,表示该方法用于处理请求的,相当于Servlet中的doGet和doPost方法。
6. 创建作为结果的jsp页面
7. 创建springmvc的配置文件
声明组件扫描器,指定@Controller注解所在的包名
声明视图解析器对象
8. 使用逻辑视图名称
1.新建Maven模块,选择从原型创建
2. 创建好web项目后,在pom.xml文件中添加Servlet,spring MVC依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
3.在web.xml中注册中央调度器
- 中央调度器的全限定类名
// 中央调度器也是一个Servlet
org.springframework.web.servlet.DispatcherServlet
- 元素
...
在<servlet/>中添加<load-on-startup/>的作用是,标记是否在Web服务器(这里是Tomcat)启动时会创建这个 Servlet 实例,即是否在 Web 服务器启动时调用执行该 Servlet 的 init()方法,而不是在真正访问时才创建。
它的值必须是一个整数。
1. 当值大于等于0时,表示容器在启动时就加载并初始化这个 servlet,数值越小,该Servlet的优先级就越高,其被创建的也就越早;
2. 当值小于 0 或者没有指定时,则表示该 Servlet 在真正被使用时才会去创建。
3. 当值相同时,容器会自己选择创建顺序。
- 元素
:该元素的值表示将一些请求交给指定的servlet处理。
<!--url-pattern可以有两种值:
1.使用 *.xxx 例如*.do、*.action、*.mvn等,不能使用*.jsp
2.使用 /,如果使用/,静态资源获取请求会报404错误,需要进行
相关的配置
-->
<url-pattern>*.do</url-pattern>
- 自定义配置文件的位置与名称(设置在指定目录下寻找springmvc配置文件)
<!-- 前提:在resources目录下创建一个springmvc.xml文件,-->
<!--在Servlet标签下,init-param标签用于查找SpringMVC
配置文件路径及文件名 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
5.创建springmvc配置文件。
- 方式1在src/main/resources目录下创建springmvc配置文件,该文件名可以任意
- 方式2在WEB-INF下创建springmvc配置文件,该配置文件名必须为
元素的值-servlet.xml
新创建的springmvc配置文件如下:
<?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">
</beans>
6.创建处理器,也叫做后端控制器(back controller)
- @Controller:在类的上面加上该注解表示当前类为处理器
- @RequestMapping:在方法上面加上该注解表示当前方法为处理器方法。该方法要对 RequestMapping的value
属性所指定的 URI进行处理与响应
1. 作用:将指定的请求交给指定的方法处理
2. 属性:value,其值表示请求中的URI地址,唯一值,以"/"开头
7.声明组件扫描器
在springmvc的配置文件的beans标签下声明
<context:component-scan base-package="nrv"></context:component-scan>
8.声明视图解析器:ModelAndView 中只需给出要跳转页面的文件名即可(该文件名即逻辑视图名称),对于具体的文件路径与文件扩展名,视图解析器会自动完成拼接。
<!-- 在springmvc的配置文件中声明视图解析器:帮助处理视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀:指定视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/"></property>
<!-- 后缀:视图文件的扩展名-->
<property name="suffix" value=".jsp"></property>
</bean>
// view文件夹用于保护视图,因为在浏览器中是访问不到WEB-INF目录的
// mv.setViewName("/WEB-INF/view/show.jsp");
// 当配置了视图解析器,可以使用文件名称作为视图名使用,叫做视图逻辑名称
mv.setViewName("other");
3.配置文件相关
1.web开发中配置文件的说明
- web.xml:部署描述符文件,给服务器使用。作用是在服务器启动的时候读取web.xml,根据文件中的声明创建各种对象,根据文件中的声明知道请求和servlet等对象的关系。
- 框架的配置文件(springMVC的配置文件):作用是声明框架创建的项目中的各种对象,主要是创建Controller对象的
2.配置文件的加载顺序和功能
- tomcat服务器启动,读取web.xml,根据web.xml文件中的说明,创建对象。比如创建DispatcherServlet类型的对象,在其中会执行init方法。在init方法中会执行springMVC容器对象的创建。
<servlet>
<servlet-name>myweb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--自定义springMVC配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
- springMVC框架中,
new ClassPathXmlApplicationContext()
将读取springMVC的配置文件。使用组件扫描器遍历com.nrv包下的所有类,根据注解创建对象。
<!--声明组件扫描器-->
<context:component-scan base-package="com.nrv"></context:component-scan>
<!-- 声明视图解析器:帮助处理视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀:指定视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/"></property>
<!-- 后缀:视图文件的扩展名-->
<property name="suffix" value=".jsp"></property>
</bean>
4.@RequestMapping注解
- 属性value:其值表示请求中的URI地址,唯一值,以"/"开头
- 位置
1. 在方法上
2. 在类定义的上面
@Controller
@RequestMapping(value="/user")
public class UserController {
// 浏览器访问地址:http://localhost:8080/mvc_war/user/some.do
@RequestMapping(value = {"/some.do"})
public ModelAndView doSome() {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "处理了some.do请求");
mv.addObject("func", "执行了dosome方法");
mv.setViewName("show");
return mv;
}
// 浏览器请求访问地址:http://localhost:8080/mvc_war/user/other.do
@RequestMapping(value="/other.do")
public String doOnther() {
return "other";
}
}
- 作用:将指定的请求交给指定的方法处理
1.定义请求提交方式
@RequestMapping的属性method,用于对被注解方法所处理请求的提交方式进行限制,只有满足该method属性指定的提交方式的请求,才会指定该被注解方法。method属性的取值为RequestMethod枚举常量,常用的为RequestMethod.GET,RequestMethod.POST 如果不指定请求方式,无论get请求方式还是post请求方式都可以被匹配。
@Controller
@RequestMapping(value="/user")
public class UserController {
// 浏览器访问地址:http://localhost:8080/mvc_war/user/some.do
// 指定some.do使用get请求方式
@RequestMapping(value = {"/some.do"}, method = RequestMethod.GET)
public ModelAndView doSome() {
ModelAndView mv = new ModelAndView();
// Model中的数据存储在request作用域中
// SpringMVC默认采用转发的方式跳转到视图
// 本次请求结束,模型中的数据销毁
mv.addObject("msg", "处理了some.do请求");
mv.addObject("func", "执行了dosome方法");
mv.setViewName("show");
return mv;
}
// 浏览器请求访问地址:http://localhost:8080/mvc_war/user/other.do
// 指定other.do使用post请求方式
@RequestMapping(value="/other.do", method = RequestMethod.POST)
public String doOnther() {
return "other";
}
// 不指定method属性,请求方式没有限制
@RequestMapping(value="/first.do")
public String doFirst() {
return "other";
}
}
// method属性取值参考:
public enum RequestMethod {
GET,
HEAD,
POST,
PUT,
PATCH,
DELETE,
OPTIONS,
TRACE;
private RequestMethod() {
}
}
附: 客户端常用的请求方式:
- 表单请求:默认为GET,可以指定为POST
- AJAX请求:默认为GET,可以指定为POST
- 地址栏请求:GET请求
- 超链接请求:GET请求
- src资源路径请求:GET请求
5.处理器方法的参数
处理器方法可以包含以下四类参数,这些参数在系统调用时由系统自动赋值,即程序员可在方法内直接使用。
- HttpServletRequest
- HttpServletResponse
- HttpSession
- 请求中所携带的请求参数
1.接受请求中的参数
- 逐个接收:请求中的参数名和控制器方法的形参名一样,按照名称对应接收参数。
<form action="user/receive-args.do" method="post">
姓名:<input type="text" name="name"/><br/>
年龄:<input type="text" name="age"/><br/>
<input type="submit" value="提交参数"/>
</form>
@Controller
@RequestMapping(value="/user")
public class UserController {
// 浏览器访问路径:http://localhost:8080/mvc_war/user/receive-args.do
@RequestMapping(value = "/receive-args.do")
public ModelAndView doRequestArgs(String name, Integer age) {
System.out.println("执行了doRequestArgs方法,name="+ name +",age="+age);
ModelAndView modelAndView = new ModelAndView();
// 添加数据
modelAndView.addObject("myname", name);
modelAndView.addObject("myage", age);
modelAndView.setViewName("show");
return modelAndView;
}
}
- 逐个接收:请求中的参数名和控制器方法的形参名不一致。使用@RequestParam注解解决名称不一样的问题
@RequestParam的使用:
属性value:值表示请求中的参数名称
属性required:Boolean类型的,默认是true
true表示请求中必须有这个参数,没有就报错。
false表示请求中可以没有这个参数
位置:在形参定义的前面
<form action="user/receive-argsname.do" method="post">
姓名:<input type="text" name="rname"/><br/>
年龄:<input type="text" name="rage"/><br/>
<input type="submit" value="提交参数"/>
</form>
@Controller
@RequestMapping(value="/user")
public class UserController {
// 浏览器访问路径:http://localhost:8080/mvc_war/user/receive-args.do
// required = false表示即使表单中未提交数据也不会报错
@RequestMapping(value = "/receive-argsname.do")
public ModelAndView doRequestArgs(@RequestParam(value = "rname", required = false)String name,
@RequestParam(value = "rage", required = false)Integer age) {
System.out.println("执行了doRequestArgs方法,name="+ name +",age="+age);
ModelAndView modelAndView = new ModelAndView();
// 添加数据
modelAndView.addObject("myname", name);
modelAndView.addObject("myage", age);
modelAndView.setViewName("show");
return modelAndView;
}
}
- 对象接收:在控制器方法的形参是Java对象,使用Java对象的属性接收请求中的参数值。要求Java对象的属性名和请求中的参数名一致。
<form action="user/receive-object.do" method="post">
姓名:<input type="text" name="name"/><br/>
年龄:<input type="text" name="age"/><br/>
<input type="submit" value="提交参数"/>
</form>
public class Student {
private String name;
private Integer age;
// getter,setter
}
@Controller
@RequestMapping(value="/user")
public class UserController {
@RequestMapping(value="/receive-object.do")
public ModelAndView doRecieveObject(Student student) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("myname", student.getName());
modelAndView.addObject("myage", student.getAge());
modelAndView.setViewName("show");
return modelAndView;
}
}
2.接收post请求的请求参数中中文乱码的问题
spring给出了专门的字符集过滤器CharacterEncodingFilter类。
<!-- 在web.xml中声明过滤器-->
<!-- 声明过滤器,框架提供的,解决post请求中中文乱码的问题-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 给过滤器属性赋值-->
<init-param>
<!--项目使用的字符编码 -->
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<!-- 强制请求对象request使用encoding的编码方式-->
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<!-- 强制应答对象response使用encoding的编码方式-->
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!-- 强制所有请求先经过过滤器处理-->
<url-pattern>/*</url-pattern>
</filter-mapping>
6.处理器方法的返回值
使用@Controller注解的处理器的处理器方法,其返回值常用的有四类类型。返回值代表本次请求的处理结果
- ModelAndView
- String
- 无返回值void
- 返回对象Object
1.ModelAndView(数据和视图)
请求的结果有数据和视图,使用ModelAndView最方便。数据存放在Request作用域,视图默认执行转发操作。
2.String(视图)
框架的返回值是String,则执行的是forward转发操作。视图可以表示为完整视图路径(物理视图名)或者视图的逻辑名称。
- 返回值是逻辑视图名称,需要在springMVC配置文件中配置视图解析器
<!-- springmvc.xml中声明视图解析器:帮助处理视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀:指定视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/"></property>
<!-- 后缀:视图文件的扩展名-->
<property name="suffix" value=".jsp"></property>
</bean>
<form action="user/return-string-view.do" method="post">
姓名:<input type="text" name="name"/><br/>
年龄:<input type="text" name="age"/><br/>
<input type="submit" value="提交参数"/>
</form>
@Controller
@RequestMapping(value="/user")
public class UserController {
// 控制器方法返回String,表示逻辑名称,需要在项目中配置视图解析器
@RequestMapping(value = "/return-string-view.do")
public String doReturnStringView(HttpServletRequest request, String name, Integer age) {
System.out.println("name:"+name+",+age:" +age);
// 处理数据
request.setAttribute("myname", name);
request.setAttribute("myage", age);
// 返回结果,转发到show.jsp
return "show";
}
}
- 返回值是完整视图路径:项目中不能配置视图解析器
<form action="user/return-string-view.do" method="post">
姓名:<input type="text" name="name"/><br/>
年龄:<input type="text" name="age"/><br/>
<input type="submit" value="提交参数"/>
</form>
@Controller
@RequestMapping(value="/user")
public class UserController {
@RequestMapping(value="return-string-view.do")
public String doReturnStringView(String name, Integer age) {
System.out.println("name:"+name+",age:" +age);
return "/WEB-INF/view/show.jsp";
}
}
3.void(没有数据和视图)
void:没有数据和视图,可以使用HttpServletResponse对象输出数据,响应Ajax请求。示例:对于Ajax的异步请求的响应
- Maven中加入Jackson依赖
<!--jackson依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.2</version>
</dependency>
- 引入JQuery依赖:因为本项目需要使用JQuery的ajax方法提交请求。
- index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<!--引入jQuery文件 -->
<script type="text/javascript" src="js/jquery-3.5.1.js"></script>
<script type="text/javascript">
$(function (){
//绑定事件
$("#btnAjax").on("click", function() {
$.ajax({
url:"return-void-ajax.do",
//url:"doStudentJson.do",
data:{
name:"张三",
age:"23"
},
dataType:"json",
success:function (resp) {
alert("resp.name:"+resp.name+"resp.age:"+resp.age);
}
})
})
})
</script>
</head>
<body>
<button id="btnAjax">发起Ajax请求</button>
</body>
</html>
- 控制器如下
@Controller
public class UserController {
/**
* 控制器方法返回是void,响应Ajax请求。使用HttpServletResponse输出数据
*/
@RequestMapping(value="/return-void-ajax.do")
public void returnVoidAjax(HttpServletResponse response, String name, Integer age) throws IOException {
System.out.println("name="+name+",age="+age);
Student student = new Student();
student.setName(name);
student.setAge(age);
// 将对象转化为json
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(student);
System.out.println("服务器端对象转化为的json"+json);
// 输出json,响应Ajax
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println(json);
writer.flush();
writer.close();
}
}
4.Object
返回的对象不是作为逻辑视图或者物理视图出现的,而是作为直接在页面显示的数据出现的。控制器方法返回对象Object,用来响应Ajax请求。返回的对象Object,可以是List,Student,Map,String,Integer,这些都是数据,正是Ajax请求需要的。在Ajax请求中,一般需要的是从服务器返回的json格式的数据,经常要处理Java对象到json的转换。而且还需要输出数据响应Ajax请求。
- HttpMessageConverter消息转换器,一个接口
作用如下:
1. 将请求的数据转为Java对象
2. 将控制器方法返回的对象转为json,xml,text,二进制等不同格式的数据
接口中方法说明:
public interface HttpMessageConverter<T> {
//检查var1这个类型的对象能否转为MediaType表示的数据格式
// 如果能转换,则返回true,返回true调用read()
boolean canRead(Class<?> var1, @Nullable MediaType var2);
//作用:接受请求中的数据,将数据转换为var1表示的对象
T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;
// 作用:检查var1这种数据类型能否转为MediaType表示的数据格式
boolean canWrite(Class<?> var1, @Nullable MediaType var2);
// 作用:将var1对象按照var2说明的格式,把对象转为json或者XML
void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
List<MediaType> getSupportedMediaTypes();
}
- HttpMessageConverter接口的实现类:
1. MappingJackson2HttpMessageConverter:用Jackson工具库的ObjectMapper将Java对象转为json数据格式
2. StringHttpMessageConverter:将字符串类型的数据进行格式转换和编码
-
注册驱动标签:在springMVC的配置文件,加入注解驱动的标签
mvc:annotation-driven
.加入这个标签后,springMVC项目启动后,会创建HttpMessageConverter接口的七个实现类对象,包括上面两个。note:有关MVC的包不要选错,选择以MVC结尾的。 -
@ResponseBody:该注解的作用就是将Java对象转换后的json数据通过HttpServletResponse对象输出给浏览器,转换后的JSON数据被放入到响应体中的。
// @ResponseBody作用类似于如下
// 输出json,响应Ajax
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println(json);
writer.flush();
writer.close();
5.总结:将控制器方法返回的对象转为json的步骤 (返回自定义类型对象时,不能以对象的形式直接返回给客户端浏览器,而是将对象转换为JSON格式的数据发送给浏览器的。)
1. pom.xml中加入Jackson依赖,因为SpringMVC框架默认处理json就是使用Jackson
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.2</version>
</dependency>
2. 在springMVC的配置文件中,加入注解驱动的标签mvc:annotation-driven
3. 在控制器方法的上面加入@ResponseBody注解,
表示将返回值数据(即转换后的数据)输出到浏览器
- 示例:控制器方法返回Student,转为json
// index.jsp
<html>
<head>
<title>Title</title>
<!--引入jQuery文件 -->
<script type="text/javascript" src="js/jquery-3.5.1.js"></script>
<script type="text/javascript">
$(function (){
//绑定事件
$("#btnAjax").on("click", function() {
$.ajax({
//url:"return-void-ajax.do",
url:"doStudentJson.do",
data:{
name:"张三",
age:"23"
},
dataType:"json",
success:function (resp) {
alert("resp.name:"+resp.name+"resp.age:"+resp.age);
}
})
})
})
</script>
</head>
<body>
<button id="btnAjax">发起Ajax请求</button>
</body>
</html>
// springMVC配置文件中需要注册驱动标签
<mvc:annotation-driven/>
@Controller
public class UserController {
// 控制器方法返回Student,将Student转为json
@RequestMapping(value="/doStudentJson.do")
@ResponseBody
public Student doAjaxJson(String name, Integer age) {
System.out.println("控制器方法返回自定义对象Student转为json"+ name+","+age);
Student student = new Student();
student.setName(name);
student.setAge(age);
return student;
}
}
- 示例:控制器方法返回String-文本数据
// index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<!--引入jQuery文件 -->
<script type="text/javascript" src="js/jquery-3.5.1.js"></script>
<script type="text/javascript">
$(function (){
//绑定事件
$("#btnAjax").on("click", function() {
$.ajax({
//url:"return-void-ajax.do",
//url:"doStudentJson.do",
url:"doStringData.do",
data:{
name:"张三",
age:"23"
},
dataType:"text",
success:function (resp) {
alert(resp);
}
})
})
})
</script>
</head>
<body>
<button id="btnAjax">发起Ajax请求</button>
</body>
</html>
// springMVC配置文件中需要注册驱动标签
<mvc:annotation-driven/>
@Controller
public class UserController {
//解决中文乱码问题:使用@RequestMapping的produces属性,其属性值即为想指定content-type的值
//其值告诉浏览器怎么显示服务器返回的数据
// 控制器方法返回String-数据
// 区分返回值String是字符串还是视图:方法上面是否有@ResponseBody注解
// 有就是数据,没有就是视图
@RequestMapping(value="/doStringData.do", produces = "text/plain;charset=utf-8")
@ResponseBody
public String doStringData() {
return "Hello,SpringMVC注解式开发";
}
}
7.Tomcat中的静态资源的访问
1.Tomcat中的default Servlet
- 配置在/conf/web.xml下
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- The mapping for the default servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern> <!-- /表示包括静态资源-->
</servlet-mapping>
- default叫做默认Servlet,作用:
1. 提供静态资源的处理
2. 处理所有未映射到其他控制器的请求的处理
2.中央调度器的url-pattern的设置
- 中央调度器的url-pattern不设置"/"
<servlet>
<servlet-name>myweb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--自定义配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!--*.do所有请求交给springmvc.xml中配置的控制器对象处理 -->
<servlet-mapping>
<servlet-name>myweb</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
http://localhost:8080/mvc_war_exploded/imags/img.png
http://localhost:8080/mvc_war_exploded/html/test.html
http://localhost:8080/mvc_war_exploded/js/jquery-3.5.1.js
上述静态资源都可以访问,因为Tomcat中的default这个Servlet会处理。
2. 中央调度器的url-pattern设置"/"
<servlet>
<servlet-name>myweb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--自定义配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myweb</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
上述三个静态资源都无法访问,使用"/"导致中央调度器成为了默认的default servlet。需要处理静态资源和其他的映射的请求。而默认的中央调度器没有处理静态资源的控制器对象,所以静态资源都是404.而some.do这个请求有MyController控制器对象,所以可以访问。
3.静态资源访问工作的处理
如果项目中,中央调度器设置了"/",动态资源能够访问,静态资源不能访问,则需要处理静态资源的访问工作。
- 方式1:使用
<mvc:default-servlet-handler/>
标签
。声明了该标签后,springMVC框架会在容器中创建DefaultServletHttpRequestHandler处理器对象。他就像检察员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理。一般的服务器都有默认的Servlet。该方式依赖Web应用服务器
// 在SpringMVC的配置文件中加入如下内容
<mvc:default-servlet-handler/>
note:default-servlet-handler和@RequestMapping使用冲突的解决方案:声明注解驱动
<mvc:annotation-driven/>
- 方式2:在SpringMVC配置文件中加入一个
mvc:resources
标签,框架会创建一个ResourceHttpRequestHandler处理器对象,使用这个对象处理静态资源的访问。该方式不依赖Web应用服务器,推荐使用
当项目结构如下时:
<!-- mapping属性:其值表示访问静态资源的请求URI地址,可以使用通配符
比如**表示任意的目录中的资源名称
location属性:其值表示静态资源在项目中的位置,不要使用/WEB-INF目录
-->
<mvc:resources mapping="/imags/**" location="/imags/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/html/**" location="/html/"/>
- 方式3:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
8.SSM整合开发
SSM编程,即SpringMVC、Spring、MyBatis整合。这三个框架对应三层架构的三层,SpringMVC是视图层,Spring是业务层,MyBatis是持久层。SSM整合的实现方式分为两种:基于XML配置方式和基于注解方式。
1.SSM整合思路
SSM整合就是将对象交给容器管理,让容器去创建项目中要使用到的Java对象。现在有两个容器:
- Spring容器:Spring容器是管理service和dao等对象的
- springMVC容器:管理控制器对象的
2.容器的创建
- spring容器的创建:在web.xml中声明了监听器ContextLoaderListener。 功能是创建spring的容器对象WebApplicationContext.在创建WebApplicationContext对象时,读取spring的配置文件,读取配置文件时,遇到bean标签或者注解,就能创建service、dao等对象,放到容器中。
- springMVC容器的创建:在web.xml中声明了中央调度器DispatcherServlet。 在这个Servlet中的init方法中,创建了容器对象WebApplicationContext,在创建WebApplicationContext对象时,读取SpringMVC的配置文件,读取文件的时候,遇到@Controller注解创建控制器controller对象,放到容器中。
3.SpringMVC容器和Spring容器的关系
Spring是父容器,SpringMVC是子容器,相当于Java中的继承关系。所以SpringMVC容器中的controller对象可以访问Spring容器中的service对象
3.SSM整合开发步骤
- 创建数据库表
- 创建Maven Web项目
- 修改pom.xml加入依赖:spring、springMVC、mybatis、mybatis-spring、MySQL驱动、druid、Jackson
- 编写web.xml:声明容器对象
- 声明spring的监听器ContextLoaderListener:创建spring容器对象,创建service、dao对象
- 声明springMVC的中央调度器DispatcherServlet:创建SpringMVC容器对象,创建controller对象
- 创建程序中的包,dao、service、controller、entity
- 编写spring、mybatis、springMVC配置文件
- 写Java代码,实体类,dao接口和Mapper文件,service类,controller类。使用注解声明对象和赋值
- 创建视图文件,各种jsp
9.相对路径
在页面中,存在路径的问题。例如:
<a href="test/some.do">没有斜杠开头</a>
<a href="/test/some.do">有斜杠开头</a>
<a href="http://www.baidu.com">以协议开头的地址</a>
上述页面中地址的区别:
- 有协议开头的地址,比如说
http://www.baidu.com
,称为绝对地址。地址是唯一的,可以直接访问。 - 没有协议开头的,比如说
test/some.do
,/test/some.do
都称为相对地址。相对地址必须和参考地址组合在一起,才能表示一个资源的完整地址,才能访问。- 没有斜杠开头的地址,参考地址为当前资源的访问路径。参考地址加上当前的相对地址组合为最后的访问地址
- 有斜杠开头的地址,参考地址就是服务器地址,比如说
http://localhost:8080
.这种组合的地址缺少项目访问路径,即Tomcat服务器配置中应用程序上下文的值,访问易出错
1.没有斜杠开头的相对地址,访问路径更不易出错的解决方式
- 使用EL表达式
<a href="${pageContext.request.ContextPath}/test/some.do">发起请求test/some.do请求</a>
ContextPath的值即为Tomcat服务器配置中的应用程序上下文。例如/springmvc_review_war_exploded
2. 使用HTMl中的base标签
1. 方式1(href的值写死了,不推荐使用)
在head标签下添加如下内容
<base href=""/>
2. 方式2
先在页面的头部添加如下内容
<%
String basePath = request.getScheme() + "://" +
request.getServerName() + ":" + request.getServerPort() +
request.getContextPath() + "/";
%>
指定 base 标签
<head>
<base href="<%=basePath%>">
<title>title</title>
</head>
2.有斜杠开头的相对地址,访问路径更不易出错的解决方式
- 使用EL表达式
<a href="${pageContext.request.ContextPath}/test/some.do">发起请求test/some.do请求</a>
10.请求转发和重定向
当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重定向。 而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器。
1.使用forward指定转发操作
- 示例:控制器方法返回ModelAndView实现转发
// 语法:setViewName("forward:视图完整路径");
// forward特点:不和视图解析器一同工作
// setViewName默认是转发
modelAndView.setViewName("forward:/WEB-INF/view/show.jsp");
2.使用redirect实现重定向操作
- 示例:控制器方法返回ModelAndView实现重定向.note:重定向不能够访问受保护的目录WEB-INF
// 语法:setViewName("redirect:视图完整路径");
// redirect特点:不和视图解析器一同工作
modelAndView.setViewName("redirect:/other.jsp");
11.异常处理
SpringMVC框架使用的是集中的异常处理:将各个Controller中抛出的异常集中到一个地方处理。处理异常的叫做异常处理器。框架中使用两个注解完成异常的集中处理,这样每个Controller就不用单独处理异常。注解是:
- @ExceptionHandler:放在方法的上面,表示此方法可以处理某个类型的异常。当异常发生时,执行这个方法。
- 属性value:其值指定该注解的方法所有处理的异常类
- @ControllerAdvice:放在类的上面,表示这个类中有异常的处理方法。相当于AOP中的@Aspect.@Controller看作是控制器增强,就是给Controller类增加异常的处理(切面)功能
12.拦截器
1.单个拦截器
- 概念:拦截器是SpringMVC框架中的一种对象。
- 作用:拦截用户的请求,可以预先对请求做处理。根据处理结果,决定是否执行controller。
- 特点
1. 拦截器分为系统拦截器和自定义拦截器
2. 一个项目可以有多个拦截器。0个或者多个自定义拦截器
3. 拦截器侧重于拦截用户的请求
4. 拦截器是在请求处理之前先执行的
5. 可以将多个controller控制器共有的功能定义到拦截器
- 定义
1. 创建类实现拦截器接口HanderInterceptor,
根据需要实现接口中的三个方法
1. preHandle:预处理方法,在处理器方法执行之前执行
可以对请求做处理,可以做登陆的检查,权限的判断,统计数据等等。
2. postHandle:后处理方法,在控制器方法之后执行
可以修改处理器方法的处理结果数据还有跳转的视图
3. afterCompletion:最后执行的方法
可以做程序最后要做的工作,释放内存,清理临时变量
例如:
// 在控制器方法中
session.setAttribute("data", "在控制器方法中往session中添加数据");
// 在拦截器的afterCompletion方法中
// 清除资源
HttpSession session = request.getSession();
session.removeAttribute("data");
System.out.println(session.getAttribute("data")); // null
2. 在springMVC配置文件中,声明拦截器对象,并指定拦截器的拦截地址
2.多个拦截器
使用两个拦截器,查看拦截器的执行以及哪个方法控制请求的执行。
- 两个拦截器,第一个拦截器的preHandle返回true,第二个拦截器的preHandle也返回true.请求可以得到处理。(这两个拦截器在springMVC配置文件中具有先后关系)
先执行拦截器1的preHandle
拦截器2的preHandle
执行了控制器的处理与用户请求相应的控制器方法
拦截器2的postHandle
拦截器1的postHandle
拦截器2的afterCompletion
拦截器1的afterCompletion
- 两个拦截器,第一个拦截器的preHandle返回true,第二个拦截器的preHandle也返回false。控制器方法得不到执行,请求得不到处理。
先执行拦截器1的preHandle
拦截器2的preHandle
拦截器1的afterCompletion
- 两个拦截器,第一个拦截器的preHandle返回false,第二个拦截器的preHandle也返回false或者true。控制器方法得不到执行,请求得不到处理。
执行拦截器1的preHandle
使用多个拦截器的原因?
- 将验证功能分散到独立的拦截器。每个拦截器做单一的验证处理
- 组合多个拦截器
3.拦截器和过滤器的对比
- 拦截器是SpringMVC框架中的对象,过滤器是Servlet中的对象
- 拦截器对象是框架容器创建的,过滤器是Tomcat创建的对象
- 拦截器侧重对请求做判断,处理的,可以截断请求。过滤器侧重对request、response对象的属性,参数设置值的。例如
request.setCharacterEncoding("utf-8")
- 拦截器的执行时间有三个:控制器方法之前,之后,请求完成后。过滤器是在请求之前。
- 拦截器是对controller,动态资源请求的。过滤器可以过滤所有请求,动态的和静态的。
- 拦截器和过滤器一起执行,先执行的是过滤器,后面是中央调度器,后面是拦截器,最后是控制器方法。
13.SpringMVC的内部执行流程