SpringMVC学习笔记
SpringMVC 也叫 Spring web mvc。是 Spring 框架的一部分,是在 Spring3.0 后发布的。
一、第一个注解的SpringMVC程序
所谓 SpringMVC 的注解式开发是指, 在代码中通过对类与方法的注解,便可完成处理器在 SpringMVC 容器的注册。 注解式开发是重点
1. 实现步骤
-
需求: 用户在页面发起一个请求, 请求交给SpringMVC中的控制器对象处理。结果显示到一个jsp中。
-
实现步骤:
- 新建maven web项目
- 加入依赖
- spring-mvc依赖
- servlet依赖
- jsp依赖
- 修改web.xml文件
注册springmvc框架中的核心对象
DispatcherServlet
(中央调度器) - 创建jsp,发起请求
- 创建一个类,作为控制器使用 1)它不是servlet, 起到servlet的作用 2)需要在类的上面加入@Controller,表示创建控制器对象 3)在类中自定义方法, 方法上面加入@RequestMapping(value="请求的uri地址")
- 创建作为结果的jsp页面
- 创建springmvc框架需要的配置文件(spring的配置文件) 1)声明组件扫描器,指定Controller注解所在的包名 2)声明视图解析器,处理视图
- 修改控制器对象, 使用逻辑视图名称
2. 加入依赖
<!--servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--spring-webmvc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
3. web.xml中注册DispatcherServlet(掌握)
DispatcherServlet它是一个Servlet, 父类继承了HttpServlet,他是一个单例多线程的
- DispatcherServlet的作用:
- 是接收请求,显示处理结果给用户。
- 把接收到的请求分派给处理器(控制器)对象。
- DispatcherServlet也叫做前端控制器(front controller)
- DispatcherServlet的配置:
- 通过
<load-on-startup>
指定在tomcat服务器启动时,创建对象<load-on-startup>
的值是大于等于0整数,数值越小,tomcat创建这个对象的时间越早 - DispatcherServlet是一个servlet,在创建servlet对象时,会执行
init()
。在init()方法中, 会创建spring的容器对象WebApplicationContext
,在创建容器对象时,会读取spring需要的配置文件。 而这个配置文件的默认路径是WEB-INF,即默认的名称是<servlet-name>-servlet.xml
- 通过
contextConfigLocation
参数来指定配置文件的位置 - 通过<url-pattern>是把请求都交给中央调度器
常用的方式:
1. 扩展名: 常用的
*.do
,*.mvc
,*.action
等等, 不要使用*.jsp
2. 斜杠:"/"
- 通过
<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:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myweb</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
3. 创建Controller(控制器)
它不是servlet, 但起到了servlet的作用。 控制器作用:接收请求,处理业务逻辑。
@Controller
public class MyController {
@RequestMapping(value = {"/some.do","/first.do"})
public ModelAndView doSome(){
//使用doSome方法处理用户的某个请求
System.out.println("使用doSome方法处理some.do的请求");
//调用service处理请求
//创建返回值,表示请求的处理结果
ModelAndView mv = new ModelAndView();
//添加数据到Model部分, 框架对Model中的数据会在后边执行
//request.setAttribute(),数据是放入到request作用域
mv.addObject("msg", "第一个springmvc项目");
mv.addObject("fun", "doSome");
//指定结果页面的视图,框架对视图执行forward操作
//setViewName("视图的完整路径")
//WEB-INF是一个受保护的文件夹,一般将不让直接访问的文件放到WEB-INF/view/文件夹下
//mv.setViewName("/WEB-INF/view/show.jsp");
//当配置了视图解析器后,可以使用逻辑视图名称(文件名),指定视图
mv.setViewName("other");
//返回结果
return mv;
}
}
4. 创建springmvc框架需要的配置文件(掌握)
声明视图解析器:InternalResourceViewResolver
<!--声明组件扫描器-->
<context:component-scan base-package="com.bjpowernode.controller" />
<!--声明视图解析器:创建视图View对象, 指定视图的路径,和扩展名-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀:指定视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/" />
<!--后缀:指定视图文件的扩展名-->
<property name="suffix" value=".jsp" />
</bean>
二、SpringMVC执行流程
- 浏览器提交请求到中央调度器
- 中央调度器直接将请求转给处理器映射器。
- 处理器映射器会根据请求,找到处理该请求的处理器,并将其封装为处理器执行链后返回给中央调度器。
- 中央调度器根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器。
- 处理器适配器调用执行处理器。
- 处理器将处理结果及要跳转的视图封装到一个对象 ModelAndView 中,并将其返回给处理器适配器。
- 处理器适配器直接将结果返回给中央调度器。
- 中央调度器调用视图解析器,将 ModelAndView中的视图名称封装为视图对象。
- 视图解析器将封装了的视图对象返回给中央调度器
- 中央调度器调用视图对象,让其自己进行渲染,即进行数据填充,成响应对象。
- 中央调度器响应浏览器。
三、SpringMVC注解式开发(掌握)
1. @Controller
需要在类的上面加入@Controller,表示创建控制器对象。默认是单例对象
- 属性:
- value:指定对象的名称,一般不需要指定名称
- 位置:在类定义的上面
2. @RequestMapping
(1) @RequestMapping修饰方法
- 方法的要求:
- public 方法
- 需要有返回值, 返回值表示请求的处理结果
- 方法可以有参数,也可以没有。
- 方法名称是自定义的
- 属性:
- method: 表示请求的方式, 用的是RequestMethod类的枚举值。不指定时不限制提交方式
- RequestMethod.GET:表示提交方式必须为GET
- RequestMethod.POST:表示提交方式必须为 POST 提交
- value:定义所匹配请求的 URI, 地址是唯一值,可以指定多个,使用
{"地址1","地址2",}
- 推荐使用“/”开头,表示绝对地址
- method: 表示请求的方式, 用的是RequestMethod类的枚举值。不指定时不限制提交方式
(2) @RequestMapping修饰类
@RequestMapping在一个类中修饰多个方法时,若这些请求具有相同的 URI 部分,可以将相同部分的URI抽取到注解在类之上的@RequestMapping 的 value 属性中。
- 属性:value:表示请求地址的公共部分, 这个公共部分的值可以看做是模块名称
3. 处理器方法的参数
处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。
- HttpServletRequest
- HttpServletResponse
- HttpSession
- 请求中所携带的请求参数
@RequestMapping(value = "/first.do")
public ModelAndView doFirst(HttpServletRequest request,
HttpServletResponse response,
HttpSession session){}
(1) 逐个接收请求参数
- 控制器方法的形参名和请求中参数名一样。请求中同名参数赋值给同名的形参
- 框架提供了参数的转换功能,将String类型的请求参数转换为对应数据类型,可以使用String,int , float, double ,long等的类型的转换
- 当方法形参与请求中的参数名不一致时,可以在形参中使用注解
@RequestParam
- 作用:
- 解决方法的形参名和请求中参数名不一样的问题
- 属性:
- value:请求中参数名
- required:是boolean值, 默认是true
- true:请求中必须有这个参数
- false:可以没有参数
- 位置:在形参定义的前面
- 作用:
@RequestMapping(value = "/receive-param-requestparam.do")
public ModelAndView doParam(@RequestParam(value = "rname",required = false) String name,
@RequestParam(value = "rage",required = false) Integer age){}
@RequestMapping(value = "/receive-param-property.do")
public ModelAndView doSome(String name,Integer age){}
(2) 对象参数接收
接收多个参数,3个以上, 可以使用java对象
- java对象的属性名和请求中参数名一样。
- 对象中必须要有对应属性的set()方法(对象是通过无参构造创建出来的,通过对应属性的set()方法来给属性赋值)
- 也有自动类型转换的能力
@RequestMapping(value = "/other2.do")
public ModelAndView doOther2( Student student){}
注意: 以下代码中student对象中的name属性和形参中的name参数都会被赋值
@RequestMapping(value = "/other2.do")
public ModelAndView doOther2(Student student,String name){
(3) 添加过滤器解决请求参数中文乱码问题
通过使用CharacterEncodingFilter
过滤器,指定请求对象使用encoding
的值对应的字符集
<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>
<!--强制请求(request)对象使用encoding的值-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--强制应答对象(response)使用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>
4. 处理器方法的返回值
使用@Controller 注解的处理器的处理器方法,其返回值常用的有四种类型
(1) ModelAndView
若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据, 此时处理器方法返回 ModelAndView 比较好。当然,若要返回 ModelAndView,则处理器方法中需要定义ModelAndView 对象。
(2) String
处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物Java 框架里视图地址,一般用于只有页面的跳转,不返回数据的情况 String对视图执行的是重定向(forward)
(3) 无返回值 void
若处理器对请求处理后,只需返回数据,无需跳转到其它任何资源, 此时可以让处理器方法返回 void。例如,对于 AJAX 的异步请求的响应。
(4) 返回自定义类型对象
返回的对象可以是几何对象,它不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。
返回对象,需要使用@ResponseBody
注解, 将转换后的 JSON
数据放入到响应体中,具体步骤:
加入Jackson依赖
<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>
声明注解驱动
SpringMVC 使用消息转换器实现请求数据和对象,处理器方法返回对象和响应输出之间的自动转换。消息转换器就是 HttpMessageConverter
接口。
框架默认在项目中有消息转换器的四个实现类,一个实现类处理一种数据类型。其中包含StringHttpMessageConverter类,能处理String类型的返回值的,但是不能处理java对象到json的转换。默认实现的四个类:
-
org.springframework.http.converter.ByteArrayHttpMessageConverter
-
org.springframework.http.converter.StringHttpMessageConverter
-
org.springframework.http.converter.xml.SourceHttpMessageConverter
-
org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter
如果需要处理json的格式,在springmvc配置文件加入注解驱动的标签
<mvc:annotation-driven/>
,框架会创建7个实现类对象,其中有org.springframework.http.converter.json.MappingJackson2HttpMessageConverter,可以处理java对象到json的转换。
<mvc:annotation-driven/>
在处理器方法的上面加入@ResponseBody
使用HttpServletResponse对象把转换后的数据输出到浏览器。放在处理器方法的上面使用。
@RequestMapping(value = "/return-student-json.do")
@ResponseBody
public Student doStudentJson(String name, Integer age){}
返回String文本数据(乱码解决)
如果处理器方法上面有@ResponseBody
注解,返回String就是数据。其他情况都是视图
框架会调用StringHttpMessageConverter类的write()方法,把String转为文本数据,默认使用text/plain;charset=ISO-8859-1
,因此,会存在乱码。
解决方法:
@RequestMapping的produces属性:指定Content-Type的值
@RequestMapping(value = "/return-string-data.do",produces = "text/plain;charset=utf-8")
@ResponseBody
public String doStringData(){
return "HelloSpringMVC 开发web应用";
}
5. 中央调度器*.do
与/
的区别
(1) *.do等
SpringMVC 的中央调度器会将匹配的请求进行处理分发,若请求不匹配,则交由Tomcat中默认的Servelt,例如,静态资源。
(2) /
DispatcherServlet 会将向静态资源的获取请求,例如.css、 .js、 .jpg、 .png 等资源的获取请求,当作是一个普通的 Controller 请求。 中央调度器会调用处理器映射器为其查找相应的处理器。当然也是找不到的,所以在这种情况下,所有的静态资源获取请求也均会报 404 错误
(3) / 无法访问静态资源的解决办法
- 使用
<mvc:default-servlet-handler/>
- 声 明 了
<mvc:default-servlet-handler />
后 , springmvc 框 架 会 在 容 器 中 创 建DefaultServletHttpRequestHandler
处理器对象。 这个对象会把静态资源的请求转交给tomcat的default这个Servlet default-servlet-handler
和@RequestMapping有冲突,导致动态资源访问是404,所以要再加入注解驱动<mvc:annotation-driven />
- 声 明 了
<mvc:default-servlet-handler />
<mvc:annotation-driven />
- 使用
使用<mvc:resources/>
(掌握)
- mvc:resources,由框架创建ResoucesHttpRequestHandler处理器对象,使用这个对象处理静态资源的访问, 由框架自己内部处理静态资源,不依赖服务器tomcat的。
- 属性:
- mapping:访问静态资源的uri地址,可以使用通配符
**
**
表示任意的目录,子目录和资源的 - location:静态资源在项目中的位置(目录),最好不要使用/WEB-INF目录
- mapping:访问静态资源的uri地址,可以使用通配符
- 属性:
- 同样需要加入注解驱动
<mvc:annotation-driven />
<mvc:resources mapping="/html/**" location="/html/" />
<mvc:resources mapping="/img/**" location="/img/" />
<!--注解驱动-->
<mvc:annotation-driven />
四、SpringMVC核心技术
1. 请求转发与重定向
(1) 请求转发
处理器方法返回ModelAndView,实现转发操作
- 语法格式:mv.setViewName("forward:视图的完整路径")
- 特点:不和视图解析器一同工作,就当项目中没有视图解析器
mv.setViewName("forward:/hello.jsp");
mv.setViewName("forward:/WEB-INF/view/show.jsp");
(2) 重定向
处理器方法返回ModelAndView,实现重定向
- 语法:mv.setViewName(redirect:视图完整路径)
- 特点:不和视图解析器一同工作,就当项目中没有视图解析器
- 总结: 1. 不能重定向到/WEB-INF受保护的资源 2. 框架提供了两次请求之间的数据传递功能, 框架把Model中简单类型的数据转为String,作为get请求参数附加到目标页面的后面, 对象类型的值不能转为string,不能作为参数传递 3. 在目标页面中,可以获取get请求的参数,使用语法${param.参数名}
mv.setViewName("redirect:/hello.jsp");
2. 异常处理
springmvc框架中异常处理是集中的方式, 把异常集中在异常处理器中。
(1) @ExceptionHandler
使用注解 @ExceptionHandler
可以将一个方法指定为异常处理方法。
- 属性:value,Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。不指定参数执行其他异常(相当于if条件判断中的else)
- 被注解的方法:
- 方法参数:
value
用来指定异常名称,可以是自定义异常
- 方法参数:
(2) @ControllerAdvice
字面理解就是“控制器增强”, 是给控制器对象增强功能的。使用 @ControllerAdvice
修饰的类为全局异常处理对象,集中处理所有的异常
当使用 @RequestMapping
注解修饰的方法抛出异常时,会执行 @ControllerAdvice
修饰的
类中的异常处理方法。异常处理方法的返回值就是看到的最后的结果。
@ControllerAdvice
是使用 @Component
注解修饰的,可以使用<context:component-scan>
扫描到 @ControllerAdvice
所在的类路径(包名), 创建对象。
@ExceptionHandler(value = NameException.class)
public ModelAndView doNameException(Exception ex){
//处理NameExceptoin异常
//处理异常1记录异常,异常的发生时间,原因等。 记录到日志文件
// 2发送通知, 邮件,短信等
// 3给用户友好的提示,说明
}
//处理其他异常,并返回json字符串
@ExceptionHandler
@ResponseBody
public ModelAndView doOtherException(Exception ex){}
3. 一个拦截器
拦截器:是springmvc中的一种对象,需要实现HandlerInterceptor
接口。
-
拦截器作用:拦截请求, 验证请求是否正确。 如果请求不正确可以截断请求。通常使用拦截器可以验证用户是否登录, 验证用户的权限(用户可以执行的操作),打印日志。
-
拦截器的分类:
- 系统拦截器,springmvc内置的拦截器
- 自定义拦截器(用户主要使用的)
-
拦截器的特点:
- 在一个项目中可以有0或多个拦截器
- 拦截器是全局的,可以对所有的Controller请求都拦截
- 拦截器的执行时间:在适配器对象获取之后, 在处理器方法执行之前,拦截用户的请求
- 拦截器有三个执行时间:
- 在处理器方法执行之前
- 在处理器方法执行之后
- 在请求处理完成后。
- 拦截器有三个执行时间:
-
拦截器的使用:
- 定义类实现HandlerInterceptor接口
- 在springmvc配置文件中声明拦截器对象,指定拦截的uri地址
(1) 自定义一个拦截器对象
实现HandlerInterceptor
接口的类叫做拦截器,接口中有三个方法:
-
boolean
preHandle
(HttpServletRequest request, HttpServletResponse response,Object handler
) throws Exception;- preHandle:预处理方法,用户的请求首先到达此方法,在处理器方法执行之前先执行的。
- 参数:
- Object handler:处理器对象,被拦截器的处理器对象
- 返回值 boolean
- true:请求可以通过拦截器, 请求能被处理
- false:请求没有验证通过,被截断,请求不能被处理。
- 作用:验证请求,比如登录检查, 权限验证,输出日志
-
void postHandle
(HttpServletRequest request, HttpServletResponse response,Object handler, ModelAndView modelAndView
) throws Exception;- postHandle:后处理方法,在处理器方法之后执行的。
- 参数:
- Object handler:被拦截的处理器对象
- ModelAndView:表示处理器方法的返回值
- 作用:能够获取到处理器方法的返回值,可以修改返回值的数据和视图,可以对请求的结果做二次处理。
-
void afterCompletion
(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex
) throws Exception;- afterCompletion:最后执行的方法,在视图处理完成后执行的。视图处理完成,请求也就处理完成了
- 参数:
- Object handler:被拦截的处理器对象
- Exception ex:异常对象
- 作用:在请求处理完成后执行此方法, 可以用来清除请求处理过程中的临时数据。把内存占用的资源清除掉, 或者打印日志。 注意:只有在preHandle返回true时,afterCompletion方法才可能有机会执行。
(2) 声明拦截器对象
使用<mvc:interceptors>
标签来声明拦截器对象(springmvc配置文件中),拦截器声明顺序为拦截器的顺序
<!--声明拦截器对象-->
<mvc:interceptors>
<!--声明第一个拦截器-->
<mvc:interceptor>
<!--mapping:指定拦截的请求uri
path:请求的uri地址,可以使用通配符 ** , **表示任意资源
-->
<mvc:mapping path="/**"/>
<!--拦截器对象-->
<bean class="com.bjpowernode.interceptor.MyInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
(3) 一个拦截器的执行方式
4. 两个拦截器
两个拦截器对象
(1) 第一个拦截器preHandle=true , 第二个拦截器preHandle=true
执行了拦截器MyInterceptor-1111中的preHandle 执行了拦截器MyInterceptor-22222中的preHandle 使用doSome方法处理some.do的请求za|22 执行了拦截器MyInterceptor-22222中的postHandle 执行了拦截器MyInterceptor-1111中的postHandle 执行了拦截器MyInterceptor-22222中的afterCompletion 执行了拦截器MyInterceptor-1111中的afterCompletion
(2) 第一个拦截器preHandle=true , 第二个拦截器preHandle=false
执行了拦截器MyInterceptor-1111中的preHandle 执行了拦截器MyInterceptor-22222中的preHandle 执行了拦截器MyInterceptor-1111中的afterCompletion
(3) 第一个拦截器preHandle=false , 第二个拦截器preHandle=true|false
执行了拦截器MyInterceptor-1111中的preHandle
(4) 执行流程
5. 源码剖析
5. 拦截器和过滤器的区别
-
过滤器是servlet中的对象,由tomcat创建的对象, 是tomcat调用的过滤器执行 拦截器是springmvc指定对象,由容器springmvc创建的, 在中央调度器中调用执行的。
-
过滤器是一个执行时间,在所有请求之前 拦截器有三个执行时间, 处理器方法之前, 处理器方法之后,视图执行之后
-
过滤器是侧重对象request,response对象的属性,参数设置值的。例如:request.setCharacterEncoding('utf-8'); 拦截器是侧重验证请求是否正确, 能截断请求。
-
过滤器是能过滤所有请求的,静态资源和动态资源的都能经过过滤器。 拦截器是拦截对controller的请求的,也就是只能拦截动态资源的(jsp也不行)。
五、SSM整合开发。
SSM:SpringMVC + Spring + MyBatis
-
SpringMVC:做web开发的,能够接受用户的请求参数, 返回结果
- SpringMVC是基于spring的,它是容器。SpringMVC能创建容器对象WebApplicationContext
- springmvc能够管理Controller对象 ,视图相关对象,所以应该把controller,视图等对象定义在springmvc配置文件中。
-
Spring:作为容器管理,创建service, dao对象, 可以创建具有事务功能的service对象。
- Spring是容器对象, 创建WebApplicationContext对象, 管理Service,dao等对象,也就是非web中的对象。 应该把service,dao等对象都交给spring容器,定义在spring的配置文件中。
-
MyBatis:访问数据库
SpringMVC和Spring各自创建容器, 在一个项目中同时存在两个容器对象,这个两个容器对象有什么关系呢 ?
- SpringMVC容器对象是Spring容器对象的子容器。
- SpringMVC容器是子,Spring容器父,那么子能够访问父的内容。
用户发起请求---springmvc中的Controller处理请求----spring容器中的Service真正处理请求的---spring容器中的dao对象