SpringMVC学习笔记
1 简介
1.1 定义
首先,MVC是模型(Model),视图(View),控制器(Controller)的简写,是一种软件设计规范
- 模型(Dao,Service)
- 视图(jsp/html)
- 控制器(Servlet)
SpringMVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架
1.2 设计
Spring的web框架围绕DIspatcherServlet设计,DispatcherServlet的作用是将请求分发到不同处理器,整个框架围绕一个中心Servlet分派请求并提供其他功能
2 HelloSpringMVC
2.1 xml配置开发
-
导入SpringMVC的依赖
-
在WEB-INF/jsp下创建hello.jsp
<body>
${msg}
</body>
- 配置web.xml,注册DispatcherServlet
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--绑定Spring的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servelt.xml</param-value>
</init-param>
<!--启动级别 1-->
<load-on-startup>1</load-on-startup>
</servlet>
<serlvet-mapping>
<servlet-name>springmvc</servlet-name>
<!--
/ 只匹配所有请求,不匹配jsp页面
/* 匹配所有请求,包括jsp页面
-->
<url-pattern>/</url-pattern>
</serlvet-mapping>
- 配置springmvc-servlet.xml
<!--配置处理器映射器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--配置处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
- 创建HelloController,实现Controller接口
public class HelloController implements Controller {
@Override
public ModelAndView handlerRequest(HttpServletRequest req, HttpServletResponse resp) throws Throwable {
ModelAndView mv = new ModelAndView();
String result = "HelloSpringMVC";
mv.addObject("msg", result);
mv.setViewName("hello");
return mv;
}
}
- 因为使用了BeanNameUrlHandlerMapping处理器映射器,所以要在Spring中注册bean
<bean id="/hello" class="com.hjc.controller.HelloController"/>
- 配置Tomcat,运行,在url输入localhost:8080/hello
在实际开发中,一般不会写这么多配置文件,而是使用注解配置
2.2 注解配置开发
- 导入SpringMVC的依赖
- 在WEB-INF/jsp下创建hello.jsp
<body>
${msg}
</body>
- 配置web.xml,注册DispatcherServlet
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--绑定Spring的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servelt.xml</param-value>
</init-param>
<!--启动级别 1-->
<load-on-startup>1</load-on-startup>
</servlet>
<serlvet-mapping>
<servlet-name>springmvc</servlet-name>
<!--
/ 只匹配所有请求,不匹配jsp页面
/* 匹配所有请求,包括jsp页面
-->
<url-pattern>/</url-pattern>
</serlvet-mapping>
- 配置springmvc-servlet.xml
<!--配置自动扫描包-->
<context:component-scan base-package="com.hjc.controller"/>
<!--不处理静态资源-->
<mvc:default-servlet-handler/>
<!--支持mvc注解驱动-->
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
- 创建HelloController
@Controller
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/test")
public String hello(Model model) {
model.addAttribute("msg", "HelloSpringMVC");
return "hello"; //返回结果会被视图解析器处理
}
}
- 配置Tomcat,运行,在url输入localhost:8080/hello/test
总结
- 不使用注解开发要配置处理器映射器、处理器适配器和视图解析器
- 使用注解开发只需要配置视图解析器
3. Controller相关配置
- 控制器提供访问应用程序的行为,通常通过接口定义(2.1)或注解定义(2.2)实现,一般开发中使用注解实现
- 控制器负责解析用户的请求并将其转化为一个模型
- 一个控制器可以包含多个方法
3.1 @Controller
这个注解和@Component、@Service、@Repository作用相同,只是为了区分不同的类。@Controller将类注册到Spring容器中,并声明类的实例是一个控制器
- @Component:组件
- @Service:service
- @Controller:controller
- @Repository:dao
3.2 @RequestMapping
- value属性
这个注解可以写在类名上或者方法名上,在类名上的注解和方法名上的注解是一种上下级的关系,value属性内部写访问的域名
- 类定义处:提供初步的请求映射信息,相对于WEB应用的根目录
- 方法处:提供进一步的细分映射信息,相对于类定义处的URL。若类定义处未标注@RequestMapping,则方法处标记的URL相对于WEB应用的根目录
- method属性
@RequestMapping内的method属性可以限定请求方式,表示只接收这种类型的请求,默认是全部接受
- params属性
@RequestMapping内的params属性可以规定请求参数,params属性接收一个数组,用大括号表示,支持简单的表达式
- param1:表示请求必须包含为名param1的请求参数,如 params=
- !param1:表示请求不能包含名为param1的请求参数,如 params=
- param1 = value1:表示请求必须包含名为param1的请求参数,且其值必须为value1,如 params=
- param1 != value1:表示请求如果包含名为param1的请求参数,其值不能为value1,或者请求不包含为名param1的请求参数,如 params=
- headers属性
@RequestMapping内的headers属性可以规定请求头,写法同params属性
headers是由HTTP协议规定的,headers中可以规定只允许某个浏览器访问等信息
- consumers属性
consumers属性可以规定只接受内容类型是哪种的请求,规定请求头中的Content-Type
- produces属性
produces属性可以规定浏览器返回的内容类型是什么,给响应头中加入Content-Type
3.3 @RequestMapping-Ant风格的URL
URL地址可以写模糊的通配符
- ?:匹配任意一个字符,0个多个都不行
- *:匹配任意多个字符和一层路径
- **:匹配多层路径
模糊和精确多个匹配的情况下,精确的优先匹配
3.4 @PathVariable
@RequestMapping注解中写入的URL可以有占位符,语法为{变量名}
,这样在方法参数上可以通过@PathVariable
注解获取URL中的变量名
@RequestMapping("/test/{id}")
public String test(@PathVariable("id") String id) {
//...
}
URL中可以有多个占位符,但一个占位符只能占一层路径
@PathVariable可以支持REST风格
3.5 方法返回String
因为配置了视图解析器,Controller内部的方法只要返回String,返回结果会被视图解析器处理
比如
return "hello";
视图解析器会根据配置的内容
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
去寻找/WEB-INF/jsp/hello.jsp文件
4 RESTful风格
4.1 概念
RESTful是一个资源定位及资源操作的风格,不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更加简洁,更有层次,更易于实现缓存机制
对资源的操作不以URL的命名为依据,而是以HTTP协议中请求方式GET、POST、PUT和DELETE来区分对资源的操作,GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源
4.2 功能
- 资源操作:使用POST、DELETE、PUT、GET等不同方法对资源进行操作
REST希望以非常简洁的URL地址来发请求,比如,对于图书的操作有以下四种情况
- /getBook?id=1:查询图书
- /deleteBook?id=1:删除图书
- /updateBook?id=1:更新图书
- /addBook?id=1:添加图书
对于这四种传统的URL写法,REST推荐我们对URL的命名为/资源名/资源标识符
- /book/1 GET请求:查询图书
- /book/1 PUT请求:更新图书
- /book/1 DELETE请求:删除图书
- /book POST请求:添加图书
可以看到,REST风格的URL地址更加简洁,而以请求方式来区别对资源的操作
传统方式
@Controller
public class HelloController {
@RequestMapping("/add")
public String add(int a, int b, Model model) {
int res = a + b;
model.addAttribute("msg", "answer is " + res);
return "hello";
}
}
那么,向这个控制器发送请求,我们需要在url中输入localhost:8080/add?a=1&b=2,最后在页面上能显示“answer is 3”
RESTful风格
@Controller
public class HelloController {
@RequestMapping(value="/add/{a}/{b}", method="RequestMethod.GET")
public String add(@PathVariable("a") int a, @PathVariable("b") int b, Model model) {
int res = a + b;
model.addAttribute("msg", "answer is " + res);
return "hello";
}
}
使用@PathVariable注解,使方法参数的值对应绑定到URI模板变量上。用@RequestMapping来限制请求方法,可以直接用@GetMapping("/add/{a}/{b}")来代替。此时,需要在url输入localhost:8080/add/1/2,且请求方法为get才能访问到。如果将method改为RequestMethod.POST,或者注解改为@PostMapping("/add/{a}/{b}"),那么url不变,请求方法为post才能访问到
再举个例子,我们来实现上文所描述的四种对图书的操作
@Controller
public class BookController {
@RequestMapping(value="/book/{bid}", method="RequestMethod.GET")
public String getBook(@PathVariable("bid") Integer id) {
//...
}
@RequestMapping(value="/book/{bid}", method="RequestMethod.DELETE")
public String deleteBook(@PathVariable("bid") Integer id) {
//...
}
@RequestMapping(value="/book/{bid}", method="RequestMethod.PUT")
public String updateBook(@PathVariable("bid") Integer id) {
//...
}
@RequestMapping(value="/book/{bid}", method="RequestMethod.POST")
public String addBook(@PathVariable("bid") Integer id) {
//...
}
}
问题是,从页面上发起GET和POST请求是简单的,但是如何发起DELETE和PUT请求
Spring提供了对REST风格的支持
- SpringMVC中有一个Filter,可以把普通的请求转化为规定的请求,首先在web.xml文件中配置这个filter
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
<filter-mapping>
- 发起请求:创建一个POST请求的表单,表单项中携带一个_method的参数,_method的值表示要发起的请求类型(DELETE,PUT)
<form action="/book/1" method="post">
<input name="_method" value="delete">
<input type="submit" value="删除1号图书">
</form>
<form action="/book/1" method="post">
<input name="_method" value="put">
<input type="submit" value="更新1号图书">
</form>
4.3 优点
- 使路径变得更加简洁
- 获得参数更加方便,框架会自动进行类型转换
5 结果跳转方式
5.1 ModelAndView
在控制器方法内部,要设置ModelAndView对象,根据view的名字和视图解析器跳到指定的页面
- 页面 = 视图解析器前缀 + viewName + 视图解析器后缀
5.2 ServletAPI
我们也可以通过ServletAPI,不设置ModelAndView和视图解析器来进行页面跳转
- 通过HttpServletResponse进行输出
- 通过HttpServletResponse实现重定向
- 通过HttpServletResponse实现转发
这些原生API进行重定向和转发的时候需要在路径之前加上项目名
这种方法实际项目中不推荐使用
5.3 SpringMVC实现转发和重定向
1.通过SpringMVC实现转发和重定向,不经过视图解析器
@Controller
public class TestController {
@RequestMapping("/tc/t1")
public String test1() {
//实现转发
return "/WEB-INF/jsp/hello.jsp";
}
@RequestMapping("/tc/t2")
public String test2() {
//实现转发
return "forward:/WEB-INF/jsp/hello.jsp";
}
@RequestMapping("/tc/t3")
public String test3() {
return "forward:/index.jsp"
}
@RequestMapping("/tc/t4")
public String test4() {
return "forward:/tc/t3";
}
@RequestMapping("/tc/t5")
public String test5() {
//实现重定向
return "redirect:/index.jsp"; //重定向无法直接访问到WEB-INF目录下的文件
}
@RequestMapping("/tc/t6")
public String test6() {
return "redirect:/tc/t5";
}
}
没有视图解析器,我们在写页面路径的时候要写其对应的全限定名
另外,不管有没有视图解析器,forward可以跳过视图解析器,不进行地址的拼串,而且forward之后可以跟项目路径,也可以跟请求路径
第二个方法是转发到当前项目的WEB-INF/jsp目录下的页面,而第三个方法是转发到当前项目下的页面,注意区别
第四个方法也是转发到当前项目下的页面,只是多经过了一次请求
同样,redirect也是跳过视图解析器,不进行地址的拼串,且redirect之后也可以跟项目路径和请求路径,比如第五个方法和第六个方法
2.通过SpringMVC来实现转发和重定向,经过视图解析器
@Controller
public class TestController {
@RequestMapping("/tc/t1")
public String test1() {
//实现转发
return "hello";
}
}
上面的带面是转发到WEB-INF/jsp下的hello.jsp页面
有了视图解析器,控制器方法返回字符串默认就是转发的情况
3.使用xml配置view-controller
在controller中有些方法不会有额外的逻辑功能,只是单纯地进行请求转发,比如上述代码,那么这些方法一旦增加,在controller类中显得多余
我们可以使用xml配置这些转发
<mvc:view-controller path="/tc/t1" view-name="hello"/>
上述代码可以写成这样的xml配置,这两者是等价的,返回的hello也要经过视图解析器,去访问/WEB-INF/jsp目录下的hello.jsp页面
但是,使用此方法一定要先配置开启注解驱动模式,否则,其他用注解配置的请求路径会无效
<mvc:annotation-driven/>
6 数据处理和交互
6.1 数据从前端到控制器
控制器要接收前端的数据进行处理,那么有这么几种情况
- 提交的域名称和处理方法的参数名一致
url:localhost:8080/hello?name=test
处理方法:直接给方法入参上写一个和请求参数名相同的变量,这个变量就来接受请求参数的值,请求中带了参数,这个变量就有值,没带参数,变量为null
@RequestMapping("/hello")
public String hello(String name) {
System.out.println(name);
return "hello";
}
- 提交的域名称和处理方法的参数不一致
url:localhost:8080/hello?username=test
处理方法,使用@RequestParam
注解明确指定请求参数名,相当于name = request.getParameter("username");
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name) {
System.out.println(name);
return "hello";
}
或者
@RequestMapping("/hello")
@RequestParam("username")
public String hello(String name) {
System.out.println(name);
return "hello";
}
在@RequestParam中可以加入几种属性
- value:指定要获取的参数
- required:指定参数是否必须
- defaultValue:指定参数的默认值
注意与@PathVariable注解区分
- @PathVariable注解是获取URL路径中的参数
- @RequestParam注解是获取请求中的参数
比如,有url/book/{user}?user=admin
,第一个user由@PathVariable获取,第二个user由@RequestParam获取
另外,还存在注解@RequestHeader
和@CookieValue
来获取数据
- @RequestHeader注解用来获取请求头中某个参数的值,使用方法与@RequestParam类似,包含的属性也相同
- @CookieValue注解是用来获取某个cookie的值,使用方法为
@CookieValue("JSESSIONID") String jid
来获取cookie中的JSESSIONID的值,使用方法也和@RequestParam类似,包含的属性也相同
- 提交的是一个对象(POJO类)
如果请求的参数是一个POJO,SpringMVC会自动为这个POJO进行赋值(将POJO中的每一个属性从request参数中尝试获取出来并封装,还可以级联封装,即对象中有对象)
要求提交的表单域和对象的属性名一致,方法的参数使用对象
比如一个表单要提交属性name、id和age,那么在User实体类中要保证有属性name,id,age
处理方法
@RequestMapping("/hello")
public String hello(User user) {
System.out.println(user);
return "hello";
}
如果前端传递的参数名和对象属性名不一致,结果会输出null
- SpringMVC直接在参数上写原生API
SpringMVC支持在Controller方法内直接使用Servlet原生API,比如HttpServletRequest,HttpServletResponse,HttpSession等
@RequestMapping("/handle")
public String handle(HttpSession session, HttpServletRequest request) {
//... 方法内可直接使用session和request
}
6.2 数据从控制器到前端
数据从控制器传到前端,我们可以使用原生API,比如HttpServletResponse和HttpSession,除了此方法外还有多种方法
-
在方法处传入
Map
、Model
或者ModelMap
,在这些参数中保存的所有数据都会放在请求域中,可以在前端页面获取
这三者的关系:Map、Model和ModelMap在这个过程中最终都是BindingAwareModelMap在工作,也就是说BindingAwareModelMap中保存的数据都会被放在请求域中
Map和Model都是接口,而ModelMap是一个继承了LinkedHashMap的类,也就是实现了Map,而BindingAwareModelMap最终是实现了Model接口,继承了ModelMap类 -
方法的返回值可以使用ModelAndView类型,在方法中创建一个ModelAndView的对象,有参构造器传入的参数为视图名,也就是要跳转的页面名字,接着就可以使用这个对象保存数据,放在请求域中
-
使用注解
@SessionAttributes
可以临时给session域中保存数据,不推荐使用,推荐使用原生API中的HttpSession
这个注解只能写在类上,其中的value属性限定了给BindingAwareModelMap或者ModelAndView中传入数据时的key值,如果是value属性中的元素,那么要将数据同时传入到session域中
比如,@SessionAttributes(value={"msg", "name"})表示当返回数据的key为msg或者name时,也要讲数据保存到session中
types属性限定了给BindingAwareModelMap或者ModelAndView中传入数据的类型,如果满足types中的类型,那么要将数据同时传入到session域中
比如,@SessionAttributes(types={String.class})表示当返回数据的类型为String时,也要讲数据保存到session中
大部分情况下,一般使用Model
7 乱码问题
从前端提交的数据到控制器会出现乱码问题,我们使用过滤器解决,直接在web.xml中配置SpringMVC提供的过滤器
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--解决post编码-->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<!--解决响应编码-->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
字符编码的filter一定要在其他的filter之前,也就是web.xml文件中写在所有的filter首位,否则可能会无效
当然,我们也可以用自己编写的过滤器,只要实现Filter接口
8 数据转换、数据格式化和数据校验
8.1 数据转化
8.2 数据格式化
8.3 数据校验
只做前端的数据校验是不安全的,重要的数据一定要加上后端校验
- 可以将接收到的数据取出进行校验,如果校验失败就返回前端重新填写数据
- SpringMVC可以利用JSR303进行数据校验
JSR303是Java为Bean数据合法性校验提供的标准框架,通过在Bean属性上标注注解来指定校验规则,并通过标准的验证接口对Bean进行验证
Hibernate Validator是JSR303的一个参考实现
使用方法
- 导入依赖包
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.9.Final</version>
</dependency>
- 在JavaBean的属性上加上相应的注解
public class User {
private Integer id;
@NotEmpty
@Length(max=20)
private String name;
@Email
private String email;
@Past
private Date birth;
}
- 在SpringMVC封装对象的时候告知这个JavaBean需要校验,在相应的controller方法参数前加上
@Valid
注解
@RequestMapping(value="/user", method=RequestMethod.POST)
public String addUser(@Valid User user) {
//...
}
- 在方法参数中在添加
BindingResult
来封装校验结果
@RequestMapping(value="/user", method=RequestMethod.POST)
public String addUser(@Valid User user, BindingResult result) {
//...
}
- 根据不同的校验结果在方法中做出不同的响应
@RequestMapping(value="/user", method=RequestMethod.POST)
public String addUser(@Valid User user, BindingResult result) {
if (result.hasErrors()) {
// 如果校验有错误
// 获取错误消息,放入model中返回给前端
} else {
// 如果校验没有错误
}
}
9 JSON
前后端分离,后端部署后端,后端提供接口,前端独立部署,渲染后端数据,前后端通过json格式来交换数据
9.1 概念
JSON(JavaScript Object Notation,JS对象标记)是一种轻量级数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据
9.2 语法
- 对象表示为键值对,数据由逗号分隔
- 花括号表示对象
- 方括号保存数组
JSON是JavaScript对象的字符串表示法,使用文本表示一个JS对象的信息,本质是一个字符串
var obj = {a: 'Hello', b: 'world'}; //这是一个对象,键名也可以使用引号
var json = '{"a": "Hello", "b": "world"}'; //这是一个JSON字符串
JSON和JavaScript对象互转
var obj = JSON.parse('{"a": "Hello", "b": "world"}');
//结果为 {a: 'Hello', b: 'world' }
var json = JSON.stringify({a: 'Hello', b: 'world'});
//结果为 '{"a": "Hello", "b": "world"}'
9.3 Controller返回JSON数据
使用JSON解析工具Jackson或者fastjson
- 导入Jackson的包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
- 配置web.xml(DispatcherSerlvet、CharacterEncodingFilter)
- 配置springmvc-servlet.xml(自动扫描包,视图解析器)
- 编写User实体类
public class User {
private String name;
private int age;
private String sex;
//省略get/set方法和构造函数
}
- 编写UserController
@Controller
public class UserController {
@RequestMapping("j1")
@ResponseBody
public String json1() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
User user = new User("test", 18, "male");
String str = mapper.writeValueAsString(user);
return str;
}
}
使用@ResponseBody,方法返回值不会走视图解析器,会直接返回字符串。在url输入localhost:8080/j1,页面上显示json字符串 {"name": "test", "age": "18", "sex": "male"}。除了使用@ResponseBody,还可以在类上使用@RestController,而不使用@Controller,那么类中的所有方法都会返回字符串
如果出现乱码问题,我们可以在springmvc-serclet.xml文件中配置
<mvc:annotation-driven>
<mvc:message-converters register-defaults="trued">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
如果使用fastjson,使用方法和Jackson类似
10 SpringMVC支持ajax
在SpringMVC的环境下完成ajax功能
- 前端页面使用
$.ajax()
- 后端返回json格式数据
10.1 @ResponseBody
既然要返回json数据,就要先导入json的依赖,比如Jackson的依赖
编写处理请求的后端代码
@Controller
public class AjaxTestController {
@Autowired
private UserService userService;
@ResponseBody
@RequestMapping("/getallajax")
public List<User> ajaxGetAll() {
List<User> users = userService.getAll();
return users;
}
}
在方法上加上@ResponseBody
注解,表示将返回的数据放在响应体中,如果是对象,自动将对象转为json格式
编写前端代码
<a href="${request.getContextPath()}/getallajax">ajax获取全部用户</a>
<div>
</div>
<script type="text/javascript">
$("a:first").click(function() {
$.ajax({
url: "${request.getContextPath()}/getallajax",
type: "GET",
success: function(data) {
$.each(data, function() {
let user = this.id + " " + this.name + " " + this.email;
$("div").append(user </br>);
});
}
});
return false;
});
</script>
10.2 @RequestBody
@RequestBody
注解可以获取一个请求的请求体
首先编写一段前端代码
<form action="${request.getContextPath()}/testrequestbody" method="post">
<input name="username" value="admin">
<input name="password" value="123456">
<input type="submit">
</form>
因为只有POST请求才有请求体,GET请求没有请求体
接下来编写后端代码
@RequestMapping("/testrequestbody")
public String testRequestBody(@RequestBody String body) {
System.out.println("requestbody: " + body);
return "success";
}
这样就能获取到请求体中的数据,这个注解的功能和@RequestParam
注解有一点类似
另外,@RequestBody注解还可以接收json格式的数据并自动封装成JavaBean对象
首先编写前端代码
<a href="${request.getContextPath()}/testrequestbody">ajax发送json数据</a>
<script type="text/javascript">
$("a:first").click(function() {
let user = {id: 1, name: "admin", email: "test@test.com"};
let userStr = JSON.stringify(user);
$.ajax({
url: "${request.getContextPath()}/testrequestbody",
type: "POST",
data: userStr,
contentType: "application/json",
success: function(data) {
console.log(data);
}
});
return false;
});
</script>
后端代码
@RequestMapping("/testrequestbody")
public String testRequestBody(@RequestBody User user) {
System.out.println(user);
return "success";
}
这样就能将接收到json格式的数据自动封装为User对象
11 拦截器
拦截器类似于过滤器Filter,用于对处理器进行预处理和后处理
过滤器和拦截器的区别:拦截器是AOP思想的具体应用
- 过滤器
- servlet规范中的一部分,任何JavaWeb都可以使用
- 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截
- 拦截器
- 拦截器是SpringMVC框架的,只有使用了SpringMVC框架的项目才能使用
- 拦截器只会拦截访问的控制器方法,如果访问的是jsp/html/css/image/js是不会进行拦截的
11.1 自定义拦截器
想要自定义拦截器,只要编写的类实现HandlerInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
//return true: 放行,执行下一个拦截器
//return false: 不放行
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
System.out.println("处理前");
return true;
}
public void postHandle(HttpServletRequest req, HttpServletResponse resp, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("处理后");
}
public void afterCompletion(HttpServletRequest req, HttpServletResponse resp, Object handler, Exception ex) throws Exception {
System.out.println("请理");
}
}
配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<!--/**代表这个请求下的所有请求-->
<mvc:mapping path="/**"/>
<bean class="com.hjc.config.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
只要访问任一控制器,拦截器的三个方法都会执行,首先执行preHandle,再执行控制器方法,接着执行postHandle方法,显示页面,最后执行afterCompletion方法
如果preHandle方法返回false,那么访问控制器时,只会执行preHandle方法,不会执行后续方法
如果在执行控制器方法是出现异常,那么postHandle方法不会执行,但是afterCompletion方法还是会执行
11.2 拦截器实现登陆判断验证
编写LoginController
@Controller
@RequestMapping("/user")
public class LoginController {
@RequestMapping("/main")
public String main() {
return "main";
}
@RequestMapping("/goLogin")
public String goLogin() {
return "login";
}
@RequestMappint("/login")
public String login(HttpSession session, String username, String password) {
//省略判断username, password的代码
session.setAttribute("userLoginInfo", username);
return "main";
}
}
在首页index.jsp中放置两个超链接,分别跳向“/goLogin”和“/main”,方法分别返回登陆页面login.jsp和main.jsp(放置于WEB-INF/jsp/目录下)
编写一个拦截器,使得未登录时无法访问main.jsp
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandler(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
HttpSession session = req.getSession();
if (req.getRequestURI().contains("goLogin")) {
return true;
}
if (req.getRequestURI().contains("login")) {
return true;
}
if (session.getAttribute("userLoginInfo") != null) {
return true;
}
req.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward();
return false;
}
}
配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<!--/**代表这个请求下的所有请求-->
<mvc:mapping path="/user/**"/>
<bean class="com.hjc.config.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
12 国际化
在SpringMVC中实现国际化的功能比较简单,实现流程为,写好国际化资源文件,让Spring的ResourceBundleMessageSource管理国际化资源文件,在页面取值
前端页面上显示的国际化信息是根据浏览器设置的语言来决定的
12.1 基本流程
要实现国际化功能,首先在配置文件目录下定义两个国际化资源文件,比如login_zh_CN.properties和login_en_US.properties来实现登录页面的国际化
login_zh_CN.properties的内容
welcomeinfo=欢迎
username=用户名
password=密码
loginBtn=登陆
login_en_US.properties的内容
welcomeinfo=welcome
username=USERNAME
password=PASSWORD
loginBtn=LOGIN
在xml文件中配置ResourceBundleMessageSource
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="login"></property>
</bean>
最后在前端页面内使用资源文件中的配置项