SpringMVC

317 阅读10分钟

SpringMVC

项目配置

注意事项

注意 JDK 版本和 spring-webmvc 版本配置。不匹配将会出现很多的问题异常,目前配置:

  1. JDK 1.8
  2. spring-webmvc 4.1.3.RELEASE
  3. javax.servlet-api 4.0.1

SpringMVC 技术与 Servlet 技术功能相等,均属于 web 层开发技术 是一种基于 java 实现的 MVC 模型的轻量级 WEb 框架 SpringMVC 是一种表现层框架技术 SpringMVC 用于进行表现层功能开发

  1. 基于 SpringMVC 获取请求参数与响应 json 数据
  2. 熟练应用基于 REST 风格的请求路径设置与参数传递
  3. 能够根据实际业务简历前后端开发通信协议并进行实现
  4. 基于 SSM 整合技术开打任意模块的功能

SpringMVC 的开发步骤

  1. 引入坐标
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.22</version>
</dependency>
<!-- spring-webmvc版本使用 5.3 以上会出现 NoSuchMethodException 异常 -->
  1. 创建 SpringMVC 控制器类
package com.lmb.controller;

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

@Controller
public class UserController() {

    @RequestMapping("/save")
    @ResponseBody
    public String save() {
        System.out.println("user save ...");
        return "{ 'info': 'springmvc' }";
    }
}
  1. 初始化 SpringMVC 配置文件,同 Spring 配置文件,设定 SpringMVC 加载对应 Bean
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.lmb.controller")
public class SpringMvcConfig() {}
  1. 初始化 Servlet 容器,加载 SpringMVC 环境, 并设置 SpringMVC 技术处理得请求
// SpringMVC 已经定义好了一个类 AbstractDispatcherServletInitializer,
// 我们只需要继承自这个类

import com.lmb.config.SpringMvcConfig;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;

public class WebApplicationContext extends AbstractDispatcherServletInitializer {
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(SpringMvcConfig.class);
        return ctx;
    }
    
    /*
     * 配置那些请求路径交给 SpringMVC 处理,
     * */
    protected String[] getServletMapping() {
        return new String[]{"/"};
    }
    
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }
}
  1. 配置 Maven Tomcat 插件,启动开发 tomcat 服务器环境
<!--pom.xml-->
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <port>9000</port>
                <path>/</path>
            </configuration>
        </plugin>
    </plugins>
</build>

SpringMVC 的工作流程

服务器初始化的过程

  1. 启动服务器,执行 ServletContainersInitConfig 类,初始化 web 容器
  2. 执行 createServletApplicationContext 方法,创建了 WebApplicationContext 对象
  3. 加载 SpringMvcConfig
  4. 执行 @ComponentScan 加载对应的 bean
  5. 加载 UserController,每个 @RequestMapping 标注的名称对应一个 具体的方法
  6. 执行 getServletMappings 方法,获取哪些请求由 SpringMVC 来处理

单次请求的过程

  1. 发送请求 localhost/save
  2. web 容器检测该请求路径是否应该交给 SpringMVC,是的话将交给 SpringMVC 来处理
  3. 解析请求路径 /save
  4. 由 /save 匹配到对应的执行方法 save()
  5. 执行 save()
  6. 检测到有 #ResponseBody 直接将 save() 方法的返回值作为响应体返回给请求方

注解

  1. @Controller

    • 设定在 SpringMVC 控制器类的定义上方
    • 用于设定 SpringMVC 核心的控制器 Bean
  2. @RequestMapping("/访问路径")

    • 写在方法的上面
    • 用来设置访问路径
    • 也可以写在 controller 类的上面表示统一前缀
    • attr
      • method:指定请求方式,没有指定默认GET,POST请求都可以访问
  3. @ResponseBody

    • 写在 SpringMVC 方法的上面
    • 设置当前控制器方法响应内容为当前的返回值,无需解析。
  4. @RequestParam("name")

    • 写在方法参数前面
    • 用来绑定请求参数和方法中的形参
  5. @EnableWebMvc

    • 写在 SpringMVC 配置类上
    • 开启自动转换 json 数据的支持
    • 使用 @RequestBody 标记一个 json 入参
  6. @RequestBody

    • 写在方法形参之前
      • 标注形参为 json 格式的参数,此注解一个处理器方法只能写一个
    • 写在方法之上
      • 设置当前的控制器返回值为响应体
    • 写在 Controller 类之上代表该控制器中的方法都将返回值作为响应体
  7. @DateTimeFormat(pattern = "yyyy-MM-dd")

    • 写在方法参数之前
    • 用来定义时间格式参数的解析模式
  8. @PathVariable

    • 从请求的路径中注入参数,需要配合 @RequestMapping 只当参数匹配模式
    • 例如 @RequestMapping("/{id}")
  9. @RestController

    • 标识使用 REST 开发的控制器类,等于在类上加入注解 @Controller + @ResponseBody
  10. @PostMapping/@GetMapping/@PutMapping/@DeleteMapping

@RequestParam 和 @RequestBody 之间有什么区别

  1. 区别
    • @RequestParam 用于接收 url 地址传参,表单传参[content-type: application/x-www-form-urlencoded]
    • @RequestBody 用于接收 json 格式的数据[content-type: application/json]
  2. 应用
    • 后期开发中,发送 json 格式的数据为主,@RequestBody 使用的范围较广
    • 如果发送非 json 格式的数据,选用 @RequestParam 接受请求参数(文件)

Spring 中整合 SpringMVC 注意事项

项目一般来说 com.lmb 下分

  1. service
  2. dao
  3. domain
  4. controller
  5. config
  6. ...

其中 SpringMVC 控制的 Bean 位于 controller 中,Spring 控制的 Bean 位于除了 controller 中的其他包 在进行 Bean 扫描控制(@ComponentScan)的时候注意不要重复扫描

  1. Spring 加载的 Bean 设定扫描范围为 com.lmb,但是排除掉 controller
// SpringConfig.java
@Configuration
//@ComponentScan({ "com.lmb.service", "com.lmb.dao" })
@ComponentScan(
        value="com.lmb",
        excludeFilters = @ComponentScan.Filter(
                type = FilterType.ANNOTATION,
                classes = Controller.class
        )
)
public class SpringConfig {}
  1. Spring 加载的 Bean 设定扫描范围精准设定为,dao,domain,service....
@Configuration
//@ComponentScan({ "com.lmb.service", "com.lmb.dao", "com.lmb.domain" })
public class SpringConfig {}
  1. 不区分环境

乱码解决问题

  1. Post 为 Web 容器添加过滤器并只当字符集为 UTF-8, Spring-web包中提供了字符过滤器
// 在 SpringMVC Web 容器配置类中重写以下的方法,并添加字符过滤器
public class ServletContainerInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF-8");
        return new Filter[]{filter};
    }
}

  1. Get

请求与响应

参数传递

  1. 直接在方法上加入参,一个入参就代表一个请求参数,请求时会将对应的参数直接作为形参传递进方中
@RequestMapping("/save")
@ResponseBody
public String save(String name, int id) {
    System.out.println("user save ....");
    System.out.println("name: " + name);
    System.out.println("id: " + id);
    return "{'module': 'springmvc'}";
}
  1. 请求参数和方法参数不一致
  • 使用 @RequestParam("name") 绑定映射关系
  1. 使用实体类作为形参接受参数
  • 实体类中要切记要提供无参的构造方法和有参的构造方法

  • 实体类中要提供成员变量的 Getter 和 Setter 方法

  • 实体类中最好重写 toString 方法

  • 如果参数的属性名和实体类中的成员变量一致,可以自动转换获取实体类对象

  • 如果实体类中有引用数据类型,可以 address.city 这样的形式来传参(不建议使用)

  1. 使用数组形式接收
  • 请求中多个一样的参数名会自动转化成一个数组
  • localhost:9000/user/add?likes=swimming&likes=run&likes=eat
  • 方法定义:public String add(String[] likes) {}
  1. 使用集合传参
  • 集合参数前面要加上 @RequestParam 注解
  1. JSON 参数
  • 先引入 jackson-databind 依赖
  • 在 SpringMVC 配置类中加入注解 @EnableWebMvc
  1. Date 时间类型
  • SpringMVC 可以自动的解析格式为 "yyyy/MM/dd" 的字符串为 Date 类型参数
  • 如果需要自定以解析格式,可以在参数加上 @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") 注解

响应请求

  1. 响应页面
  • public String jumpToPage() { return "页面路径" }
  • 只需要返回一个页面路径的字符串就行
  • 不要在方法上加入 @ResponseBody 注解,这个注解会让返回值我作为响应发送给请求方,这样返回给请求方的就是一个字符串而不是页面了

REST 风格

REST (Representational State Transfer),表现形式状态转换

REST

  1. 请求路径风格
  1. 使用 REST 风格定义用户的增删改查
  • /users 查询全部用户 GET
  • /users/1 查询指定 id 的用户 GET
  • /users 添加用户 POST
  • /users 修改用户信息 PUT
  • /users/1 删除指定 id 的用户 DELETE
  1. 为什么叫风格 REST 只是一种资源定义的思想,并不是强制性要求,开发中开发者可以根据自身需求选择遵循 也可以选择不遵循。所以称之为一种风格,而不是一种规范

  2. 模块的名称描述通常使用复数形式,也就是加 s 的风格描述,表示此类资源,而非单个资源,如:users,books,accounts ...

  3. 根据 REST 风格对资源进行访问叫做 RESTful

SpringMVC 中对 REST 的支持

  1. 修改请求方式,@RequestMapping(method = RequestMethod.GET/POST/PUT/DELETE)
  2. 指定请求的参数
    • @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    • public String getById(@PathVariable Integer id)

@RequestBody,@RequestParam,@PathVariable

  1. 区别
  • @RequestParam 用于接收 url 传参或者表单传参
  • @RequestBody 用来接收 json 数据传参
  • @PathVariable 用于接收 url 传参,使用 {参数名称} 描述路径参数
  1. 应用
  • 后期开发中,发送请求的参数超过 1 个时,以 json 格式为主,@RequestBody 使用广泛
  • 如果发送非 json 的数据,选用 @RequestParam 接收请求参数
  • 采用 RESTful 风格进行开发时,当参数数量较少,例如 1 个,采用 @PathVariable 接收请求路径的变量,通常用于传递 id

开发简化

  1. 使用 @RestController 代替控制器类中的 @Controller 和 @ResponseBody
  2. 控制器类上添加 @RequestMapping 定义总体的路径
  3. 控制器类方法上的 @RequestMapping 更换成 @PostMapping/@GetMapping/@PutMapping/@DeleteMapping 定义请求方式和路径

SSM 整合

拦截器

统一返回的风格

package com.lmb.pojo;

public class CommonRespond {
    private Object data;
    private Integer code;
    private String msg;
    /* 提供构造方法(无参/有参),提供 Getter/Setter 方法 */
}
/*
 * 1. data:数据
 * 2. code:响应码
 * 3. msg:消息
 * */

异常处理器

  1. 异常的分类
  • 框架内容抛出的异常:使用不合理导致
  • 数据层抛出的异常(dao):因外部服务器故障导致
  • 业务层抛出的异常(service):因为业务逻辑书写错误导致
  • 表现层抛出的异常(controller):因为数据收集,校验等规则导致
  • 工具类抛出的异常(utils):因为工具类书写不严谨导致不够健壮导致
  1. 各个层都有可能出现异常,如何处理
  • 所有的异常都上抛出到表现层(controller),由变现层来处理。(为什么:再往上抛出就到前端了,所以我们要在表现层处理)
  • 出现异常就要处理,可以使用 AOP 来处理,
  • springMVC 根据 AOP 思想对异常封装了异常处理器
  1. spring 中的异常处理
  • @RestControllerAdvice:写在类上,定义一个异常处理类
  • @ExceptionHandler(List):写在异常处理类中方法上,定义某些异常的处理方法
  1. 项目异常处理方案
  • 对异常分类

    1. 业务异常(BusinessException)
    • 规范额用户行为产生的异常
    • 不规范的用户行为操作产生的异常
    1. 系统异常(SystemException)
    • 项目运行过程中可预计但无法避免的异常
    1. 其他异常(Exception)
    • 开发人员未预期的异常
  • 处理

    1. 业务异常
    • 发送对应的信息给用户,提醒规范操作

    1. 系统异常
    • 发送固定的消息给用户,安抚用户

    • 发送特定消息给运维人员,提醒维护

    • 记录日志

    1. 其他异常
    • 发送固定消息给用户,安抚用户
    • 发消息给编程人员,提醒维护
    • 记录日志

拦截器

拦截器(Interceptor)是一种动态拦截方法调用的机制

  1. 作用

    • 在指定的方法调用前后执行预先预定后的代码
    • 组织原始方法的执行
  2. 拦截器和过滤器(Filter)的区别

    • Filter 属于 Servlet 技术,Interceptor 属于 SpringMVC 技术
    • 拦截的内容不同,Filter 对访问进行增强,Interception仅是针对 SpringMVC 的访问进行增强
  3. 拦截器的执行时机

    • 可以在 controller 前和后执行

4, SpringMVC 中定义拦截器

定义一个拦截器类,该拦截器类要定义成一个 bean,且保证该 bean 可以被 SpringMVC 配置类加载到

import org.springframework.web.servlet.HandlerInterceptor;

// 定义一个拦截器类实现 HandlerInterception 接口
@Component
public class ProjectInterception implements HandlerInterceptor {
    /*
     * 执行 controller 之前预处理,
     * return false:不会进入具体的 controller 方法,不会执行原始方法
     * return true:在拦截器之后继续执行原始操作
     * */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle...");
        return true;
    }

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

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

注册拦截器 在 registry.addInterceptor(interception).addPathPatterns("/folder") 中 拦截的路径可以写多个 addPathPatterns("/folder", "/folder/1", "...") 也可以使用通配符的形式

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {

    @Autowired
    private ProjectInterception interception;
    
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interception).addPathPatterns("/folder");
    }
}