wy132
wy132
发布于 2024-07-14 / 1 阅读
0

Spring MVC 笔记

M:Model,模型层,指工程中的JavaBean,作用是处理数据

JavaBean分为两类:

  • 一类称为实体类Bean:专门存储业务数据的,如 Student、User 等

  • 一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问。

V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据

C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器

1. @RequestMapping注解

:Spring MVC封装的请求处理拦截器,类似Servlet中的@WebFilter,接收请求

匹配请求,拦截、过滤请求

:需要相关配置

1.1 位置:

  1. 放在方法前:则请求路径为hostlost:8080/testRequestMapping

    public class RequestMappingController {
        //此时请求映射所映射的请求的请求路径为:hostlost:8080/testRequestMapping
        @RequestMapping("/testRequestMapping")
        public String testRequestMapping(){
            return "success";
        }
    }
  2. 放类前:请求路径有变化,需写正确才会接受请求

    @Controller
    @RequestMapping("/test")
    public class RequestMappingController {
        //此时请求映射所映射的请求的请求路径为:/test/testRequestMapping
        @RequestMapping("/testRequestMapping")
        public String testRequestMapping(){
            return "success";
        }
    }

1.2 属性

value属性:

:指定请求的资源路径,可以是数组,

<a th:href="@{/testRequestMapping}">测试@RequestMapping的value属性-->/testRequestMapping</a><br>
@RequestMapping(value = "/testRequestMapping")
public String testRequestMapping(){
    return "success";
}
  • 页面点击链接,发送请求/testRequestMapping,Spring MVC接收到请求,匹配有@RequestMapping注解的value值,使用匹配的方法进行处理

method属性: 指明请求是get还是post才接收,也是数组

@RequestMapping(
        value = {"/testRequestMapping", "/test"},
        method = {RequestMethod.GET, RequestMethod.POST}
)
public String testRequestMapping(){
    return "success";
}

params和headers属性: 指明请求带参数和请求头的参数

1.3 支持 ant 风格的路径

?:表示任意的单个字符

*:表示任意的0个或多个字符

**:表示任意的一层或多层目录

如下,/a3a可被接收,/aea也可以被接受

<a th:href="@{/a3a}">测试@RequestMapping的value属性-->/testRequestMapping</a><br>
@RequestMapping(value = "/a?a")
public String testRequestMapping(){
    return "success";
}

1.4 支持路径中的占位符

:使用{ }标明

:使用@PathVariable注解获取

如下,接收/testRest/1/tom的请求,可获取 1 和 tom

@RequestMapping("/testRest/{id}/{username}")
public String testRest(@PathVariable("id") String id, @PathVariable("username") String username){
    System.out.println("id:"+id+",username:"+username);
    return "success";
}
//最终输出的内容为-->id:1,username:admin

2. 获取请求参数

2.1 Servlet API获取(少用)

类似原生的Servlet方法

@RequestMapping("/testParam")
public String testParam(HttpServletRequest request){    //自动获取请求体对象
    String username = request.getParameter("username"); //使用HttpServletRequest的方法
    String password = request.getParameter("password"); //获取请求体中的信息
    System.out.println("username:"+username+",password:"+password);
    return "success";
}

2.2 控制器直接获取

<a th:href="@{/testParam(username='admin',password=123456)}"></a><br>
@RequestMapping("/testParam")       //名字必须和请求体中的键名一样
public String testParam(String username, String password){  //自动匹配同名参数
    System.out.println("username:"+username+",password:"+password);
    return "success";
}

2.3 注解获取信息

  1. @RequestParam:获取请求参数

  2. @RequestHeader:获取请求头信息

  3. @CookieValue:获取请求体中的Cookie数据

2.4 通过POJO实体类获取

如下例子:表单提交多个信息,定义的实体类User中有id、name、pwd等属性

<form th:action="@{/testpojo}" method="post">
   id、name、pwd、等
    <input type="submit">
</form>

可以用实体类的对象进行数据的接收

@RequestMapping("/testpojo")
public String testPOJO(User user){  //自动接收form提交的数据
    System.out.println(user);
    return "success";
}
//最终结果-->User{id=null, username='张三', password='123', age=23, sex='男', email='123@qq.com'}

3. 域共享数据

3.1 request 域

使用 ModelAndView 向 request 域对象共享数据

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
    /**
     * ModelAndView有Model和View的功能
     * Model主要用于向请求域共享数据
     * View主要用于设置视图,实现页面跳转
     */
    ModelAndView mav = new ModelAndView();
    //向请求域共享数据
    //testScope键,hello,ModelAndView值
    //同域内对象可以通过testScope键获取值,实现共享
    mav.addObject("testScope", "hello,ModelAndView");
    //设置视图,实现页面跳转
    mav.setViewName("success");
    return mav;
}

同域--前端获取数据,使用"${}" 来获取指定键对应的值

<p th:text="${testScope}"></p>

:还有Model、ModelMap、Map方法实现共享

3.2 其他域共享

可以向session、application域的共享数据

4. 视图控制

:指html,页面

4.1 ThymeleafView

:当控制器方法中所设置的视图名称没有任何前缀时,默认使用配置文件中所配置的视图解析器解析来跳转视图

@RequestMapping("/testHello")
public String testHello(){
    return "hello"; //默认解析,跳转到hello.html视图
}

4.2 转发视图

加上forward:前缀,则不会被视图解析器解析,会通过转发方式实现

@RequestMapping("/testForward")
public String testForward(){
    return "forward:/testHello";    //转发跳转到testHello
}

4.3 重定向

加上redirect:前缀

return "redirect:/testHello";

4.4 视图控制器

:当控制器方法内只实现页面跳转时,可以用view-controller标签 配置来控制

ThymeleafView中的例子可以用配置

<!--
    path:设置处理的请求地址
    view-name:设置请求地址所对应的视图名称
-->
<mvc:view-controller path="/testView" view-name="success"></mvc:view-controller>
<!--不添加这句,则视图解析器失效,所以要添加这句-->
<mvc:annotation-driven />
  • view-name的值也按ThymeleafView、转发、重定向的三种规则填,即可以填forward等前缀


5. RESTful API

:提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性

:一种接口设定的规范,思想,非新技术

如下,传统请求方式 getUserById?id=1 带动词,带?传参

操作

传统方式

REST风格

查询操作

getUserById?id=1

user/1-->get请求方式

保存操作

saveUser

user-->post请求方式

删除操作

deleteUser?id=1

user/1-->delete请求方式

更新操作

updateUser

user-->put请求方式

  • REST风格则是规定用 / 代替?,用GET、POST、PUT、DELETE请求方式来告诉后端请求要进行增删查改

5.1 如何发送delete、put请求

浏览器只能发送get、post请求,需要在 web.xml 配置过滤器,将post请求转换为delete、put请求

:在web.xml中注册HiddenHttpMethodFilter

:在web.xml中注册时,必须先注册CharacterEncodingFilter(乱码过滤器),再注册HiddenHttpMethodFilter

请求经过HiddenHttpMethodFilter的条件:

  1. 必须是post请求

  2. 请求中必须指定传输请求参数_method,并指定值为delete或put

<form id="delete_form" method="post">
    <!-- HiddenHttpMethodFilter要求:必须传输_method请求参数,并且值为最终的请求方式 -->
    <input type="hidden" name="_method" value="delete"/>
</form>

5.2 例子

实现以下接口

注意:URL地址一样,但请求方式不同,则@RequestMapping匹配到的方法也不同

功能

URL 地址

请求方式

访问首页√

/

GET

查询全部数据√

/employee

GET

删除√

/employee/2

DELETE

跳转到添加数据页面√

/toAdd

GET

执行保存√

/employee

POST

跳转到更新数据页面√

/employee/2

GET

执行更新√

/employee

PUT

查询全部数据:

前端:

<!--超链接默认是GET请求-->
<a th:href="@{/employee}">访问员工信息</a>    

后端:

@RequestMapping(value = "/employee", method = RequestMethod.GET)
public String getEmployeeList(Model model){/**/}

删除:

<form id="delete_form" method="post">
    <!-- HiddenHttpMethodFilter要求:必须传输_method请求参数,并且值为最终的请求方式 -->
    <input type="hidden" name="_method" value="delete"/>
</form>
@RequestMapping(value = "/employee/{id}", method = RequestMethod.DELETE)
public String deleteEmployee(@PathVariable("id") Integer id){/**/}

执行保存:

<form th:action="@{/employee}" method="post">
    lastName:<input type="text" name="lastName"><br>
    email:<input type="text" name="email"><br>
    gender:<input type="radio" name="gender" value="1">male
    <input type="radio" name="gender" value="0">female<br>
    <input type="submit" value="add"><br>
</form>
@RequestMapping(value = "/employee", method = RequestMethod.POST)
public String addEmployee(Employee employee){}

6. 响应体信息

HttpMessageConverter提供了两个注解和两个类型:@RequestBody,@ResponseBody,RequestEntity,ResponseEntity来处理请求和响应

6.1 @ResponseBody响应体

作用:将方法的返回数据作响应体返回

@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
    return "success";
}

6.2 处理Json数据

  1. 导入jackson的依赖

  2. Spring MVC配置中配置<mvc:annotation-driven />消息转换器,来将java对象转换Json数据

  3. 使用@ResponseBody注解

    @RequestMapping("/testResponseUser")
    @ResponseBody
    //返回User对象,消息转换器会将User转换为Json数据
    public User testResponseUser(){
        return new User(1001,"admin","123456",23,"男");
    }
  4. 浏览器的页面中展示的结果:

    {"id":1001,"username":"admin","password":"123456","age":23,"sex":"男"}

6.3 Spring MVC 接收 Ajax

后端接口一样,代码类似

@RequestMapping("/testAjax")
@ResponseBody
public String testAjax(String username, String password){   //获取Ajax的参数
    System.out.println("username:"+username+",password:"+password);
    return "hello,ajax";
}

6.4 其他同类注解

@RequestBody:获取请求体,放在形参中

@RequestEntity:封装请求报文,可以获取请求头、请求体等信息

@RestController:复合注解,放在类上,相当于给类添加@Controller、@ResponseBody注解

@ResponseEntity:控制返回值类型,即响应报文,比ResponseBody用处更广,更常用


7. 文件的上传和下载

7.1 下载

只有/static/img/1.jpgattachment;filename=1.jpg里面的1.jpg可以改,其他都可以固定照抄

@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
    //获取ServletContext对象
    ServletContext servletContext = session.getServletContext();
    //获取服务器中文件的真实路径
    String realPath = servletContext.getRealPath("/static/img/1.jpg");
    //创建输入流
    InputStream is = new FileInputStream(realPath);
    //创建字节数组
    byte[] bytes = new byte[is.available()];
    //将流读到字节数组中
    is.read(bytes);
    //创建HttpHeaders对象设置响应头信息
    MultiValueMap<String, String> headers = new HttpHeaders();
    //设置要下载方式以及下载文件的名字
    headers.add("Content-Disposition", "attachment;filename=1.jpg");
    //设置响应状态码
    HttpStatus statusCode = HttpStatus.OK;
    //创建ResponseEntity对象
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
    //关闭输入流
    is.close();
    return responseEntity;
}

7.2 上传

@RequestMapping("/testUp")
public String testUp(MultipartFile photo, HttpSession session) throws IOException {
    //获取上传的文件的文件名
    String fileName = photo.getOriginalFilename();
    //处理文件重名问题
    String hzName = fileName.substring(fileName.lastIndexOf("."));
    fileName = UUID.randomUUID().toString() + hzName;
    //获取服务器中photo目录的路径
    ServletContext servletContext = session.getServletContext();
    String photoPath = servletContext.getRealPath("photo");
    File file = new File(photoPath);
    //如果文件夹不存在,则创建文件夹
    if(!file.exists()){
        file.mkdir();
    }
    //File.separator是 / 
    String finalPath = photoPath + File.separator + fileName;
    //实现上传功能
    photo.transferTo(new File(finalPath));
    return "success";
}

8. 拦截器

:类似过滤器,执行于控制器方法前

  1. 在bean中配置拦截器,并设定拦截范围

    <mvc:interceptor>
        <!--拦截范围-->
        <mvc:mapping path="/**"/>
        <!--不拦截的范围-->
        <mvc:exclude-mapping path="/testRequestEntity"/>
        <!--指定拦截器类-->
        <ref bean="firstInterceptor"></ref>
    </mvc:interceptor>
  2. 创建拦截器类

    @Component
    public class firstInterceptor implements HandlerInterceptor {}

    重写preHandle、postHandle、afterComplation三个方法

    • preHandle:控制器方法执行之前执行preHandle,返回true为放行,执行控制器方法,反之不放行,不执行控制器方法

    • postHandle:控制器方法执行之后执行,不放行,则控制器方法不执行,即该方法不执行

    • afterComplation:渲染视图完毕之后执行

多个拦截器执行顺序:

preHandle按顺序执行,postHandle、afterComplation倒序执行

如5个拦截器,第1,2个放行,第3个不放行,则执行顺序如下:

1 preHandle true

2 preHandle true

3 preHandle false

2 afterComplation

1 afterComplation

  • 有一个preHandle返回false则所有postHandle都不执行,因为控制器方法不执行

  • afterComplation则有返回true的就执行

9. 异常处理

:可以用来当发生异常时,跳转到自定义页面,并在页面输出异常的信息

基于配置的异常处理:

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <!--
                 exceptionMappings固定注入的属性
                key填异常类
                后面的error表示跳转到自定义的error.html页面
            -->
            <prop key="java.lang.ArithmeticException">error</prop>
        </props>
    </property>
    <!--
        exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享
    -->
    <property name="exceptionAttribute" value="ex"></property>
</bean>

基于注解方式的异常处理:

10.注解配置MVC

使用配置类和注解代替web.xml和SpringMVC配置文件的功能

:类似创建一个类来配置Spring

11. SpringMVC执行流程

在这里插入图片描述

  • DispatcherServlet:前端控制器,不需要工程师开发,由框架提供

作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求

  • HandlerMapping:处理器映射器,不需要工程师开发,由框架提供

作用:根据请求的url、method等信息查找Handler,即控制器方法

  • Handler:处理器,需要工程师开发

作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理

  • HandlerAdapter:处理器适配器,不需要工程师开发,由框架提供

作用:通过HandlerAdapter对处理器(控制器方法)进行执行

  • ViewResolver:视图解析器,不需要工程师开发,由框架提供

作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView

  • View:视图

作用:将模型数据通过页面展示给用户

流程:

1、请求首先进入DispatcherServlet, 由DispatcherServletHandlerMappings中匹配对应的Handler,此时只是获取到了对应的Handler,然后拿着这个Handler去寻找对应的适配器,即:HandlerAdapter

2、拿到对应HandlerAdapter时,这时候开始调用对应的Handler方法,即执行我们的Controller来处理业务逻辑了, 执行完成之后返回一个ModeAndView

3、HandlerAdapter执行完之后,返回一个ModeAndView,把它交给我们的视图解析器ViewResolver,通过视图名称查找出对应的视图然后返回;

4、最后,渲染视图 返回渲染后的视图。