SpringMVC
第1章 SpringMVC概述
概述
springMVC:基于spring的框架,实际上就是spring的一个模块
这个模块专门做web开发
基于web开发的框架的底层都是基于servlet
springMVC可以理解成是servlet的一个升级,就像把mybatis理解成JDBC的升级
直接用servlet不方便,所以我们用框架来让web开发更加流畅简单
servlet是在controller层,是tomcat服务器的核心组件之一,用于接受请求,调用业务层处理请求,返回请求
springMVC框架是在servlet基础上加入一些功能,让我们做web开发更加方便。
springMVC实际上就是一个spring,spring是一个容器,通过ioc管理对象,使用<bean>标签或者注解的形式管理对象
springMVC能够创建对象,放入到容器中(springMVC的容器),springMVC容器中放的是控制器对象,用的是@Controller注解
在web开发中,spring容器applicationContext是通过监听器结合ServletContext来存储这个容器,使spring容器成为一个单例创建,并且可以通过servletContext全局作用域来获取到这个容器
我们要做的就是使用@Controller创建控制器对象,把对象放入到spring MVC容器中,把创建的对象作为控制器使用,这个控制器对象能够接收用户的请求,显示处理的结果,就当作一个servlet使用。
使用@Controller这个注解创建的就是一个普通类的对象---作为控制器对象,不是Servlet,但是springmvc赋予了控制器对象一些额外的功能,让它可以像servlet一样工作。加了这个@Controller注解,就能让这个类完成servlet的功能。
不是servlet的原因是servlet类都要继承于HttpServlet类。
web开发的底层都是servlet
springmvc中有一个对象是servlet:DispatcherServlet---中央调度器
DispatcherServlet:负责接收用户的所有请求,用户把请求给了DispatcherServlet,之后DispatcherServlet把请求转发给我们的Controller对象,最后是Controller对象处理请求。
Controller对象是由springMVC容器帮我们创建的,用的就是IOC技术
xxx.jsp-----DispatcherServlet(Servlet)----转发、分配给Controller对象(@Controller注解创建的对象(ioc中的注解注入))-----Controller类处理请求。
比之前的过程多了一个转发, DispatcherServlet不处理请求, 而是转发请求给Controller对象
搭建springmvc项目步骤
新建maven-web工程
加入依赖
spring-webmvc依赖----代表springmvc框架,会间接地把spring的依赖都加入到项目中,因为springmvc是基于spring的
servlet依赖,底层还是基于servlet
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.5.RELEASE</version> </dependency>
重点:要在web.xml中注册springmvc的核心对象DispatcherServlet
DispatcherServlet是中央调度器,是一个servlet,它的父类是集成HttpServlet
DispatcherServlet也叫做前端控制器(front controller)
DispatcherServlet负责接收用户提交的请求,调用其他的控制器对象,并把请求的处理结果显示给用户
接收用户请求的仍然是DispatcherServlet而不是Controller
只要是springmvc项目,必须要有DispatcherServlet对象!!
<?xml version="1.0" encoding="UTF-8"?> <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"> <!-- 声明:注册springmvc的核心对象DispatcherServlet DispatcherServlet有一个作用是创建springmvc容器对象的,容器对象创建好了,那么容器里的声明的对象也都会随着容器对象的创建而创建好 所以DispatcherServlet要随着tomcat服务器的启动而创建 DispatcherServlet的实例化对象,需要在tomcat服务器启动后创建 为什么要创建DispatcherServlet实例化对象呢? 因为DispatcherServlet在创建过程中,会同时创建springmvc容器对象,会读取springmvc的配置文件,把这个配置文件中的对象都创建好,当用户发起请求时,就可以直接使用对象了。 servlet初始化,会执行init方法。DispathcerServlet在init()中,会执行如下创建容器对象: WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml"); springmvc容器对象创建好之后,会和之前一样,放在ServletContext全局作用域中。 springmvc创建容器对象时,读取的配置文件默认是/WEB-INF/<servlet-name>-servlet.xml --> <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> <!-- 希望在tomcat启动后,创建DispatcherServlet对象 load-on-startup 表示tomcat启动后创建对象的顺序,它的值是整数,数值越小,tomcat创建对象的时间越早,大于等于0的整数 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>myweb</servlet-name> <!-- 使用框架的适合,url-pattern可以使用两种值 1. 使用扩展名的方式,语法*.xxxx,xxxx时自定义的扩展名,常用的方式:*.do,*.action,*.mvc等等 http://localhost:8080/myweb/some.do 2. 使用 “/” --> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
对象创建流程如下:
tomcat服务器启动
DispathcherServlet对象创建(这类似于监听器)
同时,DispathcherServlet对象创建过程的初始化过程中,springmvc容器对象创建并放在ServletContext中
同时,写在springmvc配置文件中的对象会创建。
总结:
tomcat服务器启动--DispathcherServlet对象创建---springmvc容器对象创建--springmvc容器中的对象会创建
创建一个发起请求的页面index.jsp
创建控制器类:
- 在类的上面加入@Controller注解,创建对象,并放入到springmvc容器中
- 在类中的方法上面加入@RequestMapping注解
/** * 创建控制器对象的,这个对象是放在springmvc容器中。 * 位置:在类的上面 * 和之前spring讲的@Service @Repository是一样的,是创建的对象的,即通过注解方式实现依赖注入,@Autowired @Resource 是给引用类型变量赋值 */ @Controller public class MyController { /* * 处理用户提交的请求,springmvc中是使用方法来处理的 * 方法是自定义的,可以有多种返回值,多种参数 */ /** * 准备使用doSome来处理some.do请求 * 我们需要@RequsetMapping,请求映射,作用是把一个请求地址和一个方法绑定在一起。位置是在方法的上面 * 属性:1.value:是一个string,是表示请求的url地址,value值是唯一的。在使用时,推荐地址以"/"开头("/"表示是跟地址,"/"映射到工程路径) * 说明: * 使用@RequestMapping修饰的方法叫做处理器方法或者控制器方法,一个请求地址和一个方法绑定在一起 * 使用@RequestMapping修饰的方法是可以处理请求的,类似于Servlet中的doGet、doPost方法 * (但是仍然要记住,DispatcherServlet才是接受请求,是中央调度器,Controller类是处理请求。) * * @return ModelAndView * Model:数据,请求处理完成后要显示给用户的数据 * View:视图,比如jsp等等 * ModelAndView:表示本次处理请求的结果,包含数据和视图两部分 */ @RequestMapping(value = "/some.do") public ModelAndView doSome() { //处理请求--相当于service层即业务层调用处理完成 ModelAndView modelAndView = new ModelAndView(); // 框架在请求的最后把数据(请求处理的结果)放入到request作用域,这个不需要我们做,我们只需要把请求处理的结果添加到modelAndView modelAndView.addObject("msg", "欢迎使用springmvc做web开发"); modelAndView.addObject("fun", "执行的是doSome()方法"); // 指定视图,指定视图的完整路径 // 框架对视图执行的forward操作,request.getRequestDispatcher("/show.jsp").forward(request, response) ,框架做的,不需要我们写 modelAndView.setViewName("/show.jsp"); return modelAndView; } }
创建一个作为结果的jsp,显示请求的处理结果
创建springmvc的配置文件(和spring配置文件一样,要用注解方式进行依赖注入,必须要有一个组件扫描器)
声明组件扫描器,指定@Controller注解所在的包名
声明视图解析器,帮助处理视图的。
<!-- 声明视图解析器,帮助开发人员设置视图文件的路径--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/view/"></property> <property name="suffix" value=".jsp"></property> </bean>
注意:
声明:注册springmvc的核心对象DispatcherServlet DispatcherServlet有一个作用是创建springmvc容器对象的,容器对象创建好了,那么容器里的声明的对象也都会随着容器对象的创建而创建好 所以DispatcherServlet要随着tomcat服务器的启动而创建 DispatcherServlet的实例化对象,需要在tomcat服务器启动后创建 为什么要创建DispatcherServlet实例化对象呢? 因为DispatcherServlet在创建过程中,会同时创建springmvc容器对象,会读取springmvc的配置文件,把这个配置文件中的对象都创建好, 当用户发起请求时,就可以直接使用对象了。 就像在spring中使用监听器来进行spring容器applicationContext的创建,因为要使spring容器只创建一次,并且在创建容器对象的过程中,会读取spring配置文件,配置文件读取完毕,这个配置文件里声明的所有对象也都创建好了,applicationContex存放在ServletContext中,能够拿到applicationContext,那么就能通过applicationContext拿到所有在spring配置文件里声明的对象。整个过程spring容器只创建一次,目的就是为了让spring容器对象是一个单例创建的模式。 而在整个地方,没有用到监听器,DispatcherServlet的作用就和监听器的作用差不多,因为,DispatcherServlet就是随着Tomcat服务器的启动而创建好,DispatcherServlet在创建过程中,会同时创建springmvc容器对象,会读取springmvc的配置文件,把这个配置文件中的对象都创建好。 整个思想和之前的监听器都是有联系的,差不多的。
控制器类能处理请求,但是控制器类只是相当于Servlet,但是控制器类不等于Servlet
DispatcherServlet也叫做前端控制器,自己写的控制器类也叫做后端控制器
springmvc请求的处理流程
用户发起请求。这个请求会发送给tomcat服务器
web.xml----url-pattern知道 *.do 的请求给DispatcherServlet
DispatcherServlet对象是随着Tomcat启动而创建好的
DispatcherServlet有一个作用就是创建springmvc容器对象,创建springmvc容器对象(容器对象创建好,那么配置文件就说明读取完毕,配置文件读取完毕,配置文件里声明的所有对象就创建好了。),就要读取springmvc配置文件,就能通过里面声明的组件扫描器,去对应的包下进行扫描。
根据springmvc.xml配置知道some.do---doSome()
DispatcherServlet把some.do请求转发给MyController的doSome()方法
框架执行doSome(),把得到ModelAndView进行处理,转发到show.jsp
DispatcherServlet的作用
负责创建springmvc容器对象,读取springmvc的xml配置文件,创建文件中的controller对象。
创建容器对象,会读取配置文件,配置文件读取完毕,配置文件中声明的所有对象就创建好了。(如果是采用基于xml文件进行依赖注入的方式的话),通过注解也是一样,因为springmvc配置文件里会声明组件扫描器
负责接收用户的请求,分派给自定义的controller对象
分析:
doDispatch方法的里面,最终会调用我们的MyController的方法
创建springmvc容器对象,仍然是只创建一次,是单例的,放在ServletContext(全局作用域)中,通过ServletContext拿到容器对象,那么就可以拿到配置文件中声明的类的对象。
ServletContext是随着Tomcat的启动而创建,随着Tomcat的结束而销毁
spring容器和springmvc容器都是放在ServletContext中
第2章 SpringMVC注解式开发
@RequestMapping
把所有地址中,公用的部分放到类上面的@RequestMapping注解定义
@RequestMapping注解的属性
若不指定请求方式,那么就没有限制。
接收用户提交的参数
概述
上面的三种代表请求、应答和会话,spring框架会自动给他们赋值。
接收请求参数,使用的是控制器方法的形参
- HttpServletRequest
- HttpServletResponse
- HttpSession
- 用户提交的数据
- 逐个参数接收
- 对象接收
逐个接收请求参数
要求:控制器方法的形参名和请求中参数名必须一致,同名的请求参数赋值给同名的形参
框架接收请求参数:
仍然是使用request对象接收请求参数,只不过是框架来做了,不用我们自己写了
springmvc框架通过中央调度器DispatcherServlet调用MyController的doSome()方法,调用方法时,按参数名称对应,把接收的参数赋值给形参
框架会提供类型转换的功能,能把String转为int、long、float、double等类型
post请求中文需要解决乱码问题,get请求方式中文没有乱码(提交表单也可以用get方法,只是不安全,请求参数会显示在地址栏上。)
需要使用过滤器解决post请求中文的乱码问题
过滤器可以自定义,也可以使用框架提供的过滤器,过滤器:CharacterEncodingFilter
<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> <!-- 强制请求对象HttpServletRequest使用encoding编码的值 --> <init-param> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param> <!-- 强制响应对象HttpServletResponse使用encoding编码的值 --> <init-param> <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>
若请求中参数名和控制器方法的形参名不一样
用一个对象来接收请求参数
用对象来接收请求参数,属性来保存参数值
要求属性名和请求中参数名一样!
控制器方法形参是Java对象,那么这个对象的属性名和请求中参数名是一样的,
框架会创建形参的Java对象,给属性赋值(ioc的set注入),请求中的参数是name,那么对象的属性名也是name,框架会调用setName
在ioc中,如果是采用注解的方式给属性赋值,就不需要属性的set方法,但不管是注解方式还是XML方式实现依赖注入,底层都是通过反射的机制,注解方式是不会用到set方法的,其中只有XML方式的set注入用到了属性的set方法
用对象来接收请求参数,就不需要用@RequestParam
这种方式,controller方法的参数就相当于是
RequestBody
控制器方法的返回值
返回ModelAndView
返回String
返回的String代表逻辑视图名称,需要配置视图解析器
控制器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址
如果只是需要跳转页面,不需要返回数据,那么返回值类型用String是最方便的
返回的字符串可以代表视图,可以是逻辑名称,也可以是完整的名称。
返回的String代表完整视图名称,不能配置视图解析器
返回void
void返回值既不能表示数据,也不能表示视图-----这种返回值用于服务器处理ajax请求。
虽然void返回值既不能表示数据,也不能表示视图,但是可以通过HttpServletResponse的输出来完成响应。
若控制器对请求处理后,无需跳转到其他任何资源(资源就是指的工程路径后面的就叫资源路径,代表的就是资源),此时可以让控制器方法返回void
例如,对于AJAX的异步请求的响应
注意:不管是ajax请求还是表单请求,逐个接收请求参数和用一个对象来接收请求参数是完全一样的语法。
返回值类型是void的时候,完全可以通过HttpServletResponse的输出来完成AJAX请求的异步响应
AJAX请求,服务器端返回的就是数据,和视图无关
ajax请求常用jQuery来发起请求
此PrintWriter对象输出的内容json,就会被传递到前端jsp页面的ajax请求的success
完成ajax请求响应的步骤
- controller调用service处理请求
- 将请求的返回结果转为json格式的字符串
- 将json格式的字符串通过response得到的PrintWriter输出
- 输出的json格式字符串被jQuery转换为json对象
返回对象Object----需加@ResponseBody
概述
返回对象代表的是数据,不是视图,而ajax请求正好要的就是数据,也跟视图无关,所以返回对象主要用来响应ajax请求,返回对象代表的是json数据,代表的是ajax请求的处理结果
返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。
返回对象,需要使用@ResponseBody注解,将转换后的JSON数据放入到响应体中。
@Controller + @ResponseBody = @RestController
返回的是对象,对象有属性,属性就是数据,所以返回Object表示数据(这里说的数据都是请求参数经过处理后的结果数据!!!结果数据才有返回的意义,返回的就是请求的处理结果。),和视图无关
可以使用对象表示的数据,响应ajax请求
实现步骤:
加入处理json的工具库的依赖,springmvc默认使用的jackson
在springmvc配置文件中加入
<mvc:annotation-driven>
注解驱动代替我们自己完成
json = om.writeValueAsString(student)
作用:把控制器方法返回的对象转为json格式的字符串
在控制器方法的上面加入@ResponseBody
作用:将json格式的字符串,通过HttpServletResponse输出给浏览器
位置:方法的上面,和其他的注解没有顺序的先后关系
@ResponseBody注解相当于以下几行代码的作用
在springmvc配置文件中加入
<mvc:annotation-driven>
注解驱动的原理springmvc控制器方法返回Object,可以转为json输出到浏览器,响应ajax的内部原理
<mvc:annotation-driven>
注解驱动实现的功能是完成Java对象到json或xml或text等数据格式的转换
HttpMessageConvert接口:消息转换器
<mvc:annotation-driven>
注解驱动加入到springmvc配置文件后,会自动创建HttpMessageConvert接口的7个实现类对象功能:定义了Java对象转为json、xml等数据格式的方法,这个接口有很多的实现类
这些实现类完成Java对象到json或Java对象到xml等数据格式的转换
canWrite 判断能否转换成指定格式,默认转换成json
write 将Java对象转换成指定格式
HttpMessageConvert接口的7个实现类
MappingJackson2HttpMessageConverter
使用jackson工具库中的ObjectMapper实现Java对象转换为json格式字符串,转换后的JSON格式的数据放到响应体中
StringHttpMessageConverter
负责读取字符串格式的数据和写出字符串格式的数据
控制器方法返回的是对象这种方式,框架的处理流程
框架会把返回Student类型,调用框架中的
ArrayList<HttpMessageConverter>
集合中的每个类的canWrite方法,来检查哪个HttpMessageConverter接口的实现类能处理Student类型的数据框架会调用第一步找出来的实现类的write(),MappingJackson2HttpMessageConverter的write()方法
把student对象转为json,调用Jackson的ObjectMapper实现转为json格式的字符串
框架会调用@ResponseBody把第二步的结果数据输出到浏览器,ajax请求处理完成
DispatcherServlet怎么分配请求给Controller的
- service方法
- doService方法
- doDispatch方法----会找到Controller类里的具体的方法,把请求交给那个方法执行,@RequestMapping让一个方法对应于一个地址
在项目中可能返回的不是一个Object,而是多个对象的集合
List<Object>
那么通过加入jackson依赖、添加注解驱动、添加@ResponseBody注解,也能得到json格式的字符串的数组!
返回字符串对象
此时代表的就不是视图,代表的是数据
怎么区分String返回的字符串代表的视图还是数据,看有没有注解@ResponseBody
如果有@ResponseBody注解,那么代表的是数据---字符串文本数据
如果没有@ResponseBody注解,代表的是视图
注意:dataType要进行修改。
控制器方法返回的是字符串对象这种方式,框架的处理流程
框架会把返回String类型,调用框架中的ArrayList<HttpMessageConverter>集合中的每个类的canWrite方法,来检查哪个HttpMessageConverter接口的实现类能处理String类型的数据
框架会调用第一步找出来的实现类的write(),StringHttpMessageConverter的write()方法
把String对象按照指定的编码来处理,即text/plain;charset=ISO-8859-1
框架会调用@ResponseBody把第二步的结果数据输出到浏览器,ajax请求处理完成
<url-pattern/>
解读tomcat本身能处理静态资源的访问,像html、图片、js文件都是静态资源
tomcat的web.xml文件中有一个servlet,名称是default,在服务器启动时创建的!!
这个地方的
/
表示静态资源和未映射的请求都给这个defaultServlet处理。tomcat的default servlet的作用
处理静态资源
处理未映射到其他servlet的请求
如果没有用springmvc来做web开发的话,在web.xml文件中会写很多servlet的类的声明和mapping(要么是通过web.xml来写,要么是通过注解,**这些servlet就和资源里比如jsp里的href对应上,或者表单的action对应上。不管是没使用springmvc时候的servlet类的地址还是使用springmvc的@RequestMapping的地址,都一定要和资源文件里的请求地址对应上!!!**意思就是点了超链接,或者提交了表单,就交给对应的servlet执行。servlet的作用就是接受请求、处理请求、返回请求),用了springmvc,只需要在web.xml里写中央调度器的声明和mapping,底层仍然是servlet。中央调度器DispatcherServlet会通过service、doService、doDispatch方法最终把对应的链接交给Controller的某个方法去执行!!
没有使用springmvc时,web.xml里的url-pattern(就是servlet类的地址,也可以不写在web.xml里,通过注解也可以)要和资源文件的请求地址对应上
使用springmvc时,@RequestMapping的地址要和资源文件的请求地址对应上
当我们的项目中的web.xml文件中的url-pattern用了
/
,那么它会替代tomcat的default servlet,对静态资源和未映射到servlet的请求进行处理导致所有的静态资源都给DispatcherServlet处理,默认情况下,DispatcherServlet没有处理静态资源的能力,没有控制器对象能处理静态资源的访问,所以静态资源都是404
那么既要在<url-pattern>用
/
,又想处理静态资源,有两种方式。使用
<mvc:default-servlet-handler/>
声明了
<mvc:default-servlet-handler/>
之后,springmvc框架会在容器中创建DefaultServletHttpRequestHandler处理器对象,它会像一个检察员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,一般的服务器都有默认的ServletTomcat中,专门用于处理静态资源访问的Servlet名叫DefaultServlet
<mvc:default-servlet-handler/>
这个配置和之前的@RequestMapping有冲突,需要加入注解驱动来解决冲突:<mvc:annotation-driven>
使用
<mvc:resources/>
**spring定义了专门用于处理静态资源访问请求的处理器ResourceHttpRequestHandler,**并且添加了<
mvc:resources/
>标签,专门用于解决静态资源无法访问的问题,需要在springmvc配置文件中添加如下形式的配置和第一种方式区别是,不依赖于tomcat服务器的默认的DefaultServlet来完成静态资源的处理,也就是不需要DefaultServletHttpRequestHandler来筛查url即把静态资源仍然分配给tomcat的DefaultServlet来进行处理,这种方式就由我们自己的处理器来进行处理
<mvc:resources/>
这个配置和之前的@RequestMapping有冲突,需要加入注解驱动来解决冲突:<mvc:annotation-driven>
<mvc:annotation-driven>
作用1. 将返回对象转换为json、xml、txt等格式,用于响应ajax请求。 2.解决静态资源访问,静态资源访问会添加另外的配置,而另外的配置会和@RequestMapping冲突,所以必须加上这个注解。(冲突是动态请求也会交给处理静态资源的servlet处理,加上之后,保证静态资源给对应的处理器处理,比如DefaultServlet或者spring定义的ResourceHttpRequestHandler,保证动态资源扔给之前的DispatcherServlet处理)
使用一个配置语句,指定多种静态资源的访问:即静态资源统一放在一个目录下即static。
访问地址
html文件或jsp文件中的访问地址或请求地址
- 前面不加斜杠,在浏览器地址栏中映射到
ip地址:端口号/项目名/
- 前面加斜杠,在浏览器地址栏中映射到
ip地址:端口号/
- 前面不加斜杠,在浏览器地址栏中映射到
所以最好保证工程路径永远是一致的,即使用以下两种方式
开头不加斜杠,配合base标签
开头加斜杠,但是在前面加EL表达式
两种方式在开头都没有单纯地加斜杠,但是却保证了访问的参考地址!!
第3章 SSM整合开发
概述
SpringMVC: 视图层、界面层,负责接受请求,显示处理结果的。
spring:业务层,管理service、dao、工具类对象的
mybatis:持久层,访问数据库的
用户发起请求--springmvc接收请求----请求给spring的service实现类对象进行处理-----service对象调用dao,通过mybatis访问数据库,处理数据
整合中有容器
- 第一个容器SpringMVC容器,管理Controller控制器对象
- 第二个容器是spring容器,管理service、dao、工具类对象的
我们要做的是把使用的对象交给合适的容器创建、管理,把Controller还有web开发的相关对象交给springmvc容器,这些web用的对象写在springmvc配置文件中
把service、dao对象定义在spring的配置文件中,让spring管理这些对象
这两个容器对象的职责不同,两个容器是独立的
那么两个容器中的对象怎么互相访问到呢?
比如Controller对象怎么访问service对象呢?一个容器中的对象怎么访问到另一个容器中的对象呢?
如果两个容器没有任何的关联肯定不行,所以这两个容器存在关系
springmvc容器和spring容器是有关系的,他们的关系已经确定好了,不需要人为去做
springmvc容器是spring容器的子容器,类似Java中的继承
子可以访问父的内容。
在子容器中的Controller可以访问父容器中的Service对象,就可以实现controller使用service对象。
这个整合开发,已经把这个关系确定好了,我们只需要确定对应的对象创建要放在对应的容器中。
整合开发步骤
新建maven项目
加入依赖
springmvc、spring、mybatis三个框架的依赖,jackson依赖,mysql驱动,druid连接池、jsp、servlet依赖
写web.xml
注册DispatcherServlet中央调度器,目的是
- 创建springmvc容器对象,于是就创建好了springmvc容器中的所有对象
- 用于接收用户请求,把请求分配给Controller去执行
注册spring的监听器:ContextLoaderListener,目的:
创建spring的容器对象,通过监听器结合ServletContext这个作用域,能够实现spring容器对象的单例创建
监听器的作用:因为ServletContext是随着tomcat的启动而创建,随着tomcat的结束而销毁,监听器可以监听到全局作用域ServletContext的创建,监听到之后,那么就创建spring容器对象,并把spring容器对象放进ServletContext全局作用域
注册字符集过滤器:解决post请求乱码的问题
创建包,Controller包,service、dao、实体类包名创建好
写springmvc、spring、mybatis的配置文件
- springmvc配置文件
- spring配置文件
- mybatis主配置文件
- 数据库的属性配置文件
写代码,dao接口(不用写dao实现类),service接口和service实现类(类是要写的,对象是交给容器创建的。)controller,实体类
写jsp页面
第4章 SpringMVC核心技术
请求重定向和转发
在javaweb中请求转发和请求重定向的区别
- 请求转发只有一次请求,请求重定向是两次请求
- 请求重定向的地址栏的url会发生变化,而请求转发不会
- 请求重定向不会共享Request域的数据,而请求转发是共享request域的数据,因为tomcat,会把每一次请求都封装到request域中,请求转发能够共享,正是因为只有一次请求,而请求重定向不可以共享,是因为是两次请求
- 请求重定向不能访问WEB-INF目录下的资源,而请求转发可以!
- 请求重定向可以访问外部资源,而请求转发不可以
springmvc框架把原来servlet中的请求转发和重定向操作进行了封装,现在可以使用简单的方式实现转发和重定向
forward:表示转发,实现request.getRequestDispatcher("xx.jsp").forward(request,response);
redirect:表示重定向,实现response.sendRedirect("xxx.jsp")
他们有一个共同的特点:就是不和视图解析器一起工作,就当项目中没有视图解析器一样
forward:
setViewName()方法的形参这样写,这种方式叫显式转发
redirect
框架会把Model中的简单类型的数据,转为String使用,作为hello.jsp的get请求参数使用
目的是在doRedirect.do和hello.jsp两次请求之间传递数据
在目标hello.jsp页面可以使用参数集合对象${param}获取请求参数值
请求重定向这种方式,直接通过EL表达式${参数名}这种方式取不到,因为是两次请求,是不同的request域。但是在重定向后的URL地址上有请求的参数,那么可以通过以下方式取
异常处理
概述
springmvc框架处理异常的常用方式---统一全局异常处理---aop思想
这样的话可以不写try-catch代码,让try-catch代码和我们的业务分离,解耦合,让方法里面只保留业务逻辑代码,让异常放到一个统一的地方处理,用的思想就是AOP,业务功能和非业务功能的解耦合!!
叫做统一全局异常处理方案
把controller的所有异常处理都集中到一个地方
使用两个注解
- @ExceptionHandler
- @ControllerAdvice
异常处理步骤
新建maven web项目
加入依赖
新建一个自定义异常类,MyUserException,再定义它的子类NameException,AgeException
在controller抛出NameException,AgeException,抛出之后,就可以处理异常了
创建一个普通类,作为全局异常处理类
- 在类的上面加入@ControllerAdvice
- 在类中定义方法,方法上面加入@ExceptionHandler
创建处理异常的视图页面
创建springmvc的配置文件
- 组件扫描器,扫描@Controller注解
- 组件扫描器,扫描@ControllerAdvice注解所在的包
- 声明注解驱动!!---两个作用
- 返回对象Object的时候,将对象进行格式的转换,添加了这个注解驱动就会创建一个接口,接口有7个实现类,
<mvc:annotation-driven>
注解驱动加入到springmvc配置文件后,会自动创建HttpMessageConverter接口的7个实现类对象,并且会将Java对象转换为json、xml、txt等格式的字符串 - 解决静态资源访问问题的两种方式会和@RequestMapping产生冲突,需要这个注解驱动来解决
- 返回对象Object的时候,将对象进行格式的转换,添加了这个注解驱动就会创建一个接口,接口有7个实现类,
如图:
方法体里,对异常的处理逻辑:
需要把异常记录下来,记录到数据库、日志文件
记录异常发生的事件,哪个方法发生的,异常错误内容。
发送通知,把异常的信息通过邮件、短信或微信发送给相关人员
给用户友好的提示
拦截器
概述
拦截器必须实现接口HandlerInterceptor
过滤器是过滤请求参数的,拦截器是拦截请求的,对请求做预先的判断处理工作
拦截器是springmvc中的一种对象,需要实现HandlerInterceptor接口
拦截器和过滤器类似,功能方向侧重点不同,
- 过滤器是用来过滤请求参数,设置编码字符集等工作
- 拦截器是拦截用户的请求,做请求的判断处理的
拦截器的特点:
是全局的,可以对多个Controller做拦截
一个项目中可以有0个或多个拦截器,他们在一起拦截用户的请求
拦截器常用在用户的登陆处理,权限的检查,记录日志方面
我们需要定义类实现HandlerInterceptor接口
需要在springmvc配置文件中声明拦截器,让springmvc框架知道拦截器的存在
拦截器的执行时间:
- 在请求处理之前,也就是Controller类中的方法执行之前先被拦截(首次执行时间)
- 在控制器方法执行之后,也会执行拦截器
- 在请求处理完成后也会执行拦截器
拦截器的使用步骤
新建maven web项目
加入依赖
创建Controller类
创建一个普通类,作为拦截器使用
- 实现HandlerInterceptor接口
- 实现接口中的三个方法
创建show.jsp结果页面
创建springmvc配置文件
组件扫描器,扫描@Controller注解
声明拦截器,并指定拦截的请求uri地址
HandlerInterceptor接口的三个方法
preHandle(HttpServletRequest request,HttpServletResponse response,Object handler)
预处理方法
这个方法很重要,是整个项目的入口、门户
参数:
HttpServletRequest request,HttpServletResponse response,Object handler
- Object handler: 被拦截的控制器对象,也就是Controller类的对象
返回值:boolean
- true:表示请求通过了拦截器的验证,可以执行控制器类的方法
- false:请求没有通过拦截器的验证,请求到达拦截器就截止了,请求没有被处理
特点:
方法在控制器类的方法执行之前先执行的
用户的请求首先到达此方法
在这个方法中可以获取请求的信息,验证请求是否符合要求
可以验证用户是否登录,验证用户是否有权限去访问某个链接地址(url)
如果验证失败,我们可以截断请求,这个请求不能被处理也就是不能到达控制器类的方法
如果验证成功,可以放行请求,此时控制器方法才能执行
示例:
postHandle(HttpServletRequest request,HttpServletResponse response,Object handler, ModelAndView modelAndView)
后处理方法
参数:
- Object handler: 被拦截的控制器对象,也就是Controller类的对象
- ModelAndView modelAndView:是控制器类的方法的返回值
特点:
- 在处理器类的方法之后执行的。
- 能够获取到处理器方法的返回值ModelAndView,可以修改modelAndView中的数据和视图,可以影响到最后的执行结果
- 主要对原来的执行结果做二次修正。
afterComletion(HttpServletRequest request,HttpServletResponse response,Object handler, Exception ex)
最后执行的方法,视图处理完成后,即请求处理完成后,给出的响应
参数:
- Object handler: 被拦截的控制器对象,也就是Controller类的对象
- Exception ex:程序中发生的异常
特点:
- 在请求处理完成后执行的,框架中规定是当视图处理完成后,对视图执行了forward,就认为请求处理完成
- 一般做资源回收工作的。比如程序请求过程中,创建了一些对象,在这里可以删除,把占用的内存回收
拦截器看作是多个Controller中共用的功能(比如用户是否登录),集中到拦截器统一处理,仍然是aop的思想!!
一个拦截器的三个方法的执行顺序
多个拦截器的三个方法执行顺序
在框架中保存多个拦截器是ArrayList
按照声明的先后顺序放入到ArrayList,先声明的先执行,后声明的后执行。
两个拦截器,
第一个拦截器的preHandle方法返回true,第二个拦截器的preHandle也返回true,执行顺序如下
第一个拦截器preHandle=true,第二个拦截器preHandle=false
第一个拦截器preHandle=false,第二个拦截器preHandle=true或false对结果没有任何影响
不管有几个拦截器,只要其中一个拦截器的preHandle方法返回了false,控制器方法一定不会执行!
我们可以有多个拦截器,每个拦截器负责不同的功能,比如第一个拦截器验证登录,第二个验证功能的用户,第三个拦截器记录用户的访问日志。
拦截器和过滤器的区别
过滤器是servlet中的对象(是servlet的三大组件之一),拦截器是框架(springmvc)中的对象
过滤器是实现Filter接口的,拦截器是实现HandlerInterceptor接口
过滤器是用来设置request,response的参数,属性的,侧重对数据过滤的(过滤器可以解决post请求中文乱码的问题)
拦截器是用来验证请求的,能截断请求
过滤器是在拦截器之前先执行的
过滤器是tomcat服务器创建的对象
拦截器是springmvc容器中创建的对象
过滤器是一个执行时间点
拦截器有三个执行时间点
过滤器可以处理jsp、js、html等等
拦截器是侧重拦截对Controller的对象,如果请求不能被DispatcherServlet接收,这个请求不会执行拦截器内容
springmvc内部请求的处理流程
springmvc内部请求的处理流程也就是springmvc接收请求,到处理完成的过程
步骤
用户发起请求some.do
DispatcherServlet接收请求some.do,把请求转发给处理器映射器
处理器映射器:springmvc框架中的一种对象,框架把实现了HandlerMapping接口的类叫做处理器映射器(多个),处理器映射器就是实现了HandlerMapping接口的类,每一个类都有它对应的能够处理的请求
处理器映射器作用:根据请求,从springmvc容器对象中获取处理器对象
类似于spring框架中,这个功能:
ApplicationContext ctx = new ClassPathXmlApplication("bean.xml"); StudentService studentService = (StudentService) ctx.getBean("service");
框架把找到的处理器对象放到一个叫做处理器执行链的类保存
处理器执行链:HandlerExecutionChain
HandlerExecutionChain:类中保存着
- 处理器对象
- 项目中所有的拦截器(用的是List集合)
DispatcherServlet把HandlerExecutionChain中的处理器对象,交给处理器适配器对象(多个)
处理器适配器:springmvc框架中的对象,需要实现HandlerAdapter接口
处理器适配器作用:执行处理器方法即控制器类中的方法,得到返回值
DispatcherServlet把上一步获取的ModelAndView(返回值)交给了视图解析器对象
视图解析器:springmvc中的对象,需要实现ViewResolver接口(可以有多个)
视图解析器作用:组成视图完整路径,使用前缀、后缀,并创建View对象
比如上图这个"show"在框架中就会被转成View对象。
View是一个接口,表示视图的,在框架中jsp、html不是string表示,而是使用View和它的实现类表示视图
InternalResourceView:视图类,表示jsp文件,视图解析器会创建InternalResourceView对象
这个对象里面,有一个属性url=xxx
DispatcherServlet把上一步创建的View接口的实现类对象获取到,调用View类自己的方法,把Model数据放入到Request作用域,执行对象视图的forward,请求结束
总结:
- 处理器映射器--得到Controller对象
- 处理器执行链--得到Controller对象放入其中
- 处理器适配器--执行Controller对象的方法--service()、doService()、doDispatch()、处理器方法
- 视图解析器--DispatcherServlet将返回的ModelAndView对象交给视图解析器,创建view对象
- view对象--DispatcherServlet调用View类自己的方法,把Model数据放入到Request作用域,执行对象视图的forward,请求结束
处理器映射器:获取处理器对象
处理器适配器:执行控制器类的方法