SpringMVC框架是基于Spring框架,可以让我们更为方便的进行Web的开发,实现前后端分离
思路和原理
我们之前仿照SpringMVC定义了一个自定义MVC框架,两者的思路其实都是一样的。
建议结合两篇文章进行学习
JSP学习笔记(6)—— 自定义MVC框架
首先,提供一个前置拦截器(DispatchServlet),拦截url请求,之后,根据url请求,跳转到Controller层,执行操作,之后再返回数据
入门
我的demo是使用了maven框架
1.创建maven项目
2.添加依赖
修改pom.xml,添加依赖
刚开始的时候,使用的是最新版本(5.x.x),然后发现有个奇怪的错误,折腾了许久找不到方法,于是便用了4.x.x版本的,果然没有出现问题了,果然是新版本都不好用。。
junit
junit
4.11
test
commons-logging
commons-logging
RELEASE
org.springframework
spring-context
4.3.9.release
org.springframework
spring-context-support
4.3.9.release
org.springframework
spring-test
4.3.9.release
org.springframework
spring-core
4.3.9.release
org.springframework
spring-beans
4.3.9.release
org.springframework
spring-expression
4.3.9.release
org.springframework
spring-web
4.3.9.release
org.springframework
spring-webmvc
4.3.9.release
org.springframework
spring-aop
4.3.9.release
org.aspectj
aspectjweaver
RELEASE
aopalliance
aopalliance
RELEASE
com.fasterxml.jackson.core
jackson-core
2.7.3
com.fasterxml.jackson.core
jackson-databind
2.7.3
org.apache.commons
commons-lang3
3.3.2
javax.servlet
javax.servlet-api
4.0.1
org.apache.taglibs
taglibs-standard-spec
1.2.5
org.apache.taglibs
taglibs-standard-impl
1.2.5
com.github.noraui
ojdbc8
12.2.0.1
org.mybatis
mybatis
3.5.2
org.mybatis
mybatis-spring
2.0.2
3.项目结构优化
由于是根据maven模板新建的项目,所以项目结构还需要添加一些文件夹
在main文件夹下新建java文件夹
把java文件夹设置为source directory
在java文件夹下新建自己的包名,然后新建一个controller文件夹和model文件夹,顺便也新建一个resources的文件夹,和上面同样的步骤,设置为resources directory
除此之外,还需要在webapp文件夹下新建一个views文件夹
4.设置Tomcat配置
我这里是设置好了,没有设置的话,是没有下拉菜单的,但是有个add configuration的选项
选择tomcat的设置,选择local,如果没有Tomcat的选项,可以点击选项最下面的show more
点击之后,添加构造物
选择那个exploded的选项
设置url
之后开始运行Web程序,就可以通过访问http://localhost:8080/springmvcdemo
来访问Web项目的首页
5.新建springmvc配置文件
springmvc配置文件和之前的spring文件一样,都是进行相关的bean的配置,这里由于是资源文件,所以按照规则我们放入resources文件夹中
springmvc-config.xml
配置中有个bean类,是配置视图解析器(也就是最后的那个bean标签),我们使用的是InternalResourceViewResolver
此解析器会把请求处理类(controller类)处理方法的返回值按照“前缀+方法返回值+后缀”的格式进行加工,并把加工后的返回值作为路径进行跳转
除此之外,还有其他的解析器,下面会进行补充说明
6.配置web.xml文件
由于我们使用的maven的模板创建的web项目,web.xml里面的内容并不是我们所需要的,所以还得进行内容的更改
SpringMVC Demo
springmvc-DispatcherServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:springmvc-config.xml
1
true
springmvc-DispatcherServlet
/
7.测试
我们用一个简单的例子去了解springmvc的使用方式
我们编写一个controller类,用来模拟实现登录操作,登录成功,跳转到登录成功的页面success.jsp
UserController.java
package com.wan.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author StarsOne
* @date Create in 2019/9/28 0028 14:27
* @description
*/
@Controller
public class UserController {
@RequestMapping("/user/login")
public String login() {
//这里的返回,之后会加上前缀和后缀
//相当于页面跳转到 views/success.jsp 页面
return "success";
}
}
success.jsp
中,只有一个简单的“登录成功”文字
index.jsp中,有个链接,请求url为user/login
登录
之后就是可以跳转到了页面
PS:上面方法返回了一个success,,会被自动加入前缀和后缀,注意,这里是进行的请求转发
除此之外,我们还可以加上forward
或redirect
前缀来进行请求转发或重定向
但是,如果使用这两种前缀,之后视图解析器就不会自动添加前缀和后缀了。所以,我们得指定具体跳转的url地址。
@RequestMapping("/user/login")
public String login() {
//请求转发
return "forward:/views/success.jsp";
}
@RequestMapping("/user/login")
public String login() {
//重定向
return "redirect:/views/success.jsp";
}
RequestMapping注解
高级使用
springmvc框架中的RequestMapping注解不像我们之前自定义MVC框架的注解那样简单,它的还可以标注一个类
例如:
package com.wan.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author StarsOne
* @date Create in 2019/9/28 0028 14:27
* @description
*/
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String login() {
//这里的返回,相当于页面跳转到 views/success.jsp 页面
return "success";
}
}
我们链接上的url请求就是/user/login
,而不能使用login
属性说明及使用
属性 | 说明 |
---|---|
value | 指定请求的实际url地址,是默认属性,如@RequestMapping("/login") 相当于@RequestMapping(value="/login" |
method | 指定请求的方法,post或get |
params | 规定请求中的参数必须满足一定的条件 |
header | 规定请求中的请求头(header)必须满足一定的条件 |
1.method
package com.wan.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author StarsOne
* @date Create in 2019/9/28 0028 14:27
* @description
*/
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login",method="RequestMethod.POST")
public String login() {
//这里的返回,相当于页面跳转到 views/success.jsp 页面
return "success";
}
}
之后如果请求不是post方式,就会出现405错误
2.params
使用此属性可以对请求的参数进行约束
例子:
package com.wan.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author StarsOne
* @date Create in 2019/9/28 0028 14:27
* @description
*/
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login",params={"name","age!=23"})
public String login() {
//这里的返回,相当于页面跳转到 views/success.jsp 页面
return "success";
}
}
上面的例子约束的url的请求参数必须要包含有name和age,且age不能等于23,如果不满足条件,就会发生404错误
如:
登录
params中可以接收以下表达式
表达式 | 说明 |
---|---|
paramName | url请求必须包含paramName此参数名 |
!paramName | url请求不能包含paramName此参数名 |
paramName!=xx | url请求必须包含paramName此参数名,且此参数数值不等于xx |
header比较少用,这里就不补充了
获得请求url参数值
获得url请求的参数值,我们可以使用RequestParam注解
使用此注解,可以把url请求参数的数值赋值给方法参数
下面是@RequestParam注解的常用属性说明
属性 | 说明 |
---|---|
value | 请求携带参数的参数名 |
required | 标识请求url参数是必须存在某个具体的参数,true(默认):必须存在,不存在则会发生异常;false:不存在 |
defaultValue | 给方法参数赋一个默认值,如果请求url不存在此参数,则使用默认值 |
package com.wan.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author StarsOne
* @date Create in 2019/9/28 0028 14:27
* @description
*/
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String login(@RequestParam(value="username") String name) {
//这里的返回,相当于页面跳转到 views/success.jsp 页面
return "success";
}
}
请求url为user/login?username=zhang
,之后便会把此请求参数赋值给方法参数name
和我们自定义mvc框架一样,springmvc框架中,我们也可以直接使用实体类、session、request、response作为方法的参数
@RequestMapping("/user/login")
public login(Student student){
...
}
@RequestMapping("/user/login")
public login(HttpServletRequest request,HttpServletResponse response,HttpSession session){
...
}
与RequestParam
类似的还有这两个RequestHeader
和CookieValue
这两个注解我现在没怎么用到,暂时了解一下,不作补充
- RequestHeader注解,主要是用来获得请求头的数据
- CookieValee注解,主要是用来获得一个cookieValue
返回json数据
我们使用@ResponseBody
,方法返回实体类或者集合的时候,springmvc就会自动帮我们转为json数据
使用之前需要导入这两个jar,jackson-core.jar
和jackson-databind.jar
,之前的依赖已经包含下面这两个jar了
com.fasterxml.jackson.core
jackson-core
2.7.3
com.fasterxml.jackson.core
jackson-databind
2.7.3
UserController.java
package com.wan.controller;
import com.wan.model.Teacher;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author StarsOne
* @date Create in 2019/9/28 0028 14:27
* @description
*/
@Controller
@RequestMapping("/user")
public class UserController {
@ResponseBody
@RequestMapping("/login")
public Teacher login() {
return new Teacher("001", "张三");
}
}
之后在jsp页面中使用ajax异步请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
就可以在控制台看到打印出来的json数据
处理Model数据
SpringMVC中,M其实就是代表着Model(模型),也就是相当于数据
假设我们要从数据查询数据:先从页面(View)发送url请求,然后控制器(Controller)通过Service/Dao从数据库中获得了数据(Model),并把数据进行处理,使得数据能够传回页面(View)并显示。
如果是异步请求的话,我们可以返回一个json数据到页面,如果不是的话,我们就得把数据存放在request或session的作用域里,之后由页面(View)从作用域中取出数据并显示
SpringMVC提供了四种方法来处理那些需要从作用域中取出数据显示的视图
- ModelAndView
- Map、ModelMap和Model
- @SessionAttributes
- @ModelAttribute
1.ModelAndView
此类一般用来作方法的返回值来实现返回一个带数据的页面(View)
UserController.java
package com.wan.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author StarsOne
* @date Create in 2019/9/28 0028 14:27
* @description
*/
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public ModelAndView login() {
String view = "success";
ModelAndView mav = new ModelAndView(view);
Teacher teacher = new Teacher("001","张三");
//相当于request.addAttribute("teacher",teacher)
mav.addObject("teacher",teacher);
return mav;
}
}
success.jsp中取出数据并显示
${requestScope.student.tno}
上面的例子和之前一样,还是会加上前缀和后缀,得到views/success.jsp
2.Map、ModelMap和Model
Map、ModelMap和Model一般使用作为方法的参数,之后,通过put方法往里面存入数据
package com.wan.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author StarsOne
* @date Create in 2019/9/28 0028 14:27
* @description
*/
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String login(Map map) {
String view = "success";
Teacher teacher = new Teacher("001","张三");
//相当于request.addAttribute("teacher",teacher)
map.put("teacher",teacher);
return "success";
}
}
ModelMap和Map的使用方法一样
还可以使用Model
@RequestMapping("/login")
public String login(Model model) {
String view = "success";
Teacher teacher = new Teacher("001","张三");
//相当于request.addAttribute("teacher",teacher)
model.addAttribute("teacher",teacher);
return "success";
}
Model类还可以使用添加一个map数据,addAllAttribute(Map
3.@SessionAttributes
前面的两个方法,都是放入到request的作用域里,如果我们想放入session作用域,可以使用@SessionAttributes注解,一般标注在类上
@SessionAttributes可以将指定的对象加入到session范围,也可以将某个类型的对象加入到session中
下面的例子,指定了key为teacher的对象,添加到了session作用域
package com.wan.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author StarsOne
* @date Create in 2019/9/28 0028 14:27
* @description
*/
@Controller
@RequestMapping("/user")
@SessionAttributes(value="teacher")
public class UserController {
@RequestMapping("/login")
public String login(Map map) {
String view = "success";
Teacher teacher = new Teacher("001","张三");
//在添加到request作用域,同时也添加了session作用域
map.put("teacher",teacher);
return "success";
}
}
把Teacher类型的对象添加到session作用域中
package com.wan.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author StarsOne
* @date Create in 2019/9/28 0028 14:27
* @description
*/
@Controller
@RequestMapping("/user")
@SessionAttributes(type=Teacher.class)
public class UserController {
@RequestMapping("/login")
public String login(Map map) {
String view = "success";
Teacher teacher = new Teacher("001","张三");
//在添加到request作用域,同时也添加了session作用域
map.put("teacher",teacher);
return "success";
}
}
4.@ModelAttribute
使用情况:
我们需要对数据进行更新,但我们只能更新数据的某个属性。
我们点击编辑之后,只有一个输入框让我们输入,用来更改那个属性,我们输入更改后的属性值,更新的时候会发现,controller里面的数据,传入的对象除了更改的那个属性值,其他的属性值都是为null,我们不希望这种情况,所以,使用此注解
此注解用来修饰controller里的某个方法,然后就会在执行controller中@RequestMapping的方法之前执行,把传入的对象中的数据更新,之后执行修改操作的话,只会把对象需要修改的属性值更改,其他的属性值不变(不为null了)
我个人觉得这样好像比较麻烦,上面的情况有个更好的解决办法,就是不要使用唯一的输入框,而是使用多个输入框,把不能更改的那几项输入框设置disable,之后提交表单也能成功传入其他的属性值
视图解析器
工作流程
springmvc框架中,请求处理方法(Controller中的方法)执行完成后,最终返回一个ModelAndView 对象。
Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是JSP ,也可能是Excel、JFreeChart 等各种表现形式的视图
对于最终究竟采取何种视图对象对模型数据进行渲染(也就是我们常说的把数据从request等作用域取出来显示到页面上),处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现MVC 的充分解耦
对于那些返回String,View 或ModeMap 等类型的处理方法,Spring MVC 也会在内部将它们装配成一个ModelAndView 对象,它包含了逻辑名和模型对象的视图;
如下图:
View
springmvc中的View其实是个接口,下面是常见的View接口的实现类
视图类型 | 实现类 | 说明 |
---|---|---|
URL视图类型 | InternalResourceView | 将JSP或其他资源封装成一个视图。被视图解析器InternalResourceViewResolver默认使用。 |
URL视图类型 | JstlView | InternalResourceView的子类。如果JSP中使用了JSTL的国际化标签,就需要使用该视图类 |
文档视图 | AbstractExcelView | Excel文档视图的抽象类 |
文档视图 | AbstractPdfView | PDF文档视图的抽象类 |
报表视图 | ConfigurableJasperReportsView | |
报表视图 | JasperReportsHtmlView | |
报表视图 | JasperReportsPdfView | |
报表视图 | JasperReportsXlsView | |
JSON视图 | MappingJackson2JsonView | 将数据通过Jackson框架的ObjectMapper对象,以JSON方式输出 |
ViewResolver以及子类
ViewResolver和View一样,也是个接口
视图解析器类型 | 类名 | 说明 |
---|---|---|
解析为映射文件 | UrlBasedViewResolver | 它简单实现了ViewResolver接口, 不用任何映射就能通过逻辑视图名称访问资源 |
解析为映射文件 | InternalResourceViewResolver | 将逻辑视图名解析为一个路径 |
解析为bean | BeanNameViewResolver | 将逻辑视图名解析为bean的name属性,从而根据name属性去找对应的bean |
解析为bean | ResourceBundleResolver | 和BeanNameViewResolver一样,只不过定义的view-bean都在一个properties文件中,用这个类进行加载这个properties文件 |
解析为bean | XmlViewResolver | 和ResourceBundleResolver一样,只不过定义的view-bean在一个xml文件中,用这个类来加载xml文件 |
解析为模版文件 | VelocityViewResolver | 对Velocity模版引擎的支持 |
解析为模版文件 | FreeMarkerViewResolver | 对FreeMarker模版引擎的支持 |
这里,我只介绍以下前两种,也就是我们用过的类型。更多请参考我们下面给出的链接
AbstractCachingViewResolver,这个类为抽象类,实现了ViewResolver接口,抽象类只能被继承,无法创建实例。
UrlBasedViewResolver就是继承于AbstractCachingViewResolver这个类,从而扩展了功能。
AbstractCachingViewResolver介绍:
这种视图解析器会把它曾经解析过的视图保存起来,然后每次要解析视图的时候先从缓存里面找。
如果找到了对应的视图就直接返回,如果没有就创建一个新的视图对象,然后把它放到一个用于缓存的map中,接着再把新建的视图返回。
使用这种视图缓存的方式可以把解析视图的性能问题降到最低。
UrlBasedViewResolver介绍:
继承了AbstractCachingViewResolver,主要就是提供的一种拼接URL的方式来解析视图,它可以让我们通过prefix
属性指定一个指定的前缀,通过suffix
属性指定一个指定的后缀,然后把返回的逻辑视图名称加上指定的前缀和后缀就是指定的视图URL了。
InternalResourceViewResolver介绍:
这个类是继承于UrlBasedViewResolver,UrlBasedViewResolver具有的功能它都有,而且还有它自己的特性。从字面翻译,InternalResourceViewResolver就是内部资源解析器。
InternalResourceViewResolver会把返回的视图名称都解析为InternalResourceView对象,InternalResourceView会把Controller处理器方法返回的模型属性都存放到对应的request属性中,然后通过RequestDispatcher在服务器端把请求forword重定向到目标URL。
参考链接:Spring MVC-从零开始-view-ViewResolver
静态资源访问
情景
如果我们想要通过一个url去访问我们项目中的一个图片、js文件、视频等静态资源,会发现报404错误
原因是我们定义一个前置Servlet,处理了所有的url请求,但是,由于未能找到RequestMapping注解上的相匹配的url,所以就会出现404错误
解决方法
在springmvc配置文件中添
和
即可解决问题
之前给出的springmvc配置文件中,其实已经添加了这两个标签,在这里稍微介绍一下作用
作用:在SpringMVC上下文中,定义了一个DefaultServletHttpRequestHandler,它会对鄋DispatcherServlet处理的请求进行检查,如果发现某个请求没有对应的@RequestMapping进行处理,就会将该请求交个Web服务器默认的Servlet进行处理,二默认的Servlet就会直接根据url去访问该资源
作用:访问静态资源的同时,眼能够正常的访问其他非静态资源
两个标签都要添加
中文乱码方法(补充)
1. 设置页面编码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2.配置过滤器
在web.xml中配置
SpringMVC Demo
characterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
characterEncodingFilter
/
springmvc-DispatcherServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:springmvc-config.xml
1
true
springmvc-DispatcherServlet
/
更多方法,请查看参考链接
参考链接:彻底解决springMVC中文乱码