通过注解实现SpringMVC、Controller使用、RestFul风格使用

718 阅读7分钟

我是跟着b站狂神说的SpringMVC来进行学习的。这是我的笔记。www.bilibili.com/video/BV1aE…

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

通过注解实现 SpringMVC

第一步:导入依赖

第二步:将项目依赖存入 lib 目录下

第三步:配置 DispatchServlet

配置 web.xml 的操作。

前三步都是相同的。


第四步:创建 springmvc-servlet.xml

这是标配模板,直接在 resources 下直接创建复制粘贴就可以了。

  • <context:component-scan base-package="cn.hyz.controller"/>:需要填入自己对应的 controller 的包路径。

  • <mvc:default-servlet-handler />:过滤静态资源 。例如:HTML . JS . CSS . 图片 , 视频 .....

  • <mvc:annotation-driven />:表示开启了处理映射器和处理适配器。通过 url 知道找到对应的 controller 的类。

  • **视图解析器:**必须存在,在 controller 返回的页面需要由解析识别。加上前缀后缀。需要在视图解析器里指定你的视图文件存放的统一位置。放在 WEB-INF 包下可以保证我们的 视图资源的安全。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
    <context:component-scan base-package="cn.hyz.controller"/>
    <!-- 让Spring MVC不处理静态资源 -->
    <mvc:default-servlet-handler />
    <!--
    支持mvc注解驱动
        在spring中一般采用@RequestMapping注解来完成映射关系
        要想使@RequestMapping注解生效
        必须向上下文中注册DefaultAnnotationHandlerMapping
        和一个AnnotationMethodHandlerAdapter实例
        这两个实例分别在类级别和方法级别处理。
        而annotation-driven配置帮助我们自动完成上述两个实例的注入。
     -->
    <mvc:annotation-driven />

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>

</beans>

第五步:创建 Controller

package cn.hyz.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author workplace
 * @date 2022/4/10 14:27
 */

@Controller
public class HelloController {
    /*
    * @Controller 代表这个类被 spring 接管
    *       这个被注解的类中的所有方法,如果返回值是 String 且有具体页面可跳转,就会被视图解析器
    * 
    * @RequestMapping("/hello") 表示在 url 中通过 /hello 识别到这个方法,返回对应的数据和视图。
    *       @RequestMapping("/test") 一般都是写在方法上。这样子如果一个类有太多方法也不会不知道 url 。
    * 
    * model.addAttribute 就是处理数据
    * 
    * return "Hello" 的就是 jsp 的名字
    * */

    @RequestMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("msg", "你好程序员");
        // 会被视图解析处理
        return "Hello";
    }
}
  • @Controller:代表这个类被 spring 接管了。在这个类中的所有方法,如果返回值是 String 且有具体页面可跳转,就会被视图解析器。从而达到页面跳转的效果
  • @RequestMapping("/value"):代表这个方法是通过 url 中的 /value 来识别。从而被调用。@RequestMapping("/value") 一般都是写在方法上。防止一个类有太多方法也不会不知道 url 。
  • 方法里返回的 String 类型字符串:会被视图解析处理,从而跳转到对应的页面。

第六步:创建对应的 JSP 页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${msg}
</body>
</html>

Controller 的使用

控制器 Controller 的使用

  • 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现。
  • 控制器负责解析用户的请求并将其转换为一个模型。
  • 在Spring MVC中一个控制器类可以包含多个方法
  • 在Spring MVC中,对于Controller的配置方式有很多种

我们可以通过类继承 Controller 来获取 控制器Controller,通过重写 控制器Controller 的方法来处理请求且返回一个模型与视图对象。

package cn.hyz.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author workplace
 * @date 2022/4/10 15:26
 */
public class ModelController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView modelAndView = new ModelAndView();

        modelAndView.addObject("msg", "你好程序员");
        modelAndView.setViewName("test");

        return modelAndView;
    }
}

缺点:这是一个比较旧得方法,一个类只能写一个方法。如果需要得方法多了,代码量将大大增加。


@Controller 注解

package cn.hyz.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author workplace
 * @date 2022/4/10 15:37
 */
@Controller
public class ModelController2 {
    @RequestMapping("/test2")
    public String test2(Model model) {
        model.addAttribute("msg", "test2");

        return "test";
    }

    @RequestMapping("/test3")
    public String test3(Model model) {
        model.addAttribute("msg", "test3");

        return "test";
    }
}
  • 优点:可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。

@RequestMapping 使用

@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。

就是 @RequestMapping 匹配 url,从而对应的 url 可以匹配到对应的方法。

  • @RequestMapping 在方法上

    @RequestMapping("/test3")
        public String test3(Model model) {
            model.addAttribute("msg", "test3");
    
            return "test";
        }
    

    当 url = localhost:8080/test3 便会调用这个方法。

  • @RequestMapping 在类和方法上

    package cn.hyz.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @author workplace
     * @date 2022/4/10 16:14
     */
    @Controller
    @RequestMapping("/c3")
    public class ModelController3 {
    
        /*
        * @RequestMapping("/test") 一般都是写在方法上。这样子如果一个类有太多方法也不会不知道 url 。
        * */
    
        @RequestMapping("/test")
        public String test3(Model model) {
            model.addAttribute("msg", "调用了 test3 方法");
            return "test";
        }
    }
    

    当 url = localhost:8080/c2/test 便会调用这个方法。

@RequestMapping("/test") 一般都是写在方法上。这样子如果一个类有太多方法也不会不知道 url 。

RestFul 风格

概念

Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

接下来分别介绍原始写法和三种 RestFul 风格的写法


方法一:原来的传参风格

@RequestMapping("/add1")
public String test1(int a, int b, Model model) {
    int res = a + b;
    model.addAttribute("msg", "结果为:" + res);
    return "test";
}

这个方法在前端显示的 url 是 http://localhost:8080/add1?a=1&b=2。这样子可能会把数据库或后台的资源泄露,这种写法不太安全。


方法二:RestRul 风格

	@RequestMapping("/add2/{a}/{b}")
    public String test2(@PathVariable int a, @PathVariable int b, Model model) {
        int res = a + b;
        model.addAttribute("msg", "结果为:" + res);
        return "test";
    }

我们对方法的参数前添加 @PathVariable 注解,并且在 @RequestMapping 里定义了 url 格式,通过 ‘{’ ‘}’ 包裹着参数。

这个方法在前端显示的 url 是 http://localhost:8080/add2/1/2。可以看到参数没有参数名,而且使用 ‘/’ 断开。


方法三:改进后的 RestFul 风格

    @RequestMapping(name = "/add3/{a}/{b}", method = RequestMethod.GET)
    public String test3(@PathVariable int a, @PathVariable int b, Model model) {
        int res = a + b;
        model.addAttribute("msg", "结果为:" + res);
        return "test";
    }
  • 改进后的 RestFul 风格:name = "/add/{a}/{b}",method = RequestMethod.GET。

    可以通过 method 参数来指定请求类型:GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;

    这个方法在前端显示的 url 是 http://localhost:8080/add2/1/2

  • 如果使用方法三,在提交不同类型的参数进行操作,明明出入的参数类型符合要求却显示 500 错误。可以把 name="url" 改为 value = "url" 或 path = "url"


方法四:最常用的 RestFul

@GetMapping("/add4/{a}/{b}")
public String test4(@PathVariable int a, @PathVariable int b, Model model) {
    int res = a + b;
    model.addAttribute("msg", "结果为:" + res);
    return "test";
}
  • 最后我们可以通过 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping 来指定请求类型

总结

  • RestFul 是一种风格。
  • 通过对要传递的参数前添加 @PathVariable 并且在 @@RequestMapping / @请求类型Mapping 中规定 url 格式,就可以做到 RestFul 风格。
  • RestFul 风格的好处有
    • 简洁,让路径更加简洁
    • 安全,不会通过 url 来暴露信息。
    • 高效,支持缓存
  • 如果在使用过程中出现 405 错误可能是请求方式出现问题,可以通过检查路径的格式是否符合请求类型。
package cn.hyz.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * @author workplace
 * @date 2022/4/10 16:27
 */

@Controller
public class RestFulController {
    /*
     * 方法一:
     *  原来的传参风格:http://localhost:8080/add1?a=1&b=2
     *
     * 方法二:
     * RestRul 风格:http://localhost:8080/add2/1/2
     *
     * 方法三:
     * 改进后的 RestFul 风格:name = "/add/{a}/{b}",method = RequestMethod.GET。
     *  可以通过 method 参数来指定请求类型:
     *      GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
     *      http://localhost:8080/add3/1/2
     *
     * 方法四:
     * 最后我们可以通过 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping
     *  来指定请求类型:http://localhost:8080/add3/1/2
     *
     * 使用 RestFul 风格可以达到相同的地址实现不同的功能。
     *
     * 所有的 RestFul 风格都需要在调用的方法里除了 Method 参数之外的所有参数前都添加 @PathVariable 这个注解。
     * 并且要在 url 中映射对应的参数
     *
     * 不同的请求类型可以通过 method 或不同类型的注解来完成。、
     *
     * 如果使用方法三,想要提交不同类型的参数进行操作却显示 500 错误。
     * 可以把 name="url" 改为 value = "url" 或 path = "url"
     *
     * 出现 405 错误可能是请求方式出现问题
     *
     * 好处:
     *  简介,让路径更加简洁
     *  安全,不会通过 url 来暴露信息。
     *  高效,支持缓存
     *
     * */

    @RequestMapping("/add1")
    public String test1(int a, int b, Model model) {
        int res = a + b;
        model.addAttribute("msg", "结果为:" + res);
        return "test";
    }

    @RequestMapping("/add2/{a}/{b}")
    public String test2(@PathVariable int a, @PathVariable int b, Model model) {
        int res = a + b;
        model.addAttribute("msg", "结果为:" + res);
        return "test";
    }

    @RequestMapping(value = "/add3/{a}/{b}", method = RequestMethod.GET)
    public String test3(@PathVariable int a, @PathVariable int b, Model model) {
        int res = a + b;
        model.addAttribute("msg", "结果为:" + res);
        return "test";
    }

    @GetMapping("/add4/{a}/{b}")
    public String test4(@PathVariable int a, @PathVariable int b, Model model) {
        int res = a + b;
        model.addAttribute("msg", "结果为:" + res);
        return "test";
    }
}