14_SpringMVC

文章目录

MVC

  • MVC设计模式的任务是将包含业务数据的模块与显示模块的视图解耦
  • SpringMVC是在Spring框架的基础上做的

SpringMVC与JavaEE对比

  • SpringMVC是通过一个Servlet(DispatcherServlet)来接收全部请求,然后分发到不同的方法上

SpringMVC

SpringMVC的核心流程


SpringMVC入门案例

  1. 导入依赖
xml 复制代码
<dependencies>
    <!--spring的核心包 + spring-web 、 spring-webmvc-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.15.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
  1. 启动项配置
    • AACDSI → 抽象类 AbstractAnnotationConfigDispatcherServletInitializer
      • 类似于SpringMVC的启动类
      • onStartup方法,里面做了两次ac = new AnnotationConfigWebApplicationContext
    • 抽象类的特点是:如果里面有抽象方法我们需要是实现其抽象方法
    • 在抽象类的子类中去写这两个抽象方法的实现 → 提供自定义的配置类
    • 示意图如下:

eg:

  • config目录下
    • ApplicationInitialization.java
java 复制代码
// AACDSI == AbstractAnnotationConfigDispatcherServletInitializer

/**
 * 启动类的配置
 */
public class ApplicationInitialization
        extends AbstractAnnotationConfigDispatcherServletInitializer {

    /**
     * AnnotationConfigWebApplicationContext 对比 ApplicationContext 多了对web应用的支持
     *
     * 创建ApplicationContext的时候提供配置类
     * ApplicationContext ac1 = new AnnotationConfigWebApplicationContext(clazz);
     *
     * 创建ApplicationContext的时候提供配置类
     * ApplicationContext ac2 = new AnnotationConfigWebApplicationContext(clazz);
     *
     * 应用程序中有两个ApplicationContext?
     * 两个ac之间存在着包含关系
     *
     */

    /**
     * 内部的ApplicationContext的配置类
     * @return
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{RootConfiguration.class};
    }

    /**
     * 外部的ApplicationContext的配置类
     * @return
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfiguration.class};
    }

    /**
     * 配置DispatcherServlet的ServletMapping = /
     * @return
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

}
  • RootConfiguration.java
java 复制代码
/**
 * 在容器中排除掉controller组件
 */

/**
 * 这里可以整合Mybatis、增加事务等
 */
@Configuration
@ComponentScan(value = "com.coo1heisenberg.demo1",
        // excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class))
        // excludeFilters = @ComponentScan.Filter(value = Controller.class))
        excludeFilters = @ComponentScan.Filter(value =
                {Controller.class, EnableWebMvc.class}))
public class RootConfiguration {
}
  • WebConfiguration.java
java 复制代码
// @Configuration

@EnableWebMvc // 功能有:@Configuration + 使用SpringMVC的相关配置
@ComponentScan("com.coo1heisenberg.demo1.controller")
// WebMvcConfigurer接口:后面还会做一些和SpringMVC相关的配置 
// 需要实现接口 WebMvcConfigurer,如果实现接口里的方法就是提供一些额外的配置信息
public class WebConfiguration implements WebMvcConfigurer {
}
  1. SpringMVC的使用
    • Controller目录下

eg:

java 复制代码
@Controller
public class UserController {
    public UserController() {
        System.out.println("UserController 组件的初始化");
    }

    @SneakyThrows
    @RequestMapping("/hello")
    public void hello(HttpServletRequest request, HttpServletResponse response) {
        BaseRespVo vo = BaseRespVo.ok("hello world");
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonStr = objectMapper.writeValueAsString(vo);
        response.getWriter().println(jsonStr);
    }

    // Handler -> HandlerMethod -> Handler方法
    @RequestMapping("hello2")
    @ResponseBody // SpringMVC帮我们使用Jackson,把我们的返回值转换成JSON字符串进行响应
    public BaseRespVo hello2() {
        return BaseRespVo.ok("hello world2");
    }
}

@RequestMapping注解的使用

  • 通过@RequestMapping注解的不同属性,实现不同功能上的限定
    • 请求URL限定
    • 窄化请求
      • 窄化请求 :将@RequestMapping写在类上
      • 该Controller组件中的方法映射的URL:类上的@RequestMapping的value属性值 + 方法上的@RequestMapping的value属性值
    • 请求方法限定
    • 请求参数限定
    • 请求头限定
  1. 注解的定义
java 复制代码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String name() default ""; // 没啥用

    @AliasFor("path")
    String[] value() default {};
    @AliasFor("value")
    String[] path() default {}; // value属性:映射URL是什么,可以映射多个值,还可以写通配符
    /**
    eg:@RequestMapping("hello/*", "hello*");
    */

    RequestMethod[] method() default {}; // 请求方法限定 GET/POST

    String[] params() default {}; // 请求参数限定 ?后面的

    String[] headers() default {}; // 请求体限定 携带的token等等

    String[] consumes() default {}; // 和请求头有关,对应的content-type
    // content-type指请求体里正文的类型

    String[] produces() default {}; // 和请求头有关,对应的accept
    // accept指服务端响应报文响应体里正文的格式
}
  1. value属性的使用
java 复制代码
/**
 * value属性的使用
 */
@Controller
@RequestMapping("user")
public class UserController {
    /**
     * localhost:8080/use/hello/111
     * localhost:8080/use/helloXXX
     * @return
     */
    @RequestMapping(value = {"hello/*", "hello*"})
    @ResponseBody
    public BaseRespVo hello() {
        return BaseRespVo.ok("hello aaaaa");
    }
}
  1. 请求方法限定
    • 引申注解:@GetMapping@PostMapping
      • 这两个注解就是限定了请求方法的@RequestMapping
java 复制代码
@RequestMapping(
    method = {RequestMethod.GET}
)
public @interface GetMapping {}


@RequestMapping(
    method = {RequestMethod.POST}
)
public @interface PostMapping {}

eg:

java 复制代码
/**
 * method属性:请求方法限定
 */
@Controller
@RequestMapping("method")
public class MethodLimitController {

    /**
     * localhost:8080/demo2/method/get
     *
     * @return
     */

    @ResponseBody
    // @RequestMapping(value = "get", method = RequestMethod.GET)
    @GetMapping("get") // 与上面等同
    public BaseRespVo methodGet() {
        return BaseRespVo.ok();
    }

    @ResponseBody
    //@RequestMapping(value = "post", method = RequestMethod.POST)
    @PostMapping("post") // 与上面等同
    public BaseRespVo methodPost() {
        return BaseRespVo.ok();
    }

    /**
     * 多个值之间的关系是or的关系
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "getorpost", method = {RequestMethod.GET, RequestMethod.POST})
    public BaseRespVo methodGetOrPost() {
        return BaseRespVo.ok();
    }
}
  1. 请求参数限定
    • 可以写多个值,写多个值的情况,多个值之间的关系是and(或着说是:且)
    • 错误码:400 → 是请求参数封装有问题

eg:

java 复制代码
@Controller
@RequestMapping("parameter")
public class ParameterLimitController {

    /**
     * localhost:8080/demo2/parameter/limit?username=zs&password=123456
     * @return
     */
    @ResponseBody
    // params = {"username","password"} 含义:既要携带username这个请求参数,也要携带password
    @RequestMapping(value = "limit", params = {"username", "password"}) // params里面是且的关系
    public BaseRespVo limit() {
        return BaseRespVo.ok();
    }

}
  1. 请求头限定
    • 是字符串数组,多个值之间的关系也是and(或者说是且)

eg:

java 复制代码
@Controller
@RequestMapping("header")
public class HeaderLimitController {

    /**
     * localhost:8080/demo2/header/limit
     * @return
     */
    @ResponseBody
    // headers也是且的关系
    @RequestMapping(value = "limit", headers = {"x-coo1heisenberg-token", "language"})
    public BaseRespVo limit() {
        return BaseRespVo.ok();
    }
}
  1. Content-Type请求头值限定consumes属性
  • 限定的是Content-Type这个请求头的值 → 这个请求头的含义:请求携带的 正文的类型(请求体)
    • 比如一个jpg文件:image/jpeg
    • 比如文本:text/html
    • 比如json:application/json
  • 语法的格式:xxx/xxx
  1. Accept请求头值限定produces属性
  • 限定的是Accept这个请求头的值 → 这个请求的含义 → 客户端希望接收到的服务端响应的正文类型
  • 语法的格式:xxx/xxx

eg:

java 复制代码
@Controller
@RequestMapping("header")
public class HeaderLimitController {

    /**
     * localhost:8080/demo2/header/consumes
     * localhost:8080/demo2/header/produces
     *
     * @return
     */

    @ResponseBody
    @RequestMapping(value = "consumes", consumes = "abc/def")
    // 限定content-type:abc/def
    public BaseRespVo consumes() {
        return BaseRespVo.ok();
    }

    @ResponseBody
    @RequestMapping(value = "produces", produces = "application/json")
    // 限定accept:application/json
    public BaseRespVo produces() {
        return BaseRespVo.ok();
    }
}
  • 解决中文乱码问题
    • JavaEE阶段解决响应中文乱码问题:响应头中Content-Type → 响应体的内容的字符集
    • SpringMVC阶段解决中文乱码问题:请求头中的Accept → 响应体的内容的字符集

eg:

java 复制代码
@Controller
@RequestMapping("chinese")
public class ChineseResolveController {
    @RequestMapping(value = "resolve", produces = "application/json;charset=utf-8")
    @ResponseBody
    public String chinese() {
        return "你好中文";
    }
}

Handler方法的返回值

DispatcherServlet获取 → 判断类型

  • 如果是ModelAndView → 转发到jsp页面,在jsp页面上渲染model提供的数据 → 已经不是主流了
  • 如果是Json 需要的形式 → response.getWriter.write(jsonStr)

  1. ModelAndView
    • ModelAndView主要是为单体应用服务的,单体应用的话,前端和后端都整合在一个应用程序中
    • ModelAndView主要是给访问前端的视图和数据的 ,当服务器的这个方法返回了一个ModelAndView的话,会访问到前端的内容
      • Tomcat对jsp处理本身就是特殊的处理 → jsp文件会被编译Servlet
    • 返回值写为了ModelAndView,在方法上并没有增加@ResponseBody

eg:

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>hello ${name}</h1>
</body>
</html>
java 复制代码
@Controller
@RequestMapping("mav")
public class ModelAndViewController {

    /**
     * localhost:8080/demo3/mav/hello?name=zs
     * localhost:8080/demo3/mav/hello2?name=zs
     */

    /**
     * 访问到hello.jsp,里面显示的也是hello xxx
     * @return
     */
//    @SneakyThrows
//    @RequestMapping("hello")
//    public ModelAndView hello(HttpServletRequest request, HttpServletResponse response) {
//        request.setAttribute("name", request.getParameter("name"));

    /**
     * 请求转发:
     * 将请求从一个Servlet转发到另一个Servlet或JSP页面,但URL不会改变。
     * 在转发过程中,请求对象(HttpServletRequest)和响应对象(HttpServletResponse)在Servlet之间共享
     * @param request
     * @return
     */
//        request.getRequestDispatcher("/WEB-INF/hello.jsp").forward(request, response);
//
//    }

    @RequestMapping("hello")
    public ModelAndView hello(HttpServletRequest request) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("/WEB-INF/hello.jsp");
        modelAndView.addObject("name", request.getParameter("name"));
        return modelAndView;
    }

    // 如果返回值是视图名,不能加@RequestBody注解
    @RequestMapping("/hello2")
    public String hello2(Model model, HttpServletRequest request) {
        model.addAttribute("name", request.getParameter("name"));
        return "/WEB-INF/hello.jsp"; // ModelAndView中的视图名
        // 等价于:modelAndView.setViewName("/WEB-INF/hello.jsp");
    }
}

  1. Json

    • 准备事务
      • jackson相关依赖(jackson-databind)
      • @EnableWebMvc
      • 方法上(或类上)增加注解@ResponseBody
    • 注意事项:如果要响应的是引用类型的对象,提供构造方法和getter/setter方法
    • @ResponseBody注解:
    java 复制代码
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ResponseBody {
    }
    • 引申注解:@RestController = @Controller + @ResponseBody
  • 有两种方式:
    • String并不建议继续使用了
      • 后续我们并不直接返回String了,因为这样子写比较繁琐,大家还需要处理字符集问题,返回值为String的话,它的字符集默认是iso-8859-1
    • Object你写啥类型都行
      • 直接返回集合或引用类型对象就可以了
      • 直接返回对象,Jackson会自动的帮我们进行转换, 并且也不需要设置字符集,返回值的中文没有乱码

eg:

java 复制代码
@RequestMapping("json")
//@Controller
//@ResponseBody // 意味着所有的方法上都加上了@ResponseBody
@RestController // 复合注解 等同于:@Controller + @ResponseBody
public class JsonController {
    /**
     * 1. 直接响应字符串
     * 2. 直接响应Object对象,然后Jackson会将该对象转换成Json字符串响应出去
     */

    @RequestMapping("hello1")
    // @ResponseBody
    public String hello1() {
        return "hello world";
    }

    @RequestMapping("hello2")
    // @ResponseBody
    public BaseRespVo hello2() {
        return BaseRespVo.ok("hello2");
    }
}

Handler方法的形参

方法的形参:主要做的事情是接收请求参数 以及一些其他的信息

  • key=value形式的请求参数
  • Json形式的请求参数
  • 其他的信息

key=value形式的请求参数

  1. String、基本类型以及对应的包装类
    • 直接接收:请求参数名和方法的形参名一致
    • 可以使用基本类型或其包装类接收的时候,建议使用包装类来接收 → 健壮性

eg:

java 复制代码
/**
 * key = value形式的请求参数的接收
 */
@RequestMapping("user")
@RestController
public class UserController {
    /**
     * localhost:8080/demo4/user/register?username=zs&password=nihaoya&age=30
     */
    @RequestMapping("/register")
    public BaseRespVo register(String username, String password, Integer age) {

        return BaseRespVo.ok();
    }
}
  • vo的封装是开发过程中非常常规的使用

eg:

java 复制代码
@Data
public class BaseRespVo<T> {
    T data;
    String errmsg;
    int errno;

    public static <T> BaseRespVo ok(T data) {
        BaseRespVo baseRespVo = new BaseRespVo();
        baseRespVo.setErrmsg("成功");
        baseRespVo.setData(data);
        return baseRespVo;
    }
    public static <T> BaseRespVo ok() {
        BaseRespVo baseRespVo = new BaseRespVo();
        baseRespVo.setErrmsg("成功");
        return baseRespVo;
    }

    public static <T> BaseRespVo invalidData(String msg) {
        BaseRespVo baseRespVo = new BaseRespVo();
        baseRespVo.setErrno(504);
        baseRespVo.setErrmsg(msg);
        return baseRespVo;
    }
    public static <T> BaseRespVo invalidData() {
        BaseRespVo baseRespVo = new BaseRespVo();
        baseRespVo.setErrno(504);
        baseRespVo.setErrmsg("更新数据已失效");
        return baseRespVo;
    }
    public static <T> BaseRespVo invalidParameter(String msg) {
        BaseRespVo baseRespVo = new BaseRespVo();
        baseRespVo.setErrno(400);
        baseRespVo.setErrmsg(msg);
        return baseRespVo;
    }
    public static <T> BaseRespVo unAuthc() {
        BaseRespVo baseRespVo = new BaseRespVo();
        baseRespVo.setErrno(502);
        baseRespVo.setErrmsg("认证失败");
        return baseRespVo;
    }
    public static <T> BaseRespVo expired() {
        BaseRespVo baseRespVo = new BaseRespVo();
        baseRespVo.setErrno(502);
        baseRespVo.setErrmsg("认证信息过期,请重新登录");
        return baseRespVo;
    }


    public static BaseRespVo badArgument() {

        return fail(401, "参数不对");
    }

    public static BaseRespVo fail(int errno, String errmsg) {
        BaseRespVo baseRespVo = new BaseRespVo();
        baseRespVo.setErrno(errno);
        baseRespVo.setErrmsg(errmsg);
        return baseRespVo;
    }
    public static BaseRespVo fail(String errmsg) {
        BaseRespVo baseRespVo = new BaseRespVo();
        baseRespVo.setErrno(500);
        baseRespVo.setErrmsg(errmsg);
        return baseRespVo;
    }
}
  1. Date形式
    • SpringMVC提供的String → Date的转换器,执行转换的过程中也是需要pattern信息的
      • 第一种是:采用默认的pattern信息 → yyyy/MM/dd
      • 第二种是:使用@DateTimeFormat注解的pattern属性指定
    • 注意:请求参数名和方法的形参名一致

eg:

java 复制代码
@RequestMapping("register3")
public BaseRespVo register3(String username, String password, Integer age, Date birthday) {
    /**
     * 期望使用Date来接收birthday,能否接收取决于SpringMVC是否有提供 String -> Date的Converter
     *
     * → 可以使用Date来接收 → SpringMVC提供了转换器 → 有DateFormat的要求 yyyy/MM/dd
     *
     * eg:localhost:8080/demo4/user/register3?username=ls&password=nihao&age=30&birthday=2022/06/21
     */
    return BaseRespVo.ok();
}


@RequestMapping("register4")
public BaseRespVo register4(String username, String password, Integer age,
                            @DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday) {
    // 也可以提供自定义的DateFormat
    /**
     * eg:localhost:8080/demo4/user/register4?username=ww&password=byebye&age=30&birthday=2022-06-21
     */
    return BaseRespVo.ok();
}
  1. 数组
    • 可以直接来形参中使用数组来接收
    • 前面能够接收到的这个类型的值,也可以使用对应类型的数组来接收

eg:

java 复制代码
@RequestMapping("register5")
public BaseRespVo register5(String username, String password, Integer age,
                            String[] hobbies, Integer[] ids) {
    // 之前得使用这个方法
    // String[] hobbies = request.getParametersValues("hobbies");
    
    /**
     *  localhost:8080/user/register5?username=zs&password=nihaoya&age=30
     *  &hobbies=sing&hobbies=dance&hobbies=rap
     *  &ids=1&ids=2&ids=3
     */
    return BaseRespVo.ok();
}
  1. 文件 MultipartFile
  • 导入commons-iocommons-fileupload依赖
xml 复制代码
<!--引入commons-fileupload时候也会将commons-io引入-->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
  • 先向容器中注册一个组件

eg:

java 复制代码
@EnableWebMvc // 功能有:@Configuration + 使用SpringMVC的相关配置
@ComponentScan("com.coo1heisenberg.demo4.controller")
// WebMvcConfigurer接口:MVC相关的配置要用到这个接口提供的方法和参数
public class WebConfiguration implements WebMvcConfigurer {

    /**
     * 如果要使用SpringMVC的文件接收,接收MultipartFile的话,
     * 需要向容器中注册MultipartResolver组件
     * 处理MultipartFile封装过程中,需要使用这个组件
     * 而这个组件 → ac.getBean(beanName)
     * beanName=multipartResolver → 也就意味着组件名称只能叫multipartResolver
     * 
     * @return
     */
    @Bean
    public CommonsMultipartResolver multipartResolver() {
        return new CommonsMultipartResolver();
    }
}
  • MultipartFile类中的内容
java 复制代码
public interface MultipartFile extends InputStreamSource {
    String getName();

    @Nullable
    String getOriginalFilename();

    @Nullable
    String getContentType();

    boolean isEmpty();

    long getSize();

    byte[] getBytes() throws IOException;

    InputStream getInputStream() throws IOException;

    default Resource getResource() {
        return new MultipartFileResource(this);
    }

    void transferTo(File var1) throws IOException, IllegalStateException;

    default void transferTo(Path dest) throws IOException, IllegalStateException {
        FileCopyUtils.copy(this.getInputStream(), Files.newOutputStream(dest));
    }
}
  • MultipartFile提供的方法
方法名 描述 返回值
getOriginFileName() 获得上传时的文件名 String
getContentType() 获得上传的文件的正文类型,比如上传banner.png,正文类型就是image/png String
getName() 获得是请求参数名(没啥用) String
getSize() 获得文件大小 long
transferTo(File) 提供的参数是File类型的值,File提供的保存位置及文件名,就可以保存这个文件 void
  • 向指定路径上传一个或者多个文件
java 复制代码
@RestController
@RequestMapping("upload")
public class uploadController {

    /**
     * localhost:8080/demo4/upload/file
     *
     * 请求参数名和handler方法的形参名一致
     * @return
     */

    // 上传单个文件
    @SneakyThrows
    @RequestMapping("file")
    public BaseRespVo fileUpload(MultipartFile myfile /*, HttpServletRequest request */) {

        /**

         // 之前对参数信息的封装
        Part part = request.getPart("1.jpg");
        InputStream inputStream = part.getInputStream();
        long size = part.getSize();
        String originFileName = part.getSubmittedFileName();
        String name = part.getName();
        String contentType = part.getContentType();

         */

        InputStream inputStream = myfile.getInputStream();
        long size = myfile.getSize();
        String originFileName = myfile.getOriginalFilename();
        String name = myfile.getName();
        String contentType = myfile.getContentType();

        /*
        FileOutputStream fileOutputStream = new FileOutputStream(new File("D:\\tmp", originFileName));
        byte[] bytes = new byte[1024];
        int length = 0;
        while ((length = inputStream.read(bytes)) != -1) {
            fileOutputStream.write(bytes,0,length);
        }
        inputStream.close();
        fileOutputStream.close();

         */
        // 上面可以优化
        File file = new File("d:/tmp", originFileName);
        myfile.transferTo(file);

        return BaseRespVo.ok();
    }

    // 上传多个文件
    @RequestMapping("files")
    public BaseRespVo uploadFiles(MultipartFile[] myfiles) throws IOException {
        File fatherPath = new File("d:/tmp");
        for (MultipartFile myfile : myfiles) {
            File file = new File(fatherPath, myfile.getOriginalFilename());
            myfile.transferTo(file);
        }
        return BaseRespVo.ok();
    }

}
  1. 使用引用类型接收
    • 使用引用类型的话,将接收到的形参,封装为一个引用类型对象,这个引用类型的对象的成员变量封装的就是这些形参的值
    • 请求参数名和引用类型的成员变量名一致

eg:

java 复制代码
/**
 * localhost:8080/user/register6?username=songge&password=niupi&age=30
 *  &hobbies=sing&hobbies=dance&hobbies=rap&ids=1&ids=2&ids=3
 *  &birthday=2022-06-21
 * @return
 */
@RequestMapping("register6")
public BaseRespVo register6(String username, String password, Integer age,
                            String[] hobbies, Integer[] ids,
                            @DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday) {
    /**
     * User user = new User();
     * user.setUsername(username);
     * user.setPassword(password);
     * user.setAge(age);
     * user.setHobbies(hobbies);
     */
    return BaseRespVo.ok();
  • 将这个形参变为一个实体类的成员变量,形参这里写这个实体类的实例

eg:

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    String username;
    /**
     * request.getParameter("username")
     * user.setUsername(username)
     */
    String password;
    Integer age;
    /**
     * request.getParameter("age")
     * Integer.parseInt(ageStr)
     * user.setAge()
     */
    String[] hobbies;
    Integer[] ids;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    Date birthday;
}
java 复制代码
@RequestMapping("register7")
public BaseRespVo register7 (User user) {
    return BaseRespVo.ok();
}

总结

  • 使用形参直接接收:如果参数比较少,直接使用形参
  • 使用引用类型的对象来接收:参数比较多;在多个请求中都使用了相同的请求参数
  • 可以都用:有些参数就用一次或者用的次数比较少(直接接收),有的参数用了多次(封装为实体类)

Json请求参数

  • 如果说一个请求携带Json请求参数,这个请求的特点是:
    • 请求方法:POST
    • 正文类型Content-Type:application/json
    • 数据是Json字符串
  • Json请求参数仍然是在形参中进行接收,可以使用以下几种类型来接收
    • String
    • 引用类型对象
    • Map
  • 形参前需要增加一个注解@RequestBody

eg:

java 复制代码
    /**
     * String类型
     * {"username":"root","password":"123456","birthday":"2022-07-12"}
     */
//    @RequestMapping("login1")
//    public BaseRespVo login1(HttpServletRequest request) {
//        String jsonStr = request.getReader().readLine();
//        return BaseRespVo.ok();
//    }

    @RequestMapping("login1")
    public BaseRespVo login1(@RequestBody String jsonStr) {
        return BaseRespVo.ok();
    }

    /**
     * 接收到这个json字符串,将字符串转换成map映射
     * @param map
     * @return
     */
    @RequestMapping("login2")
    public BaseRespVo login2(@RequestBody Map map) {
        // jsonStr -> map
        return BaseRespVo.ok();
    }

    @RequestMapping("login3")
    public BaseRespVo login3(@RequestBody UserVo userVo) {
        return BaseRespVo.ok();
    }
    
    @ResponseBody
    @RequestMapping("create")
    public BaseRespVo create(@RequestBody GoodVo goodVo) {
        return goodsService.create(goodVo);
    }
  • @RequestBody@ResponseBody这两个注解都是和Json打交道的时候使用的注解
    • 接收的时候用的是@RequestBody
    • 响应的时候用的是@ResponseBody

RESTful风格接口

  • REST → 是单词的首字母缩写 → 表述性状态传递 → Representational State Transfer
  • 通过请求(报文)能够提供一些对应的信息 ,提供给服务器,也就是通过一些方法来获得对应的信息
  • 其实就是对Request的封装
  1. 当前定义接口的URL,通过URL的不同区分不同的操作:
    • /user/list 查询
    • /user/remove 删除
    • /user/create 新增
    • /user/modify 修改
  • 过去使用RESTful风格接口的时候,构建一个场景,做user的增删改查,请求URL都是/user
    • /user 查询 GET
    • /user 删除 DELETE
    • /user 新增 PUT
    • /user 修改 POST

eg:

java 复制代码
@RestController
@RequestMapping("user")
public class UserController {
    // User的新增
    // @RequestMapping(value = "user", method = RequestMethod.PUT)
    @RequestMapping("create")
    public BaseRespVo create() {
        return BaseRespVo.ok();
    }

    // User的删除
    // @RequestMapping(value = "user", method = RequestMethod.DELETE)
    @RequestMapping("delete")
    public BaseRespVo delete() {
        return BaseRespVo.ok();
    }

    // User的修改
    // @RequestMapping(value = "user", method = RequestMethod.POST)
    @RequestMapping("modify")
    public BaseRespVo modify() {
        return BaseRespVo.ok();
    }

    // User的查询
    // @RequestMapping(value = "user", method = RequestMethod.GET)
    @RequestMapping("list")
    public BaseRespVo list() {
        return BaseRespVo.ok();
    }

}
  1. 我们还可以获得一些其他的信息:
    • 请求URL的信息 → @PathVariable
    • 请求参数信息 → @RequestParam
    • 请求头信息 → @RequestHeader
    • Cookie信息 → @CookieValue
    • Session信息 → @SessionAttribute
  • 之前JavaEE阶段的方法:
java 复制代码
@RestController
public class ArticleController {

    // @RequestMapping("cscscs/article/details/12321241")
    @RequestMapping("*/article/details/*")
    public BaseRespVo articleDetails(HttpServletRequest request) {
        /**
         * localhost:8080/demo5/cscscs/article/details/12321241
         */
        String requestURI = request.getRequestURI();
        String contextPath = request.getContextPath();// /demo5
        String suffix = requestURI.replaceAll(contextPath + "/", "");
        // → cscscs/article/details/12321241
        String username = suffix.substring(0, suffix.indexOf("/"));
        String id = suffix.substring(suffix.lastIndexOf("/") + 1);

        return BaseRespVo.ok();
    }
}
  • @PathVariable注解 → URI
    • @PathVariable → 获得请求URL的一部分值 → 在@RequestMapping的value属性写占位符{}
    • 获得指定占位符位置的值给到所对应的形参 → 形参通过注解接收指定占位符位置的值
    • 通过这个注解,就可以把一部分请求参数写到URL中

eg:

java 复制代码
@RestController
public class ArticleController {
    @RequestMapping("{username}/article/details/{id}")
    public BaseRespVo articleDetails(@PathVariable("username") String username,
                                     @PathVariable("id") Integer id) {
        return BaseRespVo.ok();
    }
}
  • @RequestParam → 请求参数
    • 形参通过这个注解获得指定请求参数,如果使用了这个注解,就一定要携带对应的请求参数

eg:

java 复制代码
/**
 * localhost:8080/demo5/param?username=zs&password=123456
 */
@RequestMapping("param")
public BaseRespVo param(@RequestParam("username") String usernamex,
                        @RequestParam("password") String passwordy) {
    return BaseRespVo.ok();
}
  • @RequestHeader → 请求头
    • 形参通过这个注解获得指定请求头的值

eg:

java 复制代码
   /**
     * localhost:8080/demo5/header
     * <p>
     * 构造请求体 X-token:a1b2c3
     */
//    @RequestMapping("header")
//    public BaseRespVo header(HttpServletRequest request) {
//        request.getHeader("X-token");
//        return BaseRespVo.ok();
//    }
    @RequestMapping("header")
    public BaseRespVo header(@RequestHeader("X-token") String tokenValue) {
        return BaseRespVo.ok();
    }

    /**
     * 也可以使用String[] 来接收,如果使用数组来接收,将字符串根据逗号进行分割,分割为数组
     */
    @RequestMapping("header1")
    public BaseRespVo header1(@RequestHeader("X-token") String tokenValue,
                              @RequestHeader("Accept") String[] acceptArray) {
        return BaseRespVo.ok();
    }
  • @CookieValue → Cookie
    • 形参通过这个注解获得指定Cookie的值,根据key获得value

eg:

java 复制代码
    /**
     * localhost:8080/demo5/cookie
     * 构造请求头 Cookie: username=zs
     */
    /**
     * 之前JavaEE的方法
     * @param request
     * @return
     */
    @RequestMapping("cookie")
    public BaseRespVo cookie(HttpServletRequest request) {
        String value = null;
        Cookie[] cookies = request.getCookies();
        if(cookies != null && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                if("username".equals(cookie.getName())) {
                    value = cookie.getValue();
                }
            }
        }
        return BaseRespVo.ok();
    }
    @RequestMapping("cookie1")
    public BaseRespVo cookie1(@CookieValue("username") String value) {
        return BaseRespVo.ok();
    }
  • 之前JavaEE的方法:
java 复制代码
    /**
     * localhost:8080/demo5/session/put
     */
    /**
     * 向session里存入数据
     * localhost:8080/demo5/session/put?username=zs
     */
    @RequestMapping("session/put")
    public BaseRespVo putSession(HttpSession httpSession, String username) {
        httpSession.setAttribute("username", username);
        return BaseRespVo.ok();
    }

    /**
     * 向session里取出数据
     * localhost:8080/demo5/session/get
     */
    @RequestMapping("session/get")
    public BaseRespVo getSession(HttpSession httpSession) {
        String value = (String) httpSession.getAttribute("username");
        return BaseRespVo.ok();
    }
  • @SessionAttribute → Session
    • 形参通过这个注解获得指定Session的值,根据key获得value
java 复制代码
    /**
     * localhost:8080/demo5/session/put
     */
    /**
     * 向session里存入数据
     * localhost:8080/demo5/session/put?username=zs
     */
    @RequestMapping("session/put")
    public BaseRespVo putSession(HttpSession httpSession, String username) {
        httpSession.setAttribute("username", username);
        return BaseRespVo.ok();
    }

    /**
     * 向session里取出数据
     * localhost:8080/demo5/session/get
     */
    @RequestMapping("session/get")
    public BaseRespVo getSession(@SessionAttribute("username") String username) {
        return BaseRespVo.ok();
    }

静态资源处理

  • JavaEE阶段如果将图片放在webapp目录,它会编译到web资源根目录,图片能访问到
  • 应用程序整合SpringMVC之后,放在web资源根目录的图片访问不到了,原因是:
    • JavaEE阶段,缺省的servlet是default → 这个缺省的servlet做的事情就是根据请求找目录下的静态资源
    • SpringMVC这里,缺省的Servlet是DispatcherServlet → localhost:8080/1.jpg找的是DispatcherServlet而不是default
  • 解决办法:
    • SpringMVC有提供对应的类,提供的是ResourceHandler ,需要我们自己来配置
      • 处理静态资源
      • 配置其映射范围
  • 之前的做法:
java 复制代码
controller目录下:

@RestController
@RequestMapping("pic/storage")
public class PictureStorageController {

    @Autowired
    StorageService storageService;
    @RequestMapping("fetch/{filename}")
    public void fetchImage(HttpServletResponse response,
                           @PathVariable("filename")String filename) {
        storageService.show(response, filename);
    }
}
java 复制代码
service目录下:

public interface StorageService {
    void show(HttpServletResponse response, String filename);

    public StorageData create(MultipartFile file) throws IOException;
}

@Service
public class StorageServiceImpl implements StorageService {
    @Value("d:/tmp")
    String path;

    @Override
    public StorageData create(MultipartFile file) throws IOException {

        // 随机获取一个 UUID,有极小的概率会重复
        String uuid = UUID.randomUUID().toString();
        String savedFileName = uuid + ".jpeg";
        file.transferTo(new File(path, savedFileName));

        // http://localhost:8083/wx/storage/fetch/yq5f2kgrno9q1eoyhz1u.jpeg
        String urlPrefix = "http://localhost:8080/demo6/wx/storage/fetch/";
        String url = urlPrefix + savedFileName;
        StorageData storageData = new StorageData(null, savedFileName, file.getOriginalFilename(),
                file.getContentType(), file.getSize(), url, new Date(), new Date());
        //storageMapper.insert(storageData); // 略,插入到数据库中,并且获得自增的主键封装给id
        return storageData;
    }

    @Override
    @SneakyThrows
    public void show(HttpServletResponse response, String filename) {
        File file = new File(path, filename);
        ServletOutputStream outputStream = response.getOutputStream();
        FileInputStream fileInputStream = new FileInputStream(file);

        int length = 0;
        byte[] bytes = new byte[1024];

        while ((length = fileInputStream.read()) != -1) {
            outputStream.write(bytes, 0, length);
        }
        outputStream.close();
        fileInputStream.close();
    }
}
  • SpringMVC中的方法
    • 注册ResourceHandler使用ResourceHandlerRegistry
      • addResourceHandler配置ResourceHandler的映射范围
      • addResourceLocations告知当前ResourceHandler你的资源文件处于什么位置
      • ** 代表多级任意url
      • location写的时候注意:最后的位置/不要漏掉
    • 建议使用文件路径
      • 过去应用程序打包的时候打包为war包,后面我们使用SpringBoot应用 → 应用程序打包的时候是jar

eg:

java 复制代码
@EnableWebMvc // 功能有:@Configuration + 使用SpringMVC的相关配置
@ComponentScan("com.coo1heisenberg.demo6.controller")
// WebMvcConfigurer接口:MVC相关的配置要用到这个接口提供的方法和参数
public class WebConfiguration implements WebMvcConfigurer {

    /**
     * 如果要使用SpringMVC的文件接收,接收MultipartFile的话,
     * 需要向容器中注册MultipartResolver组件
     * 处理MultipartFile封装过程中,需要使用这个组件
     * 而这个组件 → ac.getBean(beanName)
     * beanName=multipartResolver → 也就意味着组件名称只能叫multipartResolver
     *
     * @return
     */
    @Bean
    public CommonsMultipartResolver multipartResolver() {
        return new CommonsMultipartResolver();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        /**
         * addResourceHandler():配置的是path
         * addResourceLocations():配置的是location
         */
        /**
         * localhost:8080/demo6/pic/1.jpg
         *
         * localhost:8080/demo6/pic/2.jpg
         */
        /**
         * pic/** : 匹配多级任意值
         */
        /**
         * 如果指定文件路径,需要加上 file: 这样的前缀
         *
         * location最后要加上一个 /
         */

        registry.addResourceHandler("/pic/*").addResourceLocations("file:d:/tmp/");

        /**
         * 如果指定类加载路径(target/artifactid-version/WEB-INF/classes),要增加classpath:这样的前
         * 缀
         */
         // 得先进行编译
        registry.addResourceHandler("/pic2/**").addResourceLocations("classpath:/");

        /**
         * 如果指定web资源路径(target/artifactid-version),就不要增加前缀,如果要指定根路径,只写 / 就行
         */
    }
}

Filter

  • Filter和SpringMVC之间的关系:本质上就是Filter和Servlet之间的关系 ,执行SpringMVC的核心DispatcherServletdoDispatch方法之前先去执行的是Filter的doFilter方法
  • SpringMVC给我们提供了一个抽象类OncePerRequestFilter → 实现了Filter接口 → 里面包含了doFilter方法的实现(非抽象方法)
    • OncePerRequestFilter能够保证doFilterInternal只执行一次
    • 相当于之前在JavaEE阶段的doFilter方法
    • 避免因为JavaEE容器的原因导致Filter会执行多次

eg:

  • 配置ApplicationInitialization
java 复制代码
    @Override
    protected Filter[] getServletFilters() {
        // 提供Filter的配置 → 告知web应用,你有哪个或者哪些Filter
        return new Filter[]{new CustomFilter()};
    }
  • 配置filter目录下的CustomFilter
java 复制代码
public class CustomFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) 
                                    throws ServletException, IOException {
        // 相当于之前在JavaEE阶段的doFilter方法
        // 避免因为JavaEE容器的原因导致Filter会执行多次
        // OncePerRequestFilter能够保证doFilterInternal只执行一次
        request.setCharacterEncoding("utf-8");
        System.out.println("执行了CustomFilter");
        filterChain.doFilter(request, response);
    }
}
  • SpringMVC有提供一个处理字符集的Filter → CharacterEncodingFilter也是继承了OncePerRequestFilter

eg:

  • 配置ApplicationInitialization
java 复制代码
@Override
protected Filter[] getServletFilters() {
    // 提供Filter的配置 → 告知web应用,你有哪个或者哪些Filter
    // return new Filter[]{new CustomFilter()};
    CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
    
    // 强制设置utf-8
    characterEncodingFilter.setEncoding("utf-8");
    characterEncodingFilter.setForceEncoding(true);
    return new Filter[]{characterEncodingFilter};
}

HandlerInterceptor

  • Handler的拦截器,在Handler执行之前做的拦截,是SpringMVC提供的拦截器

  • HandlerMapping起作用 → HandlerExecutionChain实例
  • 每次发送请求:都会生成新的HandlerExecutionChain的实例
    • 封装了Handler
    • 封装了多个HandlerInterceptor
java 复制代码
public class HandlerExecutionChain {
  // Handler
  private final Object handler;
  
  //多个HandlerInterceptor
  @Nullable
  private HandlerInterceptor[] interceptors;
  @Nullable
  private List<HandlerInterceptor> interceptorList;
  
  // 标记(记号)
  private int interceptorIndex;
  /**
  interceptorIndex就是一个标记,标记HandlerInterceptorList或数组中哪一些preHandle返回值为true,
  提供的是下标(序号)
  */
}
java 复制代码
public interface HandlerInterceptor {

    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}
  • 配置HandlerInterceptor
    • 实现HandlerInterceptor接口
      • preHandle在Handler执行之前执行的 ,返回值为boolean
        • 如果返回值为true则继续流程
        • 如果返回值为false则中断流程;会去执行返回值为true的部分的afterCompletion
        • 如果有多个HandlerInterceptor,preHandle方法是正序遍历
      • Handler → 通常就是Controller组件中的Handler方法
      • postHandle在Handler之后执行的
        • 如果执行不到Handler(前面的preHandle返回值为false),那么一个postHandle都执行不到
        • 如果能够执行到Handler就能够执行到全部的postHandle
        • 如果有多个HandlerInterceptor,postHandle方法是倒序遍历
      • afterCompletion执行完postHandle、preHandle返回值为false
        • 执行完postHandle之后执行afterCompletion,能够执行到全部的afterCompletion
        • preHandle返回值为false的时候执行afterCompletion的话,执行的是preHandle返回值为true的部分afterCompletion
        • 如果有多个HandlerInterceptor,afterCompletion方法是倒序遍历
  • 配置HandlerInterceptor以及其作用范围
    • WebMvcConfigurer接口中的方法:getInterceptors

eg:

  • WebConfiguration中配置HandlerInterceptor
java 复制代码
@EnableWebMvc // 功能有:@Configuration + 使用SpringMVC的相关配置
@ComponentScan("com.coo1heisenberg.demo7.controller")
// WebMvcConfigurer接口:MVC相关的配置要用到这个接口提供的方法和参数
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        /**
         * 默认的作用范围是 /**
         */
        registry.addInterceptor(new HandlerInterceptor1()).addPathPatterns("/hello");
        registry.addInterceptor(new HandlerInterceptor2()).addPathPatterns("/hello/**");
        registry.addInterceptor(new HandlerInterceptor3()).addPathPatterns("/goodbye");
        registry.addInterceptor(new HandlerInterceptor4()).addPathPatterns("/goodbye/**");
        //registry.addInterceptor(new HandlerInterceptor5()) //.addPathPatterns("/**");
    }
}
  • interceptor目录下的handlerInterceptor1/2/3/4
java 复制代码
@Component
public class HandlerInterceptor1 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle1");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle1");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion1");
    }
}
java 复制代码
@Component
public class HandlerInterceptor2 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle2");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle2");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion2");
    }
}
java 复制代码
@Component
public class HandlerInterceptor3 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle3");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle3");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion3");
    }
}
java 复制代码
@Component
public class HandlerInterceptor4 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle4");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle4");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion4");
    }
}

Filter与HandlerInterceptor之间的对比

  • 实现的功能都是类似的:
    • Filter是在Servlet之前、之后都可以执行一些业务
    • HandlerInterceptor是在Handler之前和之后可以执行一些业务
  • 如果访问不到对应的Handler,Filter可以执行到;如果没有对应的Handler处理对应的请求,HandlerExecutionChain也为空
  • HandlerInterceptor的使用和Filter不一样 → 容器 → 注册进去,可以维护和其他组件之间的依赖关系(成员变量注入容器中的其他组件) → 取出来的时候其成员变量就已经赋值了

异常处理

  • 在Handler中制造异常
  • 如果不做异常处理:不友好、有可能泄露信息
  1. HandlerExceptionResolver
    • 处理全局的全部异常
    • 返回值为ModelAndView

eg:

java 复制代码
@Component
public class CustomHandlerExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest,
                                         HttpServletResponse httpServletResponse,
                                         Object handler, // 抛出异常的handler
                                         Exception e) { // handler抛出的异常
        ModelAndView modelAndView = new ModelAndView("/exception.jsp");
        if (e instanceof ArithmeticException) {
            // 算数异常 做特定的处理
        }
        if (e instanceof  NullPointerException) {
            // 空指针异常 做特定的处理
        }
        // 能否响应Json字符串
        // httpServletResponse.getWriter().println(jsonStr);
        return modelAndView;
    }
}
html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<h1>应用程序正在升级维护,请稍后</h1>

</body>
</html>
  1. @ExceptionHandler → 建议
    • 处理的特定类型的异常
    • 返回值可以为ModelAndView,也可以为String或Json字符串
java 复制代码
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
    Class<? extends Throwable>[] value() default {};
}

eg:

java 复制代码
@ControllerAdvice
@ResponseBody
@RestControllerAdvice // @RestControllerAdvice == @ResponseBody + @ControllerAdvice
public class ExceptionControllerAdvice {
    /**
     * 下面两个都是响应ModelAndView
     * @return
     */
    @ExceptionHandler(value = ArithmeticException.class)
    public ModelAndView arithmeticExceptionResolveMethod() {
        return new ModelAndView("/exception.jsp");
    }

    @ExceptionHandler(value = ArithmeticException.class)
    public String arithmeticExceptionResolveMethod() {
        return "/exception.jsp"; // //返回值作为ModelAndView中的视图名
    }

    /**
     * 响应Json字符串
     */
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public BaseRespVo arithmeticExceptionResolveMethod() {
        return BaseRespVo.fail("除零异常");
    }
}

SpringMVC核心流程

  • DispatcherServlet处理我们(几乎)全部请求
  • Controller组件中的方法处理请求,这也是我们主要开发的内容

流程图


DispatcherServlet和ApplicationContext的关系

  • 方法存在于Controller组件中
  • Controller组件存储在容器中 ,容器就是ApplicationContext
  • 当我们发送请求的时候,会执行到这些方法;当我们发送请求的时候,DispatcherServlet处理我们全部的请求, DispatcherServlet → 方法
  • Handler处理器,method1、method2、method3 其实就是处理器,处理我们的请求,也称之为HandlerMethod(Handler方法)
  • DispatcherServlet如果能够找到ApplicationContext(容器),就可以执行到容器中的Controller组件中Handler(方法)

相关推荐
烟沙九洲23 分钟前
算法的时间复杂度
java·算法
写bug写bug33 分钟前
小小 Stream,一篇文章拿捏它
java·后端
写bug写bug37 分钟前
好用的Lombok注解
java·后端
码熔burning1 小时前
【NIO番外篇】之组件 Selector
java·io·nio·selector
triticale1 小时前
【数论】线性筛质数
java·算法
百锦再1 小时前
Android ImageView 使用详解
android·java·app·手机·安卓·studio
小马爱打代码1 小时前
Spring JDBC 与数据访问:从性能优化到事务协同
spring·性能优化
续亮~2 小时前
提示词 (Prompt)
java·人工智能·prompt·ai编程·springai
aiden:)2 小时前
设计模式之工厂模式(factory pattern):在商品对象创建系统中的应用
java·开发语言·设计模式·软件工程·软件构建
Tdm_8882 小时前
SQL Server中OPENJSON + WITH 来解析JSON
java·数据库·sql·c#·json·mssql