Loading

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配置开发

  1. 导入SpringMVC的依赖

  2. 在WEB-INF/jsp下创建hello.jsp

<body>
    ${msg}
</body>
  1. 配置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>
  1. 配置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>
  1. 创建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;
    }
}
  1. 因为使用了BeanNameUrlHandlerMapping处理器映射器,所以要在Spring中注册bean
<bean id="/hello" class="com.hjc.controller.HelloController"/>
  1. 配置Tomcat,运行,在url输入localhost:8080/hello

在实际开发中,一般不会写这么多配置文件,而是使用注解配置

2.2 注解配置开发

  1. 导入SpringMVC的依赖
  2. 在WEB-INF/jsp下创建hello.jsp
<body>
    ${msg}
</body>
  1. 配置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>
  1. 配置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>
  1. 创建HelloController
@Controller
@RequestMapping("/hello")
public class HelloController {
    @RequestMapping("/test")
    public String hello(Model model) {
        model.addAttribute("msg", "HelloSpringMVC");
        return "hello";	//返回结果会被视图解析器处理
    }
}
  1. 配置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

  1. value属性

这个注解可以写在类名上或者方法名上,在类名上的注解和方法名上的注解是一种上下级的关系,value属性内部写访问的域名

  • 类定义处:提供初步的请求映射信息,相对于WEB应用的根目录
  • 方法处:提供进一步的细分映射信息,相对于类定义处的URL。若类定义处未标注@RequestMapping,则方法处标记的URL相对于WEB应用的根目录
  1. method属性

@RequestMapping内的method属性可以限定请求方式,表示只接收这种类型的请求,默认是全部接受

  1. params属性

@RequestMapping内的params属性可以规定请求参数,params属性接收一个数组,用大括号表示,支持简单的表达式

  • param1:表示请求必须包含为名param1的请求参数,如 params=
  • !param1:表示请求不能包含名为param1的请求参数,如 params=
  • param1 = value1:表示请求必须包含名为param1的请求参数,且其值必须为value1,如 params=
  • param1 != value1:表示请求如果包含名为param1的请求参数,其值不能为value1,或者请求不包含为名param1的请求参数,如 params=
  1. headers属性

@RequestMapping内的headers属性可以规定请求头,写法同params属性

headers是由HTTP协议规定的,headers中可以规定只允许某个浏览器访问等信息

  1. consumers属性

consumers属性可以规定只接受内容类型是哪种的请求,规定请求头中的Content-Type

  1. 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风格的支持

  1. 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>
  1. 发起请求:创建一个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 数据从前端到控制器

控制器要接收前端的数据进行处理,那么有这么几种情况

  1. 提交的域名称和处理方法的参数名一致

url:localhost:8080/hello?name=test

处理方法:直接给方法入参上写一个和请求参数名相同的变量,这个变量就来接受请求参数的值,请求中带了参数,这个变量就有值,没带参数,变量为null

@RequestMapping("/hello")
public String hello(String name) {
    System.out.println(name);
    return "hello";
}
  1. 提交的域名称和处理方法的参数不一致

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类似,包含的属性也相同
  1. 提交的是一个对象(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

  1. 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,除了此方法外还有多种方法

  1. 在方法处传入MapModel或者ModelMap,在这些参数中保存的所有数据都会放在请求域中,可以在前端页面获取
    这三者的关系:Map、Model和ModelMap在这个过程中最终都是BindingAwareModelMap在工作,也就是说BindingAwareModelMap中保存的数据都会被放在请求域中
    Map和Model都是接口,而ModelMap是一个继承了LinkedHashMap的类,也就是实现了Map,而BindingAwareModelMap最终是实现了Model接口,继承了ModelMap类

  2. 方法的返回值可以使用ModelAndView类型,在方法中创建一个ModelAndView的对象,有参构造器传入的参数为视图名,也就是要跳转的页面名字,接着就可以使用这个对象保存数据,放在请求域中

  3. 使用注解@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 数据校验

只做前端的数据校验是不安全的,重要的数据一定要加上后端校验

  1. 可以将接收到的数据取出进行校验,如果校验失败就返回前端重新填写数据
  2. SpringMVC可以利用JSR303进行数据校验

JSR303是Java为Bean数据合法性校验提供的标准框架,通过在Bean属性上标注注解来指定校验规则,并通过标准的验证接口对Bean进行验证

Hibernate Validator是JSR303的一个参考实现

使用方法

  1. 导入依赖包
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.9.Final</version>
</dependency>
  1. 在JavaBean的属性上加上相应的注解
public class User {
    private Integer id;

    @NotEmpty
    @Length(max=20)
    private String name;

    @Email
    private String email;

    @Past
    private Date birth;
}
  1. 在SpringMVC封装对象的时候告知这个JavaBean需要校验,在相应的controller方法参数前加上@Valid注解
@RequestMapping(value="/user", method=RequestMethod.POST)
public String addUser(@Valid User user) {
    //...
}
  1. 在方法参数中在添加BindingResult来封装校验结果
@RequestMapping(value="/user", method=RequestMethod.POST)
public String addUser(@Valid User user, BindingResult result) {
    //...
}
  1. 根据不同的校验结果在方法中做出不同的响应
@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

  1. 导入Jackson的包
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.0</version>
</dependency>
  1. 配置web.xml(DispatcherSerlvet、CharacterEncodingFilter)
  2. 配置springmvc-servlet.xml(自动扫描包,视图解析器)
  3. 编写User实体类
public class User {
    private String name;
    private int age;
    private String sex;
    
    //省略get/set方法和构造函数
}
  1. 编写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>

最后在前端页面内使用资源文件中的配置项

posted @ 2020-07-19 00:41  Kinopio  阅读(174)  评论(0编辑  收藏  举报