SpringMVC 也叫 Spring web mvc。是 Spring 框架的一部分,是在 Spring3.0 后发布的。
SpringMVC 是基于Spring的一个框架, 实际上就是spring的一个模块, 专门是做web开发的。
可以理解是servlet的一个升级,是在servlet基础上面加入一些功能,做web开发方便。
SpringMVC就是一个Spring。
Spring是容器,Spring核心技术ioc能够管理对象,使用配置文件的< bean> 或者注解 @Component 、 @Controller…
SpringMVC 也能够创建对象,并放入到容器中(SpringMVC容器), SpringMVC容器中放的是控制器对象,
开发人员需要做的是 使用 @Contorller注解 创建控制器(处理器)对象, 并放到SpringMVC容器中。
这个控制器对象就像一个servlet一样能够接受用户请求,显示处理结果。
但它是一个普通类,并不是Servlet。SpringMVC赋予了它特殊功能。
web开发底层是servlet, SpringMVC中有一个对象是Servlet : DispatcherServlet(中央调度器)
DispatcherServlet:
负责接收用户的所有请求, 用户把请求给了DispatcherServlet,
之后DispatcherServlet把请求转发给Controller对象,最后是Controller对象处理请求。
index.jsp ———> DispatcherServlet————> 转发,分配给 ————> Controller对象( @Controller注解创建的对象)
main.jsp MainController
addUser.jsp UserController
...... ......
基于 MVC 架构,功能分工明确,解耦合,
容易理解,上手快;使用简单。
可以开发一个注解的 SpringMVC 项目,SpringMVC 也是轻量级的,jar 很小。不依赖的特定的接口和类。
作为Spring框架一部分,能够使用 Spring 的 IoC 和 Aop 。 方便整合Strtus,MyBatis,Hiberate,JPA 等其他框架。
SpringMVC 强化注解的使用,在控制器,Service ,Dao 都可以使用注解,方便灵活。
使用 @Controller 创建处理器对象,
@Service 创建业务对象,
@Autowired 或者 @Resource 可以在Service 类中注入 Dao,可以在控制器类中注入 Service。(注入即赋值)
SpringMVC 的注解式开发:
在代码中通过对类与方法的注解,便可完成处理器(控制器)在 springmvc 容器的注册。
注解式开发是重点!
项目:【ch01-first-springmvc】
用户在页面发起一个请求, 请求交给springmvc的控制器对象,
并显示请求的处理结果(在结果页面显示一个欢迎语句)。
1.3.1 新建maven版 Web工程
1) 创建空项目,然后在项目下面创建maven模块,选择webapp模板;
2) main目录下添加java目录,并设为Source root。
3) main目录下添加resources目录,并设为Resource root。
4) 修改web.xml的约束版本为4.0,具体方法在【Spring笔记 第6章:Spring与Web】
1.3.2 pom.xml配置
1)加入依赖: spring-webmvc依赖(会间接把spring的依赖都加入到项目)、jsp、servlet依赖
2)修改jdk编译和运行的版本为1.8
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.5.RELEASEversion>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.2.1-b03version>
<scope>providedscope>
dependency>
1.3.3 【重点】注册中央调度器
在web.xml中注册SpringMVC框架的核心对象DispatcherServlet(中央调度器)
1) DispatcherServlet叫做中央调度器, 是一个servlet, 它的父类是继承HttpServlet
2) DispatcherServlet也叫做前端控制器(front controller)
3) DispatcherServlet负责接收用户提交的请求, 调用其它的控制器对象,并把请求的处理结果显示给用户
注册springmvc的核心对象DispatcherServlet。
我们需要在tomcat服务器启动后,就创建DispatcherServlet对象的实例。
为什么要创建DispatcherServlet对象的实例呢?
因为DispatcherServlet在他的创建过程中, 会同时创建springmvc容器对象,
读取springmvc的配置文件,把这个配置文件中的对象都创建好,当用户发起请求时就可以直接使用对象了。
servlet的初始化会执行init()方法。 DispatcherServlet在init()中{
//1、创建容器,读取配置文件
WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
//2、把容器对象放入到ServletContext中
getServletContext().setAttribute(key, ctx);
}
启动tomcat报错,会读取这个文件 /WEB-INF/myspringmvc-servlet.xml
因为springmvc创建容器对象时,读取的配置文件默认是/WEB-INF/-servlet.xml .
所以还需要我们自定义springmvc配置文件的位置和名称:
<servlet>
<servlet-name>myspringmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>myspringmvcservlet-name>
<url-pattern>*.dourl-pattern>
servlet-mapping>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Titletitle>
head>
<body>
<h2>请求页面h2>
<a href="some.do"> 向some.do发送请求a>
body>
html>
package com.yuming.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* @Controller:创建处理器对象,并放在springmvc容器中。
*
* 能处理请求的都是控制器(处理器): MyController能处理请求,
* 也叫做后端控制器(back controller)
*
* 没有注解之前,需要实现各种不同的接口才能做控制器使用
*/
@Controller
public class MyController {
/*
SpringMVC中使用方法来处理用户提交的请求
方法是自定义的, 可以有多种返回值、多种参数,方法名称自定义
*/
/**
* 准备使用doSome方法处理some.do请求。
* @RequestMapping: 请求映射,作用是把一个请求地址和一个方法绑定在一起。
* 一个请求指定一个方法处理。
* 属性: 1. value 是一个String,表示请求的uri地址的(some.do)。
* value的值必须是唯一的, 不能重复。 在使用时,推荐地址以“/”
* 位置:1.在方法的上面,常用的。
* 2.在类的上面
* 说明: 使用RequestMapping修饰的方法叫做处理器方法或者控制器方法。
* 使用@RequestMapping修饰的方法可以处理请求,类似Servlet中的doGet, doPost
*
* 返回值:ModelAndView 表示本次请求的处理结果
* Model: 数据,请求处理完成后,要显示给用户的数据
* View: 视图, 比如jsp等等。
*/
//@RequestMapping(value = "/some.do")
@RequestMapping(value = {"/some.do","/one.do"}) //可以放一个数组,处理多个请求
public ModelAndView doSome(){ // 相当于doGet()--service请求处理
ModelAndView mv = new ModelAndView();
//添加数据, 框架在请求的最后把数据放入到request作用域。
//相当于request.setAttribute("msg","欢迎使用springmvc做web开发");
mv.addObject("msg","欢迎使用SpringMVC做Web开发");
mv.addObject("fun","执行的是doSome方法");
//指定视图, 指定视图的完整路径
//框架对视图执行的请求转发操作,相当于request.getRequestDispather("/show1.jsp).forward(request,response)
//mv.setViewName("/show1.jsp");
//mv.setViewName("/WEB-INF/view/show.jsp");
//mv.setViewName("/WEB-INF/view/other.jsp");
//当后面SpringMVC的配置文件配置了视图解析器后,可以使用逻辑名称(文件名),指定视图
//框架会使用视图解析器的【前缀 + 逻辑名称 + 后缀】组成完成路径。
mv.setViewName("show");
//返回
return mv;
}
//同一个Controller类中,可以写多个方法,处理不同的请求
@RequestMapping(value = {"/other.do","/two.do","/three.do"})
public ModelAndView doOther(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","欢迎使用SpringMVC做Web开发");
mv.addObject("fun","******执行的是doOther方法******");
mv.setViewName("other");
return mv;
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Titletitle>
head>
<body>
<h2>/WEB-INF/view/show.jsp : 从request作用域获取数据h2>
<h3>msg数据:${msg}h3>
<h3>fun数据:${fun}h3>
body>
html>
一般的jsp或者html放在webapp目录下面是可以被用户直接访问到,
而放到 /webapp/WEB-INF下面的,是受保护的,用户不能直接访问的,例如 /webapp/WEB-INF/web.xml。
可以在/webapp/WEB-INF下面创建一个目录:在里面存放一些jsp或者html页面,
这些页面是不希望用户直接通过地址栏访问的,只有通过请求转发才能访问到这个页面
<context:component-scan base-package="com.yuming.controller"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
bean>
当配置了视图解析器后,可以使用逻辑名称(文件名),指定视图
框架会使用视图解析器的【前缀 + 逻辑名称 + 后缀】组成完成路径。
mv.setViewName("show"); //mv.setViewName("/WEB-INF/view/show.jsp");
1、SpringMVC请求的处理流程
1)发起some.do
2)tomcat (web.xml————url-pattern知道 *.do的请求是给DispatcherServlet)
3)DispatcherServlet (根据springmvc.xml配置知道 some.do————给doSome()方法)
4)DispatcherServlet 把some.do转发给 MyController.doSome()方法
5)框架执行doSome()方法,把得到的ModelAndView结果进行处理, 转发到show.jsp
上面的过程简化:
some.do ——————> DispatcherServlet ——————> MyController
(1)tomcat启动,创建容器的过程
通过load-on-start标签指定的数字1,首先创建DisaptcherServlet对象,
DisaptcherServlet的父类是继承HttpServlet的, 它是一个serlvet, 在被创建时,会执行init()方法:
//创建容器,读取配置文件
WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
//把容器对象放入到ServletContext中
getServletContext().setAttribute(key, ctx);
上面创建容器作用:创建 @controller注解所在的类的对象, 并将其放入到 SpringMVC的容器中,
容器是map集合 ,类似 map.put("myController",MyController对象)
(2)请求的处理过程,相当于执行Servlet的service()方法
protected void service(HttpServletRequest request, HttpServletResponse response)
protected void doService(HttpServletRequest request, HttpServletResponse response)
DispatcherServlet.doDispatch(request, response){
调用MyController的.doSome()方法
}
doDispatch方法:SpringMVC中DispatcherServlet的核心方法, 所有的请求都在这个方法中完成的。
项目【ch02-requestmapping】,是复制第一个项目进行改造的
1、 @RequestMapping放在控制器类上面的使用:
通过 @RequestMapping 注解可以定义处理器对于请求的映射规则。value 属性值常以“/”开始。
该注解可以注解在方法上,也可以注解在类上,但意义是不同的。
一个 @Controller 所注解的类中,可以定义多个处理器方法。当然,不同的处理器方法所匹配的 URI 是不同的。
这些不同的 URI 被指定在注解于方法之上的 @RequestMapping 的value 属性中。
但若这些请求具有相同的 URI 部分,则这些相同的 URI,可以被抽取到
注解在类之上的 @RequestMapping 的 value 属性中。
此时的这个 URI 表示模块的名称。URI 的请求是相对于 Web 的根目录。
换个角度说,要访问处理器的指定方法,必须要在方法指定 URI 之前加上处理器类前定义的模块名称
例如,某个控制器类的 每个请求方法 的 @RequestMapping注解为:
@RequestMapping("/user/XXX.XXX") 它们具有相同的部分“/user”,
那么可以在这个控制器类的类名上面添加注解 @RequestMapping("/user"),
而每个请求方法只写 @RequestMapping("/XXX.XXX"),
当然如果要访问这个控制器的指定方法方法的话,还是要访问完整的"/user/XXX.XXX"
2、 指定请求方式method属性
对于 @RequestMapping,其有一个属性 method ,用于对被注解方法所处理请求的提交方式进行限制,
即只有满足该method属性指定的提交方式的请求,才会执行该被注解方法。
method 属性的取值为 RequestMethod 枚举常量。
常用的为 RequestMethod.GET 与 RequestMethod.POST,分别表示提交方式的匹配规则为 GET 与 POST 提交。
当未指定请求方式时,无论哪种请求都会成功;
当请求方式与指定方式不匹配时,会报错,例如:
HTTP Status 405 - Request method ‘GET’ not supported
请求方式 | 提交方式 |
---|---|
表单请求 | 默认get,可以设置为post |
AJAX请求 | 默认get,可以设置为post |
超链接请求 | get |
地址栏请求 | get |
src资源路径请求 | get |
package com.yuming.controller;
import ...
/**
* @RequestMapping:
* value : 所有请求地址的公共部分,叫做模块名称
* 位置: 放在类的上面
*/
@Controller
@RequestMapping("/user")
public class MyController {
//指定some.do使用get请求方式
@RequestMapping(value = {"/some.do","/one.do"}, method = RequestMethod.GET)
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","欢迎使用SpringMVC做Web开发");
mv.addObject("fun","执行的是doSome方法");
mv.setViewName("show"); //mv.setViewName("/WEB-INF/view/show.jsp");
return mv;
}
//指定other.do是post请求方式
@RequestMapping(value = {"/other.do","/two.do","/three.do"},method = RequestMethod.POST)
public ModelAndView doOther(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","欢迎使用SpringMVC做Web开发");
mv.addObject("fun","******执行的是doOther方法******");
mv.setViewName("other");
return mv;
}
//不指定请求方式,没有限制
@RequestMapping(value = "/hello.do")
public ModelAndView doHello(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","欢迎使用SpringMVC做Web开发");
mv.addObject("fun","--------------执行的是doHello方法-------------");
mv.setViewName("other");
return mv;
}
}
处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。
➢ HttpServletRequest
➢ HttpServletResponse
➢ HttpSession
➢ 请求中所携带的请求参数
项目【ch03-receive-param】,是复制第1个项目进行改造的
@RequestMapping(value = "/hello.do")
public ModelAndView doHello(HttpServletRequest request,
HttpServletResponse response,
HttpSession session){
ModelAndView mv = new ModelAndView();
//接收请求参数
String username = request.getParameter("username");
mv.addObject("msg","欢迎使用SpringMVC做Web开发-----------》》》》" + username);
mv.setViewName("other"); // mv.setViewName("/WEB-INF/view/other.jsp");
return mv;
}
只要保证请求参数名与该请求处理方法的参数名相同即可。
---------------------------------------------------------------------
<form action="receiveproperty.do" method="post">
姓名:<input type="text" name="name"> <br/>
年龄:<input type="text" name="age"> <br/>
<input type="submit" value="提交参数">
</form>
---------------------------------------------------------------------
@RequestMapping(value = "/receiveproperty.do")
public ModelAndView doSome(String name, int age){
//可以在方法中直接使用 name , age
ModelAndView mv = new ModelAndView();
mv.addObject("myname",name);
mv.addObject("myage",age);
mv.setViewName("show");
return mv;
}
---------------------------------------------------------------------
<body>
<h3>myname数据:${myname}</h3>
<h3>myage数据:${myage}</h3>
</body>
---------------------------------------------------------------------
在提交请求参数时,使用get请求方式,传中文没有乱码。
而使用post方式提交请求,中文会有乱码!!
之前在servlet中,都是在获取参数之前,设置字符编码:
request.setCharacterEncoding(“utf-8”);
String s = request.getParameter(“username”);
这种方式比较麻烦,因为要在每一个处理器方法都加入这一行代码
现在可以使用过滤器解决乱码问题:
过滤器可以自定义,也可使用框架中提供的过滤器 CharacterEncodingFilter
Spring框架对于请求参数中的中文乱码问题,给出了专门的字符集过滤器:
spring-web-5.2.5.RELEASE.jar 的org.springframework.web.filter 包下的 CharacterEncodingFilter 类
在 web.xml 中注册字符集过滤器,即可解决 Spring 的请求参数的中文乱码问题。
不过,最好将该过滤器注册在其它过滤器之前。因为过滤器的执行是按照其注册顺序进行的。
<filter>
<filter-name>CharacterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
<init-param>
<param-name>forceRequestEncodingparam-name>
<param-value>trueparam-value>
init-param>
<init-param>
<param-name>forceResponseEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharacterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
其中字符集设置的核心方法:
在org.springframework.web.filter.CharacterEncodingFilter下面的doFilterInternal方法
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String encoding = this.getEncoding();
if (encoding != null) {
if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(encoding);
}
if (this.isForceResponseEncoding()) {
response.setCharacterEncoding(encoding);
}
}
filterChain.doFilter(request, response);
}
校正请求参数名,是指若请求 URL 所携带的参数名称与处理方法中指定的参数名不相同时,
则需在处理方法参数前,添加一个注解 @RequestParam(“请求参数名”),指定请求 URL 所携带参数的名称。
该注解是对处理器方法参数进行修饰的。value 属性指定请求参数的名称。
/**
* 请求中参数名和处理器方法的形参名不一样
* @RequestParam: 逐个接收请求参数中, 解决请求中参数名形参名不一样的问题
* 属性: 1. value 请求中的参数名称
* 2. required 是一个boolean,默认是true
* true:表示请求中必须包含此参数,否则报400
* false:可以没有此参数,也不会报400
*/
@RequestMapping(value = "/receiveparam.do")
public ModelAndView receiveParam(@RequestParam(value = "rname",required = false) String name,
@RequestParam(value = "rage",required = false) Integer age){
ModelAndView mv = new ModelAndView();
mv.addObject("myname",name);
mv.addObject("myage",age);
mv.setViewName("show"); // WEB-INF/view/show.jsp
return mv;
}
当请求中的参数比较多的时候,再使用逐个接收的方式就显得麻烦起来,推荐使用对象接收参数。
即创建一个java类,其中包含与请求参数同名的属性,并进行封装。
然后将控制器方法的形参换成此类型。在方法内部我们可以使用 getter方法 获取请求参数。
在请求发送到控制器时,框架会自动调用控制器方法参数类型的 无参构造器 ,并使用 setter方法进行赋值。
--------------------------------------------------------------------------
public class Student {
// 属性名和请求中参数名一样
private String name;
private Integer age;
//getter and setter
//toString
}
--------------------------------------------------------------------------
/**
* 处理器方法形参是java对象, 这个对象的属性名和请求中参数名一样的
* 框架会:
* 1) 调用无参构造方法 创建这个java对象
* 2) 调用set方法给这个java对象的属性赋值。
* 在处理器方法中,我们可以使用get方法获取请求的参数
*/
@RequestMapping(value = "/receiveobject.do")
public ModelAndView receiveObject(Student myStudent){
ModelAndView mv = new ModelAndView();
mv.addObject("myname", myStudent.getName());
mv.addObject("myage", myStudent.getAge());
mv.addObject("mystudent", myStudent);
mv.setViewName("show2"); // WEB-INF/view/show2.jsp
return mv;
}
--------------------------------------------------------------------------
<body>
myname数据:${myname}
myage数据:${myage}
mystudent数据:${mystudent}
</body>
--------------------------------------------------------------------------
项目【ch04-return-value】,复制第三个项目
使用 @Controller 注解的处理器的处理器方法,其返回值常用的有四种类型:
➢ 第一种:ModelAndView
➢ 第二种:String
➢ 第三种:无返回值 void
➢ 第四种:返回自定义类型对象
根据不同的情况,使用不同的返回值。
表示数据和视图,对视图执行请求转发。
若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,
此时处理器方法返回 ModelAndView 比较好。
当然,若要返回 ModelAndView,则处理器方法中需要定义 ModelAndView 对象
上面的例子都是使用的ModelAndView
package com.yuming.controller;
import ...
@Controller
public class MyController {
@RequestMapping(value = "/some.do")
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
//添加数据, 框架在请求的最后把数据放入到request作用域。
mv.addObject("msg","欢迎使用SpringMVC做Web开发");
mv.addObject("fun","执行的是doSome方法");
//指定视图名称
mv.setViewName("show"); //mv.setViewName("/WEB-INF/view/show.jsp");
//返回
return mv;
}
}
表示视图,只完成页面跳转工作。
1)这个字符串可以是逻辑名称,需要配置视图解析器,上面1.3.7节
2)也可以是完整视图路径,就不能配置视图解析器了,否则会出错
SpringMVC配置文件 配置视图解析器:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
bean>
@Controller
public class MyController {
/**
* 处理器方法返回String--表示视图路径,可以是完整路径名称,也可以是逻辑名称(需要配置视图解析器)
* 1)演示逻辑名称:
*/
@RequestMapping(value = "/returnString-view.do")
public String doReturnView(HttpServletRequest request, String name, String age){
//可以自己手工添加数据到request作用域
request.setAttribute("myname",name);
request.setAttribute("myage",age);
// show : 逻辑视图名称,项目中SpringMVC配置文件配置了视图解析器
// 框架对视图执行forward转发操作
return "show"; // WEB-INF/view/show.jsp
}
//处理器方法返回String
// 2)演示完整视图路径, 此时不能配置视图解析器:
@RequestMapping(value = "/returnString-view2.do")
public String doReturnView2(HttpServletRequest request,String name, Integer age){
//可以自己手工添加数据到request作用域
request.setAttribute("myname",name);
request.setAttribute("myage",age);
// 完整视图路径,项目中不能配置视图解析器
// 否则会是这样的:/WEB-INF/view//WEB-INF/view/show.jsp.jsp,报404,找不到资源
return "/WEB-INF/view/show.jsp";
}
}
不能表示数据,也不能表示视图。
若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void。
例如,对于 AJAX 的异步请求的响应。
在处理ajax的时候,可以使用void返回值。
通过HttpServletResponse输出数据,响应ajax请求。
ajax请求,服务器端返回的是数据, 和视图无关。
步骤:
1)在webapp下面,和WEB-INF同级,创建一个目录,命名为js,在里面粘贴一个jquery的js库文件:jquery-3.4.1.js
2)pom文件加入 jackson 依赖
由于本项目中服务端向浏览器传回的是 JSON 数据,需要使用一个工具类
将字符串包装为 JSON 格式,所以需要导入 JSON 的依赖。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
3)创建页面发送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.4.1.js"></script>
<script type="text/javascript">
//在所有dom对象创建好后(页面加载完毕之后),绑定事件
$(function () {
//#btn:id选择器,给id为btn的dom元素绑定鼠标单击事件
$("#btn").click(function () {
//alert("button click");
//发起ajax请求:
$.ajax({
url:"returnVoid-ajax.do",
data:{
name:"zhangsan",
age:20
},
type:"post", //请求的类型
dataType:"json", //期望服务器返回数据的类型
success:function (resp) {
//resp从服务器端返回的是json格式的字符串 {"name":"zhangsan","age":20}
//jquery会把字符串转为json对象, 赋值给resp形参。
alert(resp.name+","+resp.age);
}
})
})
})
</script>
</head>
<body>
<button id="btn">发起ajax请求</button>
</body>
</html>
------------------------------------------------------------------------------
4)在控制器中处理并响应请求
--------------------------------------------------------------------------------------------------------
@RequestMapping(value = "/returnVoid-ajax.do")
public void doReturnVoidAjax(HttpServletResponse response,String name, Integer age) throws IOException {
//处理ajax, 使用json格式的数据
//假设处理结果是一个Student对象
Student student = new Student();
student.setName(name);
student.setAge(age);
//把结果的对象转为json格式的字符串
String json = "";
if( student != null){
ObjectMapper om = new ObjectMapper();
json = om.writeValueAsString(student);
}
//输出数据,响应ajax的请求
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print(json);//返回数据
writer.flush();
writer.close();
}
--------------------------------------------------------------------------------------------------------
Object: 例如String , Integer , Map , List , 自定义对象 等等都是对象,
对象有属性, 属性就是数据。 所以返回Object表示数据, 和视图无关。
可以使用对象表示的数据,响应ajax请求。
返回对象,需要使用 @ResponseBody 注解,将转换后的 JSON 数据放入到响应体中。
在上面2.3.3的示例中,控制器方法的第二、三部分代码(将结果对象转化为json格式的数据和输出数据),
在许多方法中重复使用。只不过需要转化的数据类型不同。
为了减少代码重复,这一部分由框架完成:
1)加入处理json的工具库的依赖, springmvc默认使用的jackson。
2)在sprigmvc配置文件之间加入 <mvc:annotation-driven/> MVC注解驱动。
完成数据类型的转换。
相当于:
json = om.writeValueAsString(student);
3)在处理器方法的上面加入 @ResponseBody注解
作用:放在处理器方法的上面, 通过HttpServletResponse输出数据,响应ajax请求的。
相当于:
response.setContentType("application/json;charset=utf-8");
PrintWriter pw = response.getWriter();
pw.println(json);
< mvc:annotation-driven/>【内部原理】:
<mvc:annotation-driven/> mvc注解驱动
注解驱动实现的功能:完成java对象到json、xml、text、二进制等数据格式的转换。
<mvc:annotation-driven/>在加入到springmvc配置文件后, 会自动创建HttpMessageConverter接口的7个实现类对象,
包括 MappingJackson2HttpMessageConverter(使用jackson工具库中的ObjectMapper实现java对象转为json字符串)
HttpMessageConverter 接口:消息转换器。
功能: 定义了java转为json、xml等数据格式的方法。 这个接口有很多的实现类。
这些实现类完成 java对象到json、java对象到xml、java对象到二进制数据的转换
下面的HttpMessageConverter里面的两个方法是控制器类把结果输出给浏览器时使用的:
boolean canWrite(Class<?> var1, @Nullable MediaType var2);
void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3)
例如处理器方法
@RequestMapping(value = "/returnStudentJson.do")
public Student doReturnView(){
Student student = new Student();
student.setName("lisi");
student.setAge(20);
return student;
}
1)canWrite作用:检查处理器方法的返回值,能不能转为var2表示的数据格式。
检查student(lisi,20)能不能转为var2表示的数据格式。如果能,canWrite返回true
MediaType:表示数据格式的, 例如json、xml等等
2)write:把处理器方法的返回值对象,调用jackson中的ObjectMapper转为json字符串。
相当于 json = om.writeValueAsString(student);
/**
* 处理器方法返回一个Student,通过框架转为json,响应ajax请求
* @ResponseBody:
* 作用:把处理器方法返回对象转为json后,通过HttpServletResponse输出给浏览器。
* 位置:方法的定义上面。 和其它注解没有顺序的关系。
*
* 返回对象,框架的处理流程:
* 1. 框架会把返回Student类型,调用框架的中ArrayList中每个实现类的canWrite()方法
* 检查那个HttpMessageConverter接口的实现类能否处理Student类型的数据
* 最终找到这个类:MappingJackson2HttpMessageConverter
*
* 2.框架会调用实现类的write()方法: MappingJackson2HttpMessageConverter的write()方法
* 把李四同学的student对象转为json格式的数据(内部是调用Jackson的ObjectMapper实现转为json)
* contentType: application/json;charset=utf-8
*
* 3.框架会调用@ResponseBody的功能 把2的结果数据输出到浏览器, ajax请求处理完成
*/
@RequestMapping(value = "/returnStudentJson.do")
@ResponseBody
public Student doStudentJsonObject(String name, Integer age){
//假设已经调用了service,获取到了请求结果数据 , Student对象表示结果数据
Student student = new Student();
student.setName("李四同学");
student.setAge(20);
return student; // 会被框架转为json格式的数据,响应ajax请求
}
(2)返回list集合
处理器方法返回一个list集合,会被框架转为json数组,顺序与List添加元素顺序相同, 响应ajax请求
------------------------------------------------------------------------------
@RequestMapping(value = "/returnStudentJsonArray.do")
@ResponseBody
public List<Student> doStudentJsonObjectArray() {
List<Student> studentList = new ArrayList<>();
Student student = new Student("李四",24);
Student student2 = new Student("张三",23);
studentList.add(student);
studentList.add(student2);
return studentList; //会被转换成 [{"name":"李四","age":24},{"name":"张三","age":23}]
}
------------------------------------------------------------------------------
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="js/jquery-3.4.1.js"></script>
<script type="text/javascript">
$(function () {
$("#btn").click(function () {
$.ajax({
url:"returnStudentJsonArray.do",
dataType:"json",
success:function (resp) {
//使用jquery中的each函数遍历json数组
$.each(resp,function (i,n) {
alert(i + ":"+ n.name+","+n.age)
})
}
})
})
})
</script>
</head>
<body>
<button id="btn">发起ajax请求</button>
</body>
</html>
------------------------------------------------------------------------------
(3)返回字符串对象
处理器方法返回的是String , 这里的String表示数据的,不是上面的视图路径。
区分返回值String是表示数据,还是视图 ?
看有没有 @ResponseBody注解
如果有 @ResponseBody注解,返回String就是数据,反之就是视图
浏览器响应默认使用“text/plain;charset=ISO-8859-1”作为contentType, 导致中文有乱码,
解决方案:给RequestMapping增加一个属性 produces, 使用这个属性指定新的contentType.
------------------------------------------------------------------------------
@RequestMapping(value = "/returnStringData.do", produces = "text/plain;charset=utf-8")
@ResponseBody
public String doStringData(){
return "Hello SpringMVC 返回对象,有 @ResponseBody注解,这里表示数据";
}
------------------------------------------------------------------------------
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="js/jquery-3.4.1.js"></script>
<script type="text/javascript">
$(function () {
$("#btn").click(function () {
$.ajax({
url:"returnStringData.do",
dataType:"text",
success:function (resp) {
alert("返回的是文本数据:"+resp)
}
})
})
})
</script>
</head>
<body>
<button id="btn">发起ajax请求</button>
</body>
</html>
------------------------------------------------------------------------------
2.4.1、配置详解
(1) * .do
在没有特殊要求的情况下,SpringMVC 的中央调度器 DispatcherServlet 的< url-pattern/>
常使用后辍匹配方式,如写为*.do 或者 *.action, *.mvc 等。
(2)/
可以写为/。但 DispatcherServlet 会将向静态资源的获取请求,例如图片、html、css、js资源的获取请求,
当作是一个普通的 Controller 请求。中央调度器会调用处理器映射器为其查找相应的处理器。
当然也是找不到的,所以在这种情况下,所有的静态资源获取请求也均会报 404 错误。
项目:【ch05-url-pattern】。在第4个项目基础上进行修改。
需求: 在index.jsp页面中存在一个访问图片的链接。
该项目用于演示将中央调度器的<url-pattern/>写为*.do可以访问到该图片,而写为/,则无法访问。
1)在项目中添加图片
在项目的 webapp 下添加一个目录 images,并在其中添加一张图片资源:p1.jpg
2)修改 index.jsp页面
<body>
<img src="images/p1.jpg" alt="图片加载失败">
</body>
3)修改web.xml里面 中央调度器的<url-pattern/> 的值
保持<url-pattern/>的值为 *.do,扩展名方式,图片会正常显示。
将<url-pattern/>的值修改为 / ,则图片将无法显示。
分析:
=======================================================================================================
发起的请求是由哪些服务器程序处理的?
http://localhost:8080/myweb/index.jsp : tomcat(jsp会转为servlet)
http://localhost:8080/myweb/js/jquery-3.4.1.js : tomcat
http://localhost:8080/myweb/images/p1.jpg : tomcat
http://localhost:8080/myweb/html/test.html : tomcat
http://localhost:8080/myweb/some.do : DispatcherServlet(SpringMVC框架处理的)
tomcat本身能处理静态资源的访问。 图片、html、css、js文件都是静态资源。
在tomcat安装目录/conf/web.xml下有一个默认servlet, 名称是 default ,是在服务器启动时被创建的:
<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>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern> 表示静态资源和未映射的请求都由这个default处理
</servlet-mapping>
可以在该文件中看到这个默认servlet的作用:
The default servlet for all web applications, that serves static resources.
It processes all requests that are not mapped to other servlets with servlet mappings
(defined either here or in your own web.xml file).
即:1)可以处理静态资源
2)可以处理未映射到其它servlet的请求
=======================================================================================================
再来看我们项目中web.xml配置中央调度器的<url-pattern/>的值:
使用框架的时候, url-pattern可以使用两种值
1. 使用扩展名方式, 语法 *.xxxx , xxxx是自定义的扩展名。 常用的方式 *.do, *.action, *.mvc等等
不能使用 *.jsp
http://localhost:8080/myweb/some.do、 http://localhost:8080/myweb/other.do
2.使用斜杠 "/"
当你的项目中使用了 / ,它会替代 tomcat中的default的servlet。
导致所有的静态资源也都给DispatcherServlet处理, 默认情况下DispatcherServlet没有处理静态资源的能力。
没有控制器对象能处理静态资源的访问。所以静态资源(html、css、js、图片)都是404.
动态资源some.do是可以访问,是因为我们程序中有MyController控制器对象,能处理some.do请求。
2.4.2、静态资源访问
在SpringMVC中,中央调度器处理各种请求,因此需要写各种url-patrern,如果只写/会很方便。
但是会造成静态资源无法访问。
当中央调度器的url-pattern使用 / ,如何解决静态资源无法访问的问题?
方法1 :使用<mvc:default-servlet-handler/>
在springmvc配置文件加入 <mvc:default-servlet-handler/>
加入这个标签后,框架会创建控制器对象DefaultServletHttpRequestHandler(类似我们自己创建的MyController).
这个对象可以把接收的请求转发给 tomcat的default这个servlet。(requestDispatcher.forward)
方法2 :使用<mvc:resources/>
在springmvc.xml配置文件中添加:
<mvc:resources mapping="/images/**" location="/images/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/html/**" location="/html/"/>
<mvc:resources mapping="/static/**" location="/static/"/>
location 表示静态资源所在目录。目录是在webapp下面,不要使用/WEB-INF/及其子目录。
mapping 表示访问静态资源的uri地址, 使用通配符 **
加入这个标签后,框架会创建ResourceHttpRequestHandler对象来处理静态资源的访问。
这个对象是 Spring 定义的专门用于处理静态资源访问请求的处理器,不需要依赖tomcat服务器。
注意:需要声明注解驱动
上面2种方式均需要在 springmvc 配置文件加入 MVC注解驱动, 解决访问静态资源和动态资源冲突的问题
<mvc:annotation-driven/>
注:在2.3.4节,也使用过该标签,但是作用不同
项目【ch06-path】 ,复制【ch02-requestmapping】改造
解决jsp页面中的路径问题, 用 “/” 还是不用
地址分类:
(1)绝对地址: 带有协议名称的是绝对地址。 http://www.baidu.com 、 ftp://202.122.23.1
(2)相对地址: 没有协议开头的。 例如 user/some.do、 /user/some.do
相对地址不能独立使用,必须有一个参考地址。 通过【参考地址+相对地址】才能指定资源。
(3)参考地址
1)在页面中,访问地址加 "/"
当前页面: http://localhost:8080/myweb/index.jsp
资源: index.jsp
在index.jsp发起 /user/some.do 请求,访问地址变为下面这个,报404(缺少项目的名称)
http://localhost:8080/user/some.do
参考地址是 【服务器地址】
http://localhost:8080
点击链接时, 访问地址是当前页面的参考地址+ 链接的地址:
http://localhost:8080 + /user/some.do
解决方法:
方法一:在链接 /user/some.do 前面手动添加项目名称
发起some.do的请求
方法二:加入EL表达式 ${pageContext.request.contextPath},这种方法更灵活
发起some.do的请求
2) 在页面中,访问地址不加 "/"
当前页面: http://localhost:8080/myweb/index.jsp
当前资源: index.jsp
在index.jsp发起 user/some.do请求,访问地址变为
http://localhost:8080/myweb/user/some.do
参考地址是 【当前页面地址去掉资源名称后的地址】
http://localhost:8080/myweb/
点击链接时, 访问地址是当前页面的参考地址+ 链接的地址:
http://localhost:8080/myweb/ + user/some.do
--------------------------------------------------------------------------------------------
修改 MyController类的doSome方法(/user/some.do),使其再转发到index.jsp,即
index.jsp ——————》 MyController类 ——————》index.jsp
当前页面: http://localhost:8080/myweb/index.jsp
index.jsp 访问 user/some.do , 返回后现在的地址:
http://localhost:8080/myweb/user/some.do (页面显示的内容还是index.jsp的内容)
参考地址: http://localhost:8080/myweb/user/
资源: some.do
然后 再继续访问 user/some.do ,就变为
http://localhost:8080/myweb/user/user/some.do
解决方法:
方法一:加入 /项目名称
方法二:加入 ${pageContext.request.contextPath}
方法三:加入一个base标签, 是html语言中的标签。 表示当前页面中访问地址的基地址。
你的页面中所有 没有“/”开头的地址,都是以base标签中的地址为参考地址
使用base中的地址 + user/some.do 组成访问地址
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String basePath = request.getScheme() + "://" +
request.getServerName() + ":" + request.getServerPort() +
request.getContextPath() + "/";
%>
<html>
<head>
<title>Titletitle>
<base href="<%=basePath%>">
head>
<body>
<h2>请求页面h2>
<a href="user/some.do"> 发起user/some.do的请求 a> <br>
<a href="/user/some.do"> 发起 /user/some.do的请求 a> <br> <br>
body>
html>
SSM 编程,即 SpringMVC + Spring + MyBatis 整合,是当前最为流行的 JavaEE 开发技术架构。
其实SSM整合的实质,仅仅就是将 MyBatis 整合入 Spring。因为SpringMVC原本就是Spring的一部分,不用专门整合。
SSM 整合的实现方式可分为两种:
1)基于 XML 配置方式
2)基于注解方式。
SpringMVC: 视图层、界面层,负责接收请求,显示处理结果的。
Spring: 业务层,管理service、dao、工具类对象的。
MyBatis: 持久层,访问数据库的
用户发起请求 ———> SpringMVC接收 ———> Spring中的Service对象 ———> MyBatis处理数据
SSM整合也叫做SSI (IBatis也就是MyBatis的前身), 整合中有容器
1.第一个容器SpringMVC容器, 管理Controller控制器对象的
2.第二个容器Spring容器, 管理Service,Dao,工具类对象的
开发人员要做的把使用的对象交给合适的容器创建、管理
1.把Controller对象、Web开发的相关对象 交给SpringMVC容器管理, 定义在springmvc配置文件中
2.把service、dao对象 交给Spring容器管理,定义在spring配置文件中
SpringMVC容器和Spring容器是有关系的,关系已经确定好了:
SpringMVC容器是Spring容器的子容器, 类似java中的继承。 子可以访问父的内容
在子容器中的Controller可以访问父容器中的Service对象, 就可以实现controller使用service对象
项目【ch07-ssm】
数据库名:springdb;
表名:student(id auto_increment, name, age)
1)创建空项目,然后在项目下面创建maven模块,选择webapp模板;
2) main目录下添加java目录,并设为Source root。
3) main目录下添加resources目录,并设为Resource root。
4) 修改web.xml的约束版本为4.0,具体方法在【Spring笔记 第6章:Spring与Web】
springmvc、spring、mybatis三个框架的依赖;
jackson依赖、mysql驱动、druid连接池、jsp、servlet依赖
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.yuminggroupId>
<artifactId>ch07-ssmartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>5.2.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.2.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.1version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>1.3.1version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-coreartifactId>
<version>2.9.0version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.9.0version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.9version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.12version>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.2.1-b03version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
dependencies>
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
project>
1) 注册中央调度器:DispatcherServlet
目的: 1.创建springmvc容器对象,才能创建Controller类对象。
2.创建的是Servlet,才能接受用户的请求。
2)注册spring的监听器:ContextLoaderListener
目的: 使用监听器,当全局作用域对象被创建时,创建Spring容器对象,并存入ServletContext中
创建了spring的容器对象,才能创建service、dao等对象。
3)注册字符集过滤器:CharacterEncodingFilter
目的: 解决post请求的请求参数中文乱码的问题
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>myspringmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:conf/springmvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>myspringmvcservlet-name>
<url-pattern>*.dourl-pattern>
servlet-mapping>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:conf/applicationContext.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<filter>
<filter-name>CharacterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
<init-param>
<param-name>forceRequestEncodingparam-name>
<param-value>trueparam-value>
init-param>
<init-param>
<param-name>forceResponseEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharacterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
1)数据库的属性配置文件
2)spring配置文件
3)springmvc配置文件
4)mybatis主配置文件
Jdbc 属性配置文件 jdbc.properties
jdbc.url=jdbc:mysql://localhost:3306/springdb
jdbc.username=root
jdbc.password=123456
spring配置文件
<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 https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:conf/jdbc.properties"/>
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="configLocation" value="classpath:conf/mybatis.xml"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.yuming.dao"/>
bean>
<context:component-scan base-package="com.yuming.service"/>
beans>
springmvc配置文件
<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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.yuming.controller"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
bean>
<mvc:annotation-driven/>
beans>
mybatis主配置文件
<configuration>
<typeAliases>
<package name="com.yuming.domain"/>
typeAliases>
<mappers>
<package name="com.yuming.dao"/>
mappers>
configuration>
//实体类
package com.yuming.domain;
public class Student {
private Integer id;
private String name;
private Integer age;
//set、get方法
}
//dao接口
package com.yuming.dao;
import com.yuming.domain.Student;
import java.util.List;
public interface StudentDao {
//接口里面的抽象方法的public abstract可以省略不写
int insertStudent(Student student);
List<Student> selectStudents();
}
mapper映射文件(StudentDao.xml)
<mapper namespace="com.yuming.dao.StudentDao">
<select id="selectStudents" resultType="com.yuming.domain.Student">
select id,name,age from student order by id asc
select>
<insert id="insertStudent">
insert into student(name,age) values(#{name},#{age})
insert>
mapper>
service接口和实现类:
package com.yuming.service;
import com.yuming.domain.Student;
import java.util.List;
public interface StudentService {
int addStudent(Student student);
List<Student> findStudents();
}
package com.yuming.service;
import com.yuming.dao.StudentDao;
import com.yuming.domain.Student;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/*
@Service注解
放在service的实现类上面,用于创建service对象,service对象是做业务处理,可以有事务等功能的
*/
@Service
public class StudentServiceImpl implements StudentService {
//引用类型自动注入,使用@Autowired或者@Resource,给其赋值
//另外,该接口的实现类对象是 spring的配置文件的MyBatis扫描器 创建的
@Resource
private StudentDao dao;
@Override
public int addStudent(Student student) {
int nums = dao.insertStudent(student);
return nums;
}
@Override
public List<Student> findStudents() {
List<Student> students = dao.selectStudents();
return students;
}
}
controller控制器类:
package com.yuming.controller;
import com.yuming.domain.Student;
import com.yuming.service.StudentService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import java.util.List;
/*
控制器(处理器)类
1)在类的上面加入 @Controller注解。会创建对象,并放入到springmvc容器中
2)在类中的方法上面加入 @RequestMapping注解,来请求映射
*/
@Controller
@RequestMapping("/student") //表示student模块,那么完整访问路径就是 【/student + 下面方法的路径】
public class StudentController {
//引用类型自动注入,使用@Autowired或者@Resource,给其赋值
//另外,该service的实现类对象是 spring的配置文件里的组件扫描器 创建的
@Resource
private StudentService service;
//注册学生
@RequestMapping("/addStudent.do")
public ModelAndView addStudent(Student student){
ModelAndView mv = new ModelAndView();
String tips = "注册失败";
//调用service处理student
int nums = service.addStudent(student);
if( nums > 0 ){
//注册成功
tips = "学生【" + student.getName() + "】注册成功";
}
//添加数据,框架在请求的最后把数据放入到request作用域。
mv.addObject("tips",tips);
//指定结果页面
mv.setViewName("result"); //实际上是/WEB-INF/jsp/result.jsp,但已在SpringMVC配置文件中声明了视图解析器
return mv;
}
//处理查询,响应ajax请求,输出数据
@RequestMapping("/queryStudent.do")
@ResponseBody
public List<Student> queryStudent(){
//参数检查, 简单的数据处理..等等
//调用service查询student
List<Student> studentList = service.findStudents();
return studentList;
}
}
1、首页index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String basePath = request.getScheme() + "://" +
request.getServerName() + ":" + request.getServerPort() +
request.getContextPath() + "/";
%>
<html>
<head>
<title>功能入口title>
<base href="<%=basePath%>">
head>
<body>
<div align="center">
<h2>SSM整合的例子-----student表的操作h2>
<img src="images/ssm.jpg"/>
<table>
<tr>
<td> <a href="addStudent.jsp">注册学生a> td>
tr>
<tr>
<td> <a href="listStudent.jsp">浏览学生a> td>
tr>
table>
div>
body>
html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String basePath = request.getScheme() + "://" +
request.getServerName() + ":" + request.getServerPort() +
request.getContextPath() + "/";
%>
<html>
<head>
<title>Titletitle>
<base href="<%=basePath%>">
head>
<body>
<div align="center">
<form action="student/addStudent.do" method="post">
<table>
<tr>
<td>姓名:td>
<td><input type="text" name="name">td>
tr>
<tr>
<td>年龄:td>
<td><input type="text" name="age">td>
tr>
<tr>
<td>td>
<td><input type="submit" value="注册">td>
tr>
table>
form>
div>
body>
html>
3、注册结果result.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Titletitle>
head>
<body>
<h2>/WEB-INF/jsp/result.jsp 注册结果页面h2>
结果:${tips}
body>
html>
4、浏览页面listStudent.jsp-----使用ajax请求
1)在webapp下面,创建js目录,在里面粘贴一个jquery的js库文件:jquery-3.4.1.js
2)pom文件加入 jackson 依赖,用于处理json数据
3)创建页面发送ajax请求:listStudent.jsp
4)在sprigmvc配置文件之间加入 MVC注解驱动。作用:完成java对象到json数据格式的转换。
5)在控制器方法的上面加入 @ResponseBody 注解。作用:通过HttpServletResponse输出给浏览器。
注:开发中,第3步一般是最后写的,完成其4他步之后,可以测试以下,直接访问第5步的控制器方法对应的url:
http://localhost:8080/myweb/student/queryStudent.do
页面结果应该是一个json的数组:[{"id":2,"name":"lisi","age":25},{"id":1,"name":"张三","age":22} ......]
那么就说明其他4步是没有问题的,然后再写第3步的页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String basePath = request.getScheme() + "://" +
request.getServerName() + ":" + request.getServerPort() +
request.getContextPath() + "/";
%>
<html>
<head>
<base href="<%=basePath%>">
<title>查询学生,使用ajax请求title>
<script type="text/javascript" src="js/jquery-3.4.1.js">script>
<script type="text/javascript">
//在所有dom对象创建好后(页面加载完毕之后),执行里面的
$(function () {
loadStudentData();//调用函数,发起ajax。应该是页面打开之后就看到所有的学生信息了
//#btnLoader:id选择器,给id为btnLoader的dom元素绑定鼠标单击事件
$("#btnLoader").click(function () {
//loadStudentData();
//怎样获取选中的列表框的值?
// 1、表单对象过滤器----$("选择器 > option : selected") 获取下拉列表框被选中元素
// 2、jquery的val()函数,获取dom对象的value属性值
alert($("#country > option:selected").val()); //1
// query的text()函数,获取dom对象的 文字显示内容
alert($("#country > option:selected").text()); //中国
})
})
//自定义函数
function loadStudentData() {
//发起ajax请求:
$.ajax({
url:"student/queryStudent.do",
type:"get",
dataType:"json", //期望服务器返回数据的类型
success:function (data) { //data是json数组
//alert(data); //先测试一下是否可以弹出这个窗口,再写下面的
//清除旧的数据
$("#stuBody").html("");
//增加新的数据。使用jquery中的each函数遍历json数组
$.each(data,function (i,n) {
$("#stuBody").append("")
.append(""+n.id+" ")
.append(""+n.name+" ")
.append(""+n.age+" ")
.append(" ")
})
}
})
}
script>
head>
<body>
<div align="center">
<table>
<thead>
<tr>
<td>学号td>
<td>姓名td>
<td>年龄td>
tr>
thead>
<tbody id="stuBody">
tbody>
table>
<input type="button" id="btnLoader" value="查询数据">
<select id="country">
<option value="1">中国option>
<option value="2">俄罗斯option>
<option value="3">西班牙option>
select>
div>
body>
html>
当处理器(控制器)对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:【请求转发】和【重定向】
而根据所要跳转的资源类型,又可分为两类: 【跳转到页面】与【跳转到其它处理器】
回顾在【Web后端之Servlet】中学习的内容 —————— 请求转发和重定向的区别:
1)转发是在服务器端完成的,重定向是在客户端发生的;
2)转发的速度快,重定向速度慢;
3)转发是同一次请求,重定向是两次请求;
4)转发地址栏没有变化,重定向地址栏有变化;
5)转发必须是在同一台服务器下完成,重定向可以在不同的服务器下完成。
注意:对于请求转发的页面,可以是WEB-INF中的页面;而重定向的页面,是不能为WEB-INF中页的。
因为重定向相当于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资源的。
SpringMVC 框架把原来 Servlet 中的请求转发和重定向操作进行了封装。现在可以使用简单的方式实现转发和重定向。
forward: 表示转发,实现 request.getRequestDispatcher(“资源文件名”).forward(request,response);
redirect: 表示重定向,实现 response.sendRedirect(“请求地址”);
forward 和 redirect 都是关键字, 有一个共同的特点:
不和视图解析器一同工作,当作项目中没有视图解析器,
也就是可以转发或重定向至视图解析器指定资源以外的资源!
回顾:视图解析器 是配置在SpringMVC配置文件当中,帮助开发人员设置视图文件的路径的(前缀和后缀)
语法:ModelAndView对象.setViewName("forward:相对项目根的完整路径");
框架对请求转发的操作:
(1)在请求转发给 show1.jsp的同时,可以携带存放在request作用域中的数据
(2)在目标页面 show1.jsp 可以直接使用EL表达式获取request作用域中的数据: ${fun}
(3)请求转发 可以访问/WEB-INF资源
index.jsp ————> /doForward.do ————> 转发 ————> /show1.jsp 或者 /WEB-INF/view/show.jsp
--------------------------------------------------------------------------------------------------
@RequestMapping(value = "/doForward.do")
public ModelAndView doForward(){
ModelAndView mv = new ModelAndView();
//数据放入到 request作用域
mv.addObject("fun","执行的是 doForward 方法");
//mv.setViewName("show"); //框架默认的转发
//mv.setViewName("forward:/WEB-INF/view/show.jsp"); //使用forward的 显式转发
mv.setViewName("forward:/show1.jsp"); //show1.jsp不在视图解析器指定的位置,也可实现转发
return mv;
}
--------------------------------------------------------------------------------------------------
语法:ModelAndView对象.setViewName("redirect:相对项目根的完整路径");
框架对重定向的操作:
(1)框架会把 Model 中的简单类型的数据,转为String使用,作为 访问 show2.jsp的 get请求参数使用。
目的是在 doRedirect.do 和 show2.jsp 两次请求之间传递数据
(2)在目标页面 show2.jsp 可以使用 EL表达式的内置对象 param,获取get请求参数值: ${param.myname}
也可以使用:<%=request.getParameter("myname")%> 获取请求参数值
(3)重定向 不能访问/WEB-INF资源
(4)重定向除了访问本网站资源,还可以访问其他网站资源
index.jsp ————> /doRedirect.do ————> 重定向 ————> /show2.jsp 或者 其他网站资源
--------------------------------------------------------------------------------------------------
@RequestMapping(value = "/doRedirect.do")
public ModelAndView doRedirect(String name,Integer age){
ModelAndView mv = new ModelAndView();
mv.addObject("myname",name);
mv.addObject("myage",age);
//重定向
mv.setViewName("redirect:/show2.jsp");
//相当于 http://localhost:8080/myweb/show2.jsp?myname=李四&myage=25
//重定向也可以访问其他网站资源
//mv.setViewName("redirect:https://www.baidu.com/");
//相当于 https://www.baidu.com/?myname=李四&myage=25
//重定向不能访问/WEB-INF资源
//mv.setViewName("redirect:/WEB-INF/view/show.jsp");
return mv;
}
--------------------------------------------------------------------------------------------------
3、 补充
forward和redirect他们都可以访问 视图文件,比如某个jsp ,html
forward:/hello.jsp forward:/main.html
forward和redirect他们都可以访问其它的controller
forward:/some.do , redirect:/other.do
处理器方法返回ModelAndView、String、void 都可以使用forward 和 redirect
4、从url的get请求参数中 获取数据
访问http://localhost:8080/myweb/show2.jsp?myname=lisi&myage=20:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Titletitle>
head>
<body>
<h2>/show2.jsp : 从url的get请求参数中 获取数据h2>
1、使用 参数集合对象param 结合EL表达式 获取到get请求中的参数 $ { param.myname } 、 $ { param.myage }
<h3>myname数据:${param.myname}h3>
<h3>myage数据: ${param.myage} h3> <br>
2、或者< % = request.getParameter("myname") % >
<h3>获取参数数据:<%=request.getParameter("myname")%>h3>
body>
html>
SpringMVC框架采用的是统一全局的异常处理方案。
把所有的controller类中的所有异常处理都集中到一个地方去处理。
使用AOP的思想,把异常处理的代码与业务逻辑代码分开,达到解耦合的目的。
需要使用到2个注解
(1) @ControllerAdvice
(2) @ExceptionHandler
项目:【ch09-exception-handler】
实现步骤:
(1)新建maven版 web项目,加入依赖:spring-webmvc、jsp、Servlet依赖
(2)配置web.xml文件:注册中央调度器DispatcherServlet
(3)创建一个自定义异常类 MyUserException , 再定义它的子类NameException、AgeException
自定义的异常类需要继承java.lang.Exception,并至少给出无参构造器和带有String参数的构造器。
(4)创建controller类,在controller类中方法抛出NameException 、 AgeException异常
(5)创建一个普通类,作用全局异常处理类
1)在类的上面加入 @ControllerAdvice
2) 在类中定义方法,方法的上面加入 @ExceptionHandler
(6)创建异常响应的视图页面
nameError.jsp、ageError.jsp、defaultError.jsp
(7)创建springmvc的配置文件
1)声明 springmvc框架中的视图解析器
2)注册组件扫描器 , 扫描 @Controller注解所在包名,创建controller对象
3)注册组件扫描器 , 扫描 @ControllerAdvice所在包名,创建对象
4)声明 mvc注解驱动
主要代码:
index.jsp ————> /some.do ————> 验证输入合法, 转发 ————> /show.jsp
index.jsp ————> /some.do ————> name不合法,抛name异常 ————> 全局异常处理类 ————> 处理name异常的方法 ————> nameError.jsp
index.jsp ————> /some.do ————> age不合法,抛age异常 ————> 全局异常处理类 ————> 处理age异常的方法 ————> ageError.jsp
index.jsp ————> /some.do ————> 其他不合法,抛异常 ————> 全局异常处理类 ————> 处理其他异常的方法 ————> defaultError.jsp
(1)controller类:
@Controller
public class MyController {
@RequestMapping(value = "/some.do")
public ModelAndView doSome(String name,Integer age) throws MyUserException { //把异常抛出给框架,不要自己try catch
ModelAndView mv = new ModelAndView();
//根据请求参数抛出异常
if (!"lisi".equals(name)) {
throw new NameException("姓名不正确!!!");
}
if (age == null || age > 80) {
throw new AgeException("年龄不合适!!!");
}
//数据放入到 request作用域
mv.addObject("myname",name);
mv.addObject("myage",age);
mv.setViewName("show"); // 转发到/WEB-INF/view/show.jsp
return mv;
}
}
(2)全局异常处理类:
/**
* @ControllerAdvice : 控制器增强(给控制器类增加功能——异常处理功能)
* 特点:必须让框架知道这个注解所在的包名,需要在springmvc配置文件声明组件扫描器, 指定 @ControllerAdvice所在的包名
*/
@ControllerAdvice
public class GlobalExceptionHandler {
//定义方法,处理发生的异常
/**
* @ExceptionHandler(异常的class):表示异常的类型,当发生此类型异常时,由当前方法处理
*
* 处理异常的方法和控制器方法的定义一样,
* 可以有多个参数,可以是ModelAndView、String、void、对象类型的返回值
*
* 形参:Exception,表示Controller中抛出的异常对象。通过形参可以获取发生的异常信息。
*/
@ExceptionHandler(value = NameException.class)
public ModelAndView doNameException(Exception exception) {
//处理NameException异常
/*
异常发生处理逻辑:
1.需要把异常记录下来。记录到数据库,日志文件等。
记录异常发生的时间、哪个方法发生的、异常错误内容等。
2.发送通知,把异常的信息通过邮件、短信、微信发送给相关人员。
3.给用户友好的提示。
*/
ModelAndView mv = new ModelAndView();
//添加提示信息,数据放入在request作用域
mv.addObject("msg", "姓名必须是lisi,其它用户不能访问");
mv.addObject("ex", exception);
// 转发到/WEB-INF/view/nameError.jsp
mv.setViewName("nameError");
return mv;
}
//处理AgeException异常
@ExceptionHandler(value = AgeException.class)
public ModelAndView doAgeException(Exception exception) {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "你的年龄不能大于80");
mv.addObject("ex", exception);
mv.setViewName("ageError");
return mv;
}
//处理其它异常, NameException, AgeException以外,不知类型的异常
//注解里面不加value属性值
@ExceptionHandler
public ModelAndView doOtherException(Exception exception){
//处理其它异常
ModelAndView mv = new ModelAndView();
mv.addObject("msg","未知类型的异常");
mv.addObject("ex",exception);
mv.setViewName("defaultError");
return mv;
//要演示这个异常处理方法,可以输入age为非整数的,比如小数,或者abc这种字符串,就会提示NumberFormatException
}
}
(3)异常响应的视图页面 nameError.jsp
<body>
<h2>/WEB-INF/view/nameError.jsp 发生了异常h2>
提示信息: ${msg} <br/>
系统异常消息:${ex.message}
body>
4.3.1 介绍
1)拦截器Interceptor 是SpringMVC中的一种对象,需要实现HandlerInterceptor接口。
2)拦截器和过滤器类似,功能方向侧重点不同。
过滤器是用来过滤请求参数,设置编码字符集等工作。
拦截器是拦截用户的请求,对请求做判断处理的。
(更多区别看下面4.3.4节和百度)
3)拦截器是全局的,可以对多个Controller做拦截。
一个项目中可以有0个或多个拦截器,它们在一起拦截用户的请求。
常用在:用户登录处理、权限检查、记录日志等。
拦截器的使用步骤:
1)定义HandlerInterceptor接口实现类
2)在SpringMVC配置文件中,声明拦截器,让框架知道拦截器的存在。
拦截器的执行时间:
1)在请求处理之前,也就是controller类中的方法执行之前先被拦截。
2)在控制器方法执行之后也会执行拦截器。
3)在整个请求处理完成后也会执行拦截器。
拦截器:看做是多个Controller中公用的功能,集中到拦截器统一处理。使用的 AOP的思想
4.3.2 一个拦截器的执行
项目【ch10-interceptor】:
(1)新建maven版 web项目,加入依赖:spring-webmvc、jsp、Servlet依赖
(2)配置web.xml文件:注册中央调度器DispatcherServlet
(3)创建Controller类
(4)创建一个普通类,作为拦截器使用
1)实现HandlerInterceptor接口
2)实现接口中的三个方法
(5)创建show.jsp
(6)创建springmvc的配置文件
1) 组件扫描器 ,扫描 @Controller注解,创建对象
2) 声明视图解析器
3) 声明拦截器:指定拦截的请求uri地址、声明拦截器对象
主要代码:
(1)拦截器类
-----------------------------------------------------------------------------------------------------------------------------
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
//拦截器类:拦截用户的请求。
public class MyInterceptor implements HandlerInterceptor {
//可以自定义一个成员变量,用于计算从preHandle到请求处理完成执行了多长时间
private long startTime = 0;
/*
* preHandle 预处理方法。
* 重要:是整个项目的入口、门户。
* 当preHandle返回true 表示请求可以被处理。preHandle返回false,表示请求到此方法就截止。
*
* 参数:
* 1)HttpServletRequest request,
* 2)HttpServletResponse response
* 3)Object handler : 被拦截的处理器(控制器)对象
*
* 返回值boolean
* true:请求是通过了拦截器的验证,可以执行处理器方法。
* ---------------------------------------------
拦截器的MyInterceptor的preHandle()方法执行了
***MyController的doSome方法***
拦截器的MyInterceptor的postHandle()执行了
拦截器的MyInterceptor的afterCompletion()执行了
* ---------------------------------------------
* false:请求没有通过拦截器的验证,请求到达拦截器就截止了。 请求没有被处理
* ---------------------------------------------
* 拦截器的MyInterceptor的preHandle()方法执行了
* ---------------------------------------------
*
* 特点:
* 1)方法在控制器方法(MyController的doSome)之前先执行的。用户的请求首先到达此方法
* 2)在这个方法中可以获取请求的信息, 验证请求是否符合要求。
* 可以验证用户是否登录、验证用户是否有权限访问某个连接地址(url)。
* 如果验证失败,可以截断请求,请求不能被处理;
* 如果验证成功,可以放行请求,此时控制器方法才能执行。
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
startTime=System.currentTimeMillis();
System.out.println("拦截器的MyInterceptor的preHandle()方法执行了");
//计算的业务逻辑,根据计算结果,返回true或者false
//给浏览器一个返回结果
//request.getRequestDispatcher("/tips.jsp").forward(request,response);
return true;
}
/*
postHandle:后处理方法。
参数:
1)HttpServletRequest request,
2)HttpServletResponse response
3)Object handler:被拦截的处理器对象MyController
4)ModelAndView mv:处理器方法的返回值
特点:
1)在处理器方法之后执行的(MyController.doSome()之后)
2)能够获取到处理器方法的返回值ModelAndView
可以修改ModelAndView中的数据和视图,可以影响到最后的执行结果。
3)主要是对原来的执行结果做二次修正
ModelAndView mv = MyController.doSome();
postHandle(request,response,handler,mv);
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器的MyInterceptor的postHandle()执行了");
//对原来的doSome执行结果,需要调整。
if( modelAndView != null){
//添加数据
modelAndView.addObject("mydate",new Date());
//修改视图
modelAndView.setViewName("other");
}
}
/*
afterCompletion:请求完成之后、最后执行的方法
参数
1)HttpServletRequest request,
2)HttpServletResponse response
3)Object handler: 被拦截器的处理器对象
4)Exception ex:程序中发生的异常
特点:
1)在请求处理完成后执行的。框架中规定是当你的视图处理完成后,对视图执行了forward转发。就认为请求处理完成。
2)一般做资源回收工作的, 程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收。
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("拦截器的MyInterceptor的afterCompletion()执行了");
long endTime = System.currentTimeMillis();
System.out.println("计算从preHandle到请求处理完成的时间(毫秒):"+(endTime - startTime ));
}
}
------------------------------------------------------------------------------------------------------------------------------
(2)SpringMVC的配置文件:注册拦截器
--------------------------------------------------------
<!--声明拦截器:可以有0个或多个-->
<mvc:interceptors>
<mvc:interceptor>
<!--指定拦截的请求uri地址
path:就是uri地址,可以使用通配符 **。 /**表示拦截所有请求
-->
--------------------------------------------------------
4.3.3 多个拦截器
项目【ch11-interceptor2】:
1)在【ch10-interceptor】项目基础上添加一个拦截器类;
2)在SpringMVC的配置文件中注册第二个拦截器
测试多个拦截器的执行顺序:
1)第一个和第二个拦截器的preHandle方法分别返回 true , true
11111拦截器的MyInterceptor的preHandle()方法执行了
22222拦截器的MyInterceptor的preHandle()方法执行了
***MyController的doSome方法***
22222拦截器的MyInterceptor的postHandle()执行了
11111拦截器的MyInterceptor的postHandle()执行了
22222拦截器的MyInterceptor的afterCompletion()执行了
11111拦截器的MyInterceptor的afterCompletion()执行了
2)第一个和第二个拦截器的preHandle方法分别返回 true , false
11111拦截器的MyInterceptor的preHandle()方法执行了
22222拦截器的MyInterceptor的preHandle()方法执行了
11111拦截器的MyInterceptor的afterCompletion()执行了
3)第一个和第二个拦截器的preHandle方法分别返回 false , true或false
11111拦截器的MyInterceptor的preHandle()方法执行了
当有多个拦截器时,形成拦截器链。拦截器链的执行顺序,与其注册顺序一致。
需要再次强调一点的是,当某一个拦截器的 preHandle()方法返回 true 并被执行到时,
会向一个专门的方法栈中放入该拦截器的 afterCompletion()方法。
多个拦截器中方法与处理器方法的执行顺序如下图:
从图中可以看出,只要有一个 preHandle()方法返回 false,则上部的执行链将被断开,
其后续的处理器方法与 postHandle()方法将无法执行。
但,无论执行链执行情况怎样,只要方法栈中有方法,即执行链中只要有 preHandle()方法返回 true,
就会执行方法栈中的afterCompletion()方法。最终都会给出响应
4.3.4 拦截器和过滤器的区别
4.3.5 拦截器举例 ———— 登录权限验证
只有经过登录的用户方可访问处理器,否则,将返回“无权访问”提示。
本例的登录,由一个 JSP 页面模拟完成。即在该页面里将用户信息放入 会话作用域对象session 中。
也就是说,只要访问过该页面,就说明登录了。没访问过,则为未登录用户。
项目【ch12-interceptor-permission】
实现步骤:
1. 新建maven版 web项目,加入依赖:spring-webmvc、jsp、Servlet依赖
2. 配置web.xml文件:注册中央调度器DispatcherServlet
3. 新建index.jsp发起请求
4. 创建MyController处理请求
5. 创建结果页面show.jsp
6. 创建login.jsp,模拟登录(把用户的信息放入到session会话作用域对象);
创建logout.jsp, 模拟退出系统(从session中删除数据)
7. 创建拦截器,从session中获取用户的登录数据,验证能否访问系统
8. 创建一个验证的tips.jsp:用户未登录
9. 创建springmvc配置文件
1) 组件扫描器 ,扫描 @Controller注解,创建对象
2) 声明视图解析器
3) 声明拦截器:指定拦截的请求uri地址、声明拦截器对象
未登录index.jsp ————> 拦截器:验证是否登录,否 ————> /tips.jsp:未登录,不能访问系统
登录login.jsp ————> index.jsp ————> 拦截器:验证是否登录,是 ————> 处理器 /some.do,转发 ————> show.jsp
退出logout.jsp ————> index.jsp ————> 拦截器:验证是否登录,否 ————> /tips.jsp:未登录,不能访问系统
主要代码:
------------------------------------------------------------------------
<body>
模拟用户Tom登录: 把用户的信息放入到session会话作用域对象中
<% session.setAttribute("name","Tom"); %>
</body>
------------------------------------------------------------------------
<body>
模拟用户Tom退出系统: 从会话作用域对象session中删除用户信息
<% session.removeAttribute("name"); %>
</body>
--------------------------------------------------------------------------------------------------------------------------
//拦截器类:拦截用户的请求。
public class MyInterceptor implements HandlerInterceptor {
//验证登录的用户信息, 正确就return true,其它就return false
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("拦截器的MyInterceptor的preHandle()方法执行了");
//从session中获取name的值
String loginName = (String)request.getSession().getAttribute("name");
//判断登录的账户,是否符合要求
if( !"Tom".equals(loginName)){
//用户姓名不是“Tom”,不能访问系统,给用户提示:
request.getRequestDispatcher("/tips.jsp").forward(request,response);
return false;
}
//说明用户确实登录了,且用户名为Tom,可以访问处理器
return true;
}
}
在之前 Spring框架中,是手动创建容器对象,然后才能获取对应的java对象的,在SpringMVC框架,并不需要我们做这些
ApplicationContext ctx = new classPathXmlApplication(“配置文件.xml”);
StudentService service = (StudentService)ctx.getBean (“service”);
SpringMVC内部请求的处理流程:也就是SpringMVC接收请求,到处理完成的过程
1.用户发起请求 some.do 到中央调度器DispatcherServlet
2.DispatcherServlet 接收请求some.do, 把请求转交给【处理器映射器 HandlerMapping】
处理器映射器: springmvc框架中的对象,框架把实现了 HandlerMapping 接口的类叫做映射器(多个)
作用: 根据请求,从springmvc容器对象中获取对应的处理器对象。相当于 MyController controller = ctx.getBean("some.do")
框架把找到的处理器对象放到【处理器执行链(HandlerExecutionChain)】类中保存。
HandlerExecutionChain 类中保存着:
1.处理器对象(MyController);
2. 项目中的所有的拦截器 List interceptorList;
3.DispatcherServlet 把2中的 HandlerExecutionchain中的处理器对象交给了对应的【处理器适配器 HandlerAdapter】
处理器适配器: springmvc框架中的对象,把实现了 HandlerAdapter接口的类叫做适配器(多个)
作用: 执行处理器方法( 调用MyController.doSome()得到返回值ModelAndView )
4.DispatcherServlet 把3中获取的 ModelAndView交给了【视图解析器 ViewResolver】
视图解析器: springmvc框架中的对象,需要实现 ViewResolver接口(可以有多个)
作用: 使用前缀、后缀,组成视图完整路径。并创建 view 视图对象。
View是一个接口,表示视图的,在框架中jsp、html不是用String表示,而是使用view和它的实现类来表示视图。
InternalResourceView: 一个视图类,表示jsp文件,视图解析器会创建 InternalResourceView类对象。
这个对象的里面,有一个属姓url=/WEB-INF/view/show.jsp
5.DispatcherServlet 把4步骤中创建的 view对象获取到,调用view类自己的方法,把Model数据放入到request作用域。
执行对象视图的forward请求转发。请求结束。
注:以上的过程都是在 中央调度器DispatcherServlet 的 doDispatch()方法中调用的
上一章:框架 - Spring框架