SpringMvc 纯注解搭建和 Request 小结

SpringMvc 是一种底层基于 Servlet 实现 MVC 模型的轻量级 Web 开发框架,是一种侧重于表现层的开发技术。

SpringMvc 使用起来比 Servlet 要方便很多,性能也很不错,常用于小型项目的快速搭建和开发。本篇博客不会介绍有关 MVC 的理论知识,主要侧重于代码实践,采用纯注解的方式快速搭建 SpringMvc 工程,并展示常用的接收请求参数的方式。在本篇博客的最后会提供 Demo 的源代码。

有关 SpringMvc 的详细使用可参考官网:
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc


一、搭建工程

新建一个 maven 项目,导入相关 jar 包,我所导入的 jar 包都是最新的,内容如下:

有关具体的 jar 包地址,可以在 https://mvnrepository.com 上进行查询。

<dependencies>
    <!--导入 servlet 相关的 jar 包-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
        <scope>provided</scope>
    </dependency>
    
    <!--导入 Spring 核心 jar 包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.18</version>
    </dependency>
    <!--导入 SpringMvc 的 jar 包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.18</version>
    </dependency>
</dependencies>

配置好引用的 jar 包后,打开右侧的 Maven 窗口,刷新一下,这样 Maven 会自动下载所需的 jar 包文件。

搭建好的项目工程整体目录比较简单,具体如下图所示:

image

项目工程结构简单介绍:

com.jobs.config 包下存储的是 SpringMvc 的配置文件和 Servlet 的初始化文件
com.jobs.controller 包下存储的是用于提供 http 请求的类
com.jobs.domain 包下存储的是 JavaBean 实体类

web 目录下放置的是网站文件,其中 statics 目录下存放是静态资源文件


二、SpringMvc 配置相关

com.jobs.config 下的 SpringMvcConfig 类是 SpringMvc 的配置类,具体内容如下:

package com.jobs.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@Configuration
@ComponentScan("com.jobs")
//启用 mvc 功能,配置了该注解之后,SpringMvc 拦截器放行相关资源的设置,才会生效
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {

    //配置 SpringMvc 拦截器放行的资源类型
    /*@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/statics/**").addResourceLocations("/statics/");
        //registry.addResourceHandler("/statics/css/**").addResourceLocations("/statics/css/");
        //registry.addResourceHandler("/statics/img/**").addResourceLocations("/statics/img/");
        //registry.addResourceHandler("/statics/js/**").addResourceLocations("/statics/js/");
    }*/

    //配置 SpringMvc 连接器放行常用资源的格式(图片,js,css)
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    //注解配置 SpringMvc 返回配置的字符串所表示的页面,从哪些去找
    //可以注释掉下面的方法,这样需要在 SpringMvc 方法返回时,指定全局路径的页面地址
    //这里配置的是:根据 SpringMvc 方法返回的字符串,到 /WEB-INF/pages/ 下找对应名称的 jsp 页面
    @Bean
    public InternalResourceViewResolver getViewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/pages/");
        viewResolver.setSuffix(".jsp");
        //如果页面需要使用JSTL标签库的话
        //viewResolver.setViewClass(JstlView.class);
        return viewResolver;
    }
}

这里需要注意以下几项:

  • 使用注解 @ComponentScan 中的 includeFilters 指定 SpringMvc 只扫描装载带有 @Controller 注解的类,其它的注解不需要给 SpringMvc 扫描,其它的注解全部交给 Spring 来扫描装载。
  • 必须在 SpringMvc 的配置类上添加 @EnableWebMvc 注解,这样才能够有机会访问到静态资源(图片、css、js)。
  • 可以重写 configureDefaultServletHandling 方法,只需要一句代码 configurer.enable() 即可让 SpringMvc 拦截器放行静态资源的访问限制。

SpringMvc 技术底层是基于 Servlet 实现的,因此必须让 Servlet 装载才能实现,我们编写 ServletInitConfig 类初始化 Servlet 容器,装载 SpringMvc 的配置,具体内容如下:

package com.jobs.config;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;

import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.util.EnumSet;

public class ServletInitConfig extends AbstractDispatcherServletInitializer {

    //初始化 Servlet 容器,加载 SpringMvc 配置类
    //创建 web 专用的 Spring 容器对象:WebApplicationContext
    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext cwa = new AnnotationConfigWebApplicationContext();
        //装载 SpringMvc 的配置
        cwa.register(SpringMvcConfig.class);
        return cwa;
    }

    //配置 SpringMvc 的 DispatcherServlet 拦截地址,拦截所有请求
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }

    //在servlet容器启动时进行配置统一字符编码,防止接收到的中文参数值是乱码
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        //这行代码不能省略
        super.onStartup(servletContext);

        //设置【获取到的请求】的统一字符编码过滤器,
        //无论是【请求、转发、包含】统一使用 UTF-8 编码
        CharacterEncodingFilter cef = new CharacterEncodingFilter();
        cef.setEncoding("UTF-8");
        //注意:这里的过滤器名称必须是 characterEncodingFilter
        FilterRegistration.Dynamic registration =
            servletContext.addFilter("characterEncodingFilter", cef);
        registration.addMappingForUrlPatterns(
            EnumSet.of(DispatcherType.REQUEST,
                DispatcherType.FORWARD, DispatcherType.INCLUDE), false, "/*");
    }
}

经过上面两个类的处理,SpringMvc 纯注解方式已经配置完毕,下面就可以使用 SpringMvc 技术处理请求了。


三、验证搭建成果

我们编写 ReqController1 类来验证 SpringMvc 搭建成果,具体内容如下:

package com.jobs.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

//@RequestMapping 注解可以在类上使用,也可以在类中具体的方法上使用
@RequestMapping("/req1")
@Controller
public class ReqController1 {

    //使用配置的前缀和后缀
    @RequestMapping("/test1")
    public String SpringMvcTest1() {
        System.out.println("SpringMvc 搭建成功...默认使用所配置的前缀和后缀");

        //由于在 SpringMvcConfig 类中的 getViewResolver 方法,配置了前缀和后缀
        //因此这里如果返回字符串的话,默认会到 /WEB-INF/pages/ 下找对应名称的 jsp 页面
        //由于这里返回字符串 success,因此直接找 /WEB-INF/pages/success.jsp 页面
        return "success";
    }

    //临时不想使用所配置的前缀和后缀
    @RequestMapping("test2")
    public String SpringMvcTest2() {
        System.out.println("SpringMvc 如果不想使用所配置的前缀和后缀,可以使用 forward 或 redirect");

        //当使用了 forward 或 redirect 时,将不使用所配置的前缀和后缀
        return "forward:/ok.html";
        //return "redirect:/ok.html";
    }

    //访问静态资源
    @RequestMapping(value = "test3")
    public String SpringMvcTest3() {
        System.out.println("SpringMvc 查看静态资源");

        //由于在 SpringMvcConfig 类中重写了 configureDefaultServletHandling 方法,
        //启用了常用的静态资源访问,所以 SpringMvc 不会拦截静态资源的访问
        //在 picture.jsp 页面,可以看到 css 样式效果,图片展示,以及外部 js 引用后可以运行
        return "picture";
    }
}

上面用到的静态资源内容如下:

test.css 的内容为:

h1{
    color: coral;
}

test.js 的内容为:

function testalert() {
    alert('努力学习,天天向上!!!');
}

所用到的页面内容如下:

success.jsp 页面代码为:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>请求成功</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/statics/css/test.css">
</head>
<body>
    <h1>这里是 jsp 动态页面:success.jsp</h1>
    <h1>请求成功,请查看控制台上打印的结果...</h1>
</body>
</html>

picture.jsp 页面代码为:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/statics/css/test.css">
</head>
<body>
    <h1>这里是 picture.jsp 展示静态资源(图片,css,js)的访问:</h1>
    <img src="${pageContext.request.contextPath}/statics/img/study.jpg"/>
    <input type="button" onclick="testalert()" value="点击我,运行js弹出框"/>

    <!--建议把 js 文件放在网页的最下面,提高网页的打开速度-->
    <script src="${pageContext.request.contextPath}/statics/js/test.js"></script>
</body>
</html>

ok.html 静态页面的代码为:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>请求成功</title>
    <link rel="stylesheet" href="/statics/css/test.css">
</head>
<body>
    <h1>这里是静态页面:ok.html</h1>
    <h1>请求成功,请查看控制台上打印的结果...</h1>
</body>
</html>

四、请求参数的接收

首先列出 Employee 实体类,因为下面的代码会演示使用实体类作为接收参数,来接收请求参数中的值,然后编写 ReqController2 类,来演示在实际开发中,常用的一些请求参数的接收方式,具体细节如下:

package com.jobs.domain;

import java.util.List;

public class Employee {

    //姓名
    private String name;
    //年龄
    private Integer age;
    //爱好
    private List<String> hobby;

    //这里省略了各个字段的 get 和 set 方法...

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", hobby=" + hobby +
                '}';
    }
}
package com.jobs.controller;

import com.jobs.domain.Employee;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

//@RequestMapping 注解可以在类上使用,也可以在类中具体的方法上使用
//@RequestMapping("req2")
@Controller
public class ReqController2 {

    //请求参数与接收参数,名称必须一致,如果参数缺少,则参数取类型默认值
    //http://localhost:8080/request1?name=jobs
    //http://localhost:8080/request1?name=jobs&age=33
    @RequestMapping("/request1")
    public String request1(String name, Integer age) {
        String result = "name=" + name + ",age=" + age;
        System.out.println(result);
        return "success";
    }

    //设置请求参数的名称,以及是否为必须提供的参数,以及设置参数的默认值
    //http://localhost:8080/request2 报错,没有提供必填参数 userName
    //http://localhost:8080/request2?name=jobs
    //http://localhost:8080/request2?name=jobs&age=33
    @RequestMapping("/request2")
    public String request2(
            @RequestParam(value = "userName", required = true) String name,
            @RequestParam(value = "userAge", defaultValue = "30") Integer age) {
        String result = "name=" + name + ",age=" + age;
        System.out.println(result);
        return "success";
    }

    //请求参数名称,与接收参数的 JavaBean 类的字段名称相同的话,
    //则 SpringMvc 会根据请求参数自动给 JavaBean 实例化的对象属性赋值
    //http://localhost:8080/request3?name=jobs
    //http://localhost:8080/request3?name=jobs&age=33
    @RequestMapping("/request3")
    public String request3(Employee emp) {
        String result = "name=" + emp.getName() + ",age=" + emp.getAge();
        System.out.println(result);
        return "success";
    }

    //如果接收参数中,普通参数名称 与 JavaBean 类的字段名称相同的话,则都会进行赋值
    //http://localhost:8080/request4?name=jobs&age=33
    @RequestMapping("/request4")
    public String request4(Employee emp, String name, Integer age) {
        StringBuilder sb = new StringBuilder();
        sb.append("使用 JavaBean 类作为参数,获取的结果:");
        sb.append("name=" + emp.getName() + ",age=" + emp.getAge());
        sb.append("\r\n");
        sb.append("使用普通参数获取的结果:");
        sb.append("name=" + name + ",age=" + age);

        String result = sb.toString();
        System.out.println(result);
        return "success";
    }

    //使用数组作为参数,接收传入的数组数据
    //http://localhost:8080/request5?hobby=唱歌&hobby=跳舞&hobby=打游戏
    @RequestMapping("/request5")
    public String request5(String[] hobby) {
        if (hobby != null && hobby.length > 0) {
            for (String h : hobby) {
                System.out.println(h);
            }
        } else {
            System.out.println("未获取到所传入的 hobby 数据");
        }

        return "success";
    }

    //使用集合作为参数,接收传入的数组数据,无法直接获取数组数据
    //必须要使用 @RequestParam 注解将请求参数与接收参数进行名称映射
    //http://localhost:8080/request6?hobby=唱歌&hobby=跳舞&hobby=打游戏
    @RequestMapping("/request6")
    public String request6(
            @RequestParam(value = "hobby") List<String> hobbylist) {
        if (hobbylist != null && hobbylist.size() > 0) {
            for (String hobby : hobbylist) {
                System.out.println(hobby);
            }
        } else {
            System.out.println("未获取到所传入的 hobby 数据");
        }

        return "success";
    }

    //使用 JavaBean 类作为接收参数,接收数组数据
    //http://localhost:8080/request7?hobby=唱歌&hobby=跳舞&hobby=打游戏
    @RequestMapping("/request7")
    public String request7(Employee emp) {
        List<String> hobbylist = emp.getHobby();
        if (hobbylist != null && hobbylist.size() > 0) {
            for (String hobby : hobbylist) {
                System.out.println(hobby);
            }
        } else {
            System.out.println("未获取到所传入的 hobby 数据");
        }

        return "success";
    }

    //发来的请求数据,如果是日期类型,如果想直接使用日期类型的参数进行接收的话,
    //可以根据传入数据的日期格式,明确指定好接收数据的对应格式,就可以接收到具体的日期数据
    //http://localhost:8080/request8?date=2022-01-21 15:20:30
    @RequestMapping("/request8")
    public String request8(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date) {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
        String dateString = sdf.format(date);
        System.out.println(dateString);

        return "success";
    }

    //可以接收提交过来的 body 数据,必须使用 Post 请求方式
    //默认情况下,各种请求方式都支持,可以通过 method 指定允许的请求方式
    //这里可以使用 PostMan 来请求该接口,必须使用 Post 请求方式从 body 中获取数据
    //http://localhost:8080/request9
    @RequestMapping(value = "/request9", method = RequestMethod.POST)
    public String request9(@RequestBody String json) {
        System.out.println(json);
        return "success";
    }
}

上面的请求方式代码中,最后一个请求方法,从 body 中获取数据,需要采用 Post 请求才可以。这里可以使用 PostMan 工具进行测试,在 PostMan 工具中可以 Post 发送各种类型的数据,这里仅仅以 Post 发送 Json 数据为例,因为在实际开发中,向服务器发送 Json 格式的数据比较常用,使用 PostMan 工具请求最后一个接口的细节如下所示:

image

在 IDEA 工具的控制台上打印出 Post 请求发送过来的数据:

image



到此为止,SpringMvc 采用纯注解方式快速搭建已经介绍完毕。在 SpringMvc 的纯注解配置中实现了监控所有请求,并忽略和放行常见静态资源的请求,通过配置前缀和后缀,实现通过返回的字符串自动查找对应路径下的 jsp 页面。最后列出了常用的接收传入的参数值的几种方式。

本篇博客的 demo 源代码下载地址为:https://files.cnblogs.com/files/blogs/699532/SpringMVC_Request.zip



posted @ 2022-04-03 21:00  乔京飞  阅读(10118)  评论(0编辑  收藏  举报