2、详细使用

简单使用

1、创建Web项目

创建Java Enterprise项目,选择Maven管理依赖

注意:由于SpringMVC并没有集成Web容器,所以仍需要依靠Tomcat等Web容器启动

image-20230629143319016

1、pom.xml

<!-- war包打包 -->
<packaging>war</packaging>

<dependencies>
    <!-- springmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <!-- Servlet API -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <!-- jsp -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

<!-- war包打包插件 -->
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.3.0</version>
        </plugin>
    </plugins>
</build>

2、web.xml

web/WEB-INF/目录或webapp/WEB-INF/目录下,修改web.xml文件,配置SpringMVC的前端(核心)控制器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         id="WebApp_ID" version="3.1">
    <!-- 项目访问路径 -->
    <display-name>myweb</display-name>
    <!-- 配置前端控制器 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 开启一个专门用来支持WEB项目的WebApplicationContext容器 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!-- 此处为项目编译后spring配置文件路径 -->
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <!-- 正数:项目在启动时就会创建DispatcherServlet;负数:会在第一次访问时创建 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--此处使用*.do来标记动态请求,如果使用/的话,会导致静态请求404-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

3、applicationContext.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop" 
       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 
                           http://www.springframework.org/schema/aop 
                           http://www.springframework.org/schema/aop/spring-aop.xsd 
                           http://www.springframework.org/schema/context 
                           http://www.springframework.org/schema/context/spring-context.xsd 
                           http://www.springframework.org/schema/mvc
                           http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 配置自动扫描注解路径 -->
    <context:component-scan base-package="top.ygang.*"></context:component-scan>
    <!-- 开启MVC注解支持 -->
    <mvc:annotation-driven></mvc:annotation-driven>
    <!-- 
        静态资源处理方式一:交给Tomcat默认的defaultServlet处理 
        <mvc:default-servlet-handler></mvc:default-servlet-handler>
 	-->
    <!--
        静态资源处理方式二:将静态资源交给SpringMVC处理
		mapping:指的是以static开头的url资源路径
		location:指的是静态资源存放路径,多个路径可以使用逗号分隔
 	-->
    <mvc:resources mapping="/static/**" location="classpath:/static/"></mvc:resources>
    <!-- 视图解析器、设置controller转发、重定向路径默认前缀后缀,可选 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

4、编写entity、mapper、service、controller等

配置方式

在Spring2.5之前的版本,没有加入SpringMVC相关注解,所以自定义Controller需要实现rg.springframework.stereotype.Controller接口,并且实现handleRequest方法,然后在Spring配置文件applicationContext.xml中配置bean

bean标签对应的name即为该Controller映射的路径

注意:这种方法基本现在已经没人用了,都是基于注解进行开发的

public class TestController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        return new ModelAndView("test");
    }
}
<bean name="/test" class="top.ygang.springmvc_demo.controller.TestController"></bean>

注解方式(推荐)

Spring 2.5 版本新增了 Spring MVC 注解功能,用于替换传统的基于 XML 的 Spring MVC 配置。

不是所有的业务都需要持久层、业务层、控制层,可以根据自己的实际需求搭配

实体Entity

一般作为数据库表实体出现,封装数据库表实体数据

public class TestEntity {

    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "TestEntity{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

持久层Dao

一般作为与数据库交互层出现,主要是数据持久化相关代码逻辑

@Repository
public class TestDao {

    public TestEntity test(){
        TestEntity testEntity = new TestEntity();
        testEntity.setAge(18);
        testEntity.setName("lucy");
        return testEntity;
    }
}

业务层Service

一般作为实际业务层出现,主要是业务相关代码逻辑

@Service
public class TestService {

    @Autowired
    private TestDao testDao;

    public TestEntity test(){
        return testDao.test();
    }
}

info.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    name: ${entity.name}<br/>
    age: ${entity.age}
</body>
</html>

控制层Controller

控制层中一般不会编写业务逻辑,主要是接收、响应前端数据,以及异常响应的处理

@Controller
public class InfoController{

    @Autowired
    private TestService testService;

    @GetMapping("/info")
    public ModelAndView info(){
        ModelAndView modelAndView = new ModelAndView("info");
        modelAndView.addObject("entity",testService.test());
        return modelAndView;
    }
}

控制层方法的形参

控制层方法的形参,在满足条件的情况下,一般可以由SpringMVC框架自动注入(传参)

域对象

可以直接获取域对象:HttpServletRequest,HttpServletResponse,HttpSession,方便控制请求和响应

@PostMapping("/login")
public ModelAndView login(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
    String loginName = request.getParameter("loginName");
    String password = request.getParameter("password");
    System.out.println(loginName);
    System.out.println(password);
    ModelAndView mv = new ModelAndView();
    mv.setViewName("sysmag/main");
    return mv;
}

Cookie

@RequestMapping("/login")
public String login(@CookieValue("JSESSIONID") String sessionID) {
    System.out.println(sessionID);
    return "sysmag/main";
}

请求头

@RequestMapping("/login")
public String login(@RequestHeader("User-Agent") String userAgent) {
    System.out.println(userAgent);
    return "sysmag/main";
}

RequestEntity

RequestEntity是封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息,泛型为请求体数据类型

@PostMapping("/test")
public String test(RequestEntity<String> entity){
    HttpHeaders headers = entity.getHeaders();
    String body = entity.getBody();
    System.out.println(headers);
    System.out.println(body);
    return "info";
}

接收请求参数

除了通过域对象获取请求参数外,我们可以通过SpringMVC封装的参数解析器,接收解析常用类型的请求参数

1、UrlParams

参数拼接在url上,例如:http://localhost:8080/test?name=lucy&age=18

// 可以分别接收每个属性
@GetMapping("/test")
public String test(String name,int age){
    System.out.println(name);
    System.out.println(age);
    return "info";
}

可以使用@RequestParam注解指定参数名称,以及是否必传,默认情况下如果少参数会抛出异常

@GetMapping("/test")
public String test(@RequestParam(value = "name",required = false)String name, int age){
    System.out.println(name);
    System.out.println(age);
    return "info";
}

还可以通过实体类接收

class User{
    private String name;

    private int age;

    public User(){}

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


@GetMapping("/test")
public String test(User user){
    System.out.println(user);
    return "info";
}

也可以通过Map进行接收,必须使用@RequestParam修饰

@GetMapping("/test")
public String test(@RequestParam Map<String,Object> map){
    System.out.println(map);
    return "info";
}

urlParams也可以传递数组或List,必须使用@RequestParam修饰

http://localhost:8080/test?id=1&id=2

@GetMapping("/test")
public String test(@RequestParam int[] id){
    System.out.println(Arrays.toString(id));
    return "info";
}
@GetMapping("/test")
public String test(@RequestParam List<Integer> id){
    System.out.println(id);
    return "info";
}

2、PathParams

rest风格参数,例如:http://localhost:8080/test/lucy/18

@GetMapping("/test/{name}/{age}")
public String test(@PathVariable("name") String name,@PathVariable("age") int age){
    System.out.println(name);
    System.out.println(age);
    return "info";
}

3、BodyParams

存在于请求体的参数,常见的由application/json multipart/form-dataapplication/x-www-form-urlencoded

application/x-www-form-urlencoded
@PostMapping("/test")
public String test(String name, int age){
    System.out.println(name);
    System.out.println(age);
    return "info";
}

也可以使用实体接收

class User{
    private String name;

    private int age;

    public User(){}

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

@PostMapping("/test")
public String test(User user){
    System.out.println(user);
    return "info";
}

同样支持Map、Array、List

application/form-data

一般作为前端文件上传到后端的方式,SpringMVC的FormHttpMessageConverter不支持读取该Content-Type,所以如果需要解析这种类型

1、需要引入common-fileupload依赖

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

2、Spring配置文件applicationContext.xml中配置解析器

<!-- application/multipart-file解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	<!-- 配置编码集 -->
    <property name="defaultEncoding" value="utf-8"/>
    <!-- 单次文件上传时的最大容量,单位:byte eg:100M=1024*1024*100,-1 表示不做限制-->
    <property name="maxUploadSize" value="104857600"/>
    <!-- 每次读取文件时,以1KB的方式来读取 -->
    <property name="maxInMemorySize" value="1024"/>
</bean>

3、控制层接收文件字段

@PostMapping("/test")
public String test(String name,MultipartFile file){
    System.out.println(name);
    System.out.println(file.getOriginalFilename());
    return "info";
}

image-20230711165606523

application/json

默认的@RequestBody,只会接收Body中的值,不会进行解析,例如json则需要我们自己解析

{
    "name": "lucy",
    "age": 18
}
@PostMapping("/test")
public String test(@RequestBody String json){
    System.out.println(json); 
    return "info";
}

//{
//    "name": "lucy",
//    "age": 18
//}

我们可以定义json消息转换器,来自动解析application/json类型数据

1、导入jackson依赖

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.6</version>
</dependency>

2、使用@RequestBody接收即可

@PostMapping("/test")
public String test(@RequestBody Map<String,Object> map){
    System.out.println(map);
    return "info";
}

或者使用实体接收

class User{
    private String name;

    private int age;

    public User(){}

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


@PostMapping("/test")
public String test(@RequestBody User user){
    System.out.println(user);
    return "info";
}

控制层方法返回类型

1、ModelAndView

ModelAndView 是Spring MVC框架中默认的返回类型,Model代表是模型数据,View代表的是需要跳转的页面(但是只是一个逻辑视图的名字),可以通过addObject进行传递数据,可以通过在构造器参数,或setViewName进行页面的跳转,return该对象即可

@RequestMapping("/login")
public ModelAndView login(String loginName){
    System.out.println(loginName);
    ModelAndView mv = new ModelAndView();
    mv.setViewName("sysmag/main");
    return mv;
}

2、String

默认是转发

String返回的字符串,就是需要跳转的页面,如果返回值是null,Spring MVC框架将会自动将请求路径作为即将要跳转的页面,所以尽量不要返回 null

@RequestMapping("/login")
public String login(String loginName){
    System.out.println(loginName);
    return "sysmag/main";
}

转发

"forward:/sys/login02"

注意:forward后一定要是完整路径,可以用来转发控制层的路径

@RequestMapping("/login")
public String login(String loginName){
    System.out.println(loginName);
    return "forward:/sys/login02";
}

重定向

"redirect:/sys/login02"

@RequestMapping("/login")
public String login(String loginName){
    System.out.println(loginName);
    return "redirect:/sys/login02";
}

视图参数传递方法

1、ModelAndView

ModelAndView可以指定视图文件(.jsp)路径,也可以给视图进行传参

@GetMapping("/info")
public ModelAndView info(ModelAndView modelAndView){
    User user = new User();
    user.setId(3L);
    user.setUserName("王五");
    modelAndView.addObject("user",user);
    modelAndView.setViewName("userView");
    return modelAndView;
}

2、ModelMap

ModelMap对象主要用于传递控制方法处理数据到结果页面,他的作用类似于request对象的setAttribute方法的作用,用来在一个请求过程中传递处理的数据

addAttribute(String key,Object value);

modelmap本身不能设置页面跳转的url地址别名或者物理跳转地址,那么我们可以通过控制器方法的返回值来设置跳转url地址别名或者物理跳转地址。

@RequestMapping("/useModelMap")
public String useModelMap(ModelMap modelMap) {
    User user = new User();
    user.setId(3L);
    user.setUserName("王五");
    modelMap.addAttribute("user", user);
    return "userView";
}

3、Model

ModelMap 主要用于传递控制器方法处理数据到页面。等同于ModelMap。

@RequestMapping("/userModel")
public String userModel(Model model) {
    User user = new User();
    user.setId(2L);
    user.setUserName("李四");;
    model.addAttribute("user", user);
    return "userView";
}

HttpMessageConverter

org.springframework.http.converter.HttpMessageConverter,报文消息转换器,主要用来控制层接收、响应消息时,对消息进行解析

public interface HttpMessageConverter<T> {
	// 检查clazz对象是否能转换为mediaType表示的数据类型,这个mediaType是前端页面请求时设定的ContentType格式!
	boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
	// 如果canRead方法返回值为true则调用read方法将数据进行格式转换!
	T read(Class<? extends T> clazz, HttpInputMessage inputMessage);
    // 检查clazz对象是否能转换为mediaType类型,此时的mediaType表示后端想要响应给前端的数据格式!
	boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
	// 如果canWrite返回值为true,则将数据进行格式的转换然后响应!
	void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage);
    // 获取该转换器支持的mediaType,即ContentType
	List<MediaType> getSupportedMediaTypes();
}

默认情况下SpringMVC框架在配置文件中不进行注解驱动<mvc:annotation-driven>的支持情况下,会默认给应用使用4个实现类用于接受/响应数据的转换,在开启后会增加到7个左右(版本不同)。

实现类 说明
StringHttpMessageConverter 负责读取、写出String格式的数据
MappingJackson2HttpMessageConverter 利用jackson的ObjectMapper读写Json数据,需要引入jackson依赖
StringHttpMessageConverter 将请求信息转为字符串
FormHttpMessageConverter 可以读写application/x-www-form-urlencoded的数据,但只能用来写multipart/form-data的数据,
XmlAwareFormHttpMessageConverter 扩展与FormHttpMessageConverter,如果部分表单属性是XML数据,可用该转换器进行读取
ResourceHttpMessageConverter 读写org.springframework.core.io.Resource对象
BufferedImageHttpMessageConverter 读写BufferedImage对象
ByteArrayHttpMessageConverter 读写二进制数据
SourceHttpMessageConverter 读写java.xml.transform.Source类型的对象
MarshallingHttpMessageConverter 通过Spring的org.springframework,xml.Marshaller和Unmarshaller读写XML消息
Jaxb2RootElementHttpMessageConverter 通过JAXB2读写XML消息,将请求消息转换为标注的XmlRootElement和XmlType连接的类中
MappingJacksonHttpMessageConverter 利用Jackson开源包的ObjectMapper读写JSON数据
RssChannelHttpMessageConverter 读写RSS种子消息
AtomFeedHttpMessageConverter 和RssChannelHttpMessageConverter能够读写RSS种子消息

SpringMVC乱码过滤器

web.xml文件中添加过滤器

<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <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>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>