SpringMvc

125 阅读12分钟

SpringMvc(web框架)

入门案例

【第一步】创建web工程(Maven结构)

【第二步】设置tomcat服务器,加载web工程(tomcat插件)

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.1</version>
            <configuration>
                <port>80</port>
                <path>/</path>
            </configuration>
        </plugin>
    </plugins>
</build>

【第三步】导入坐标(SpringMVC+Servlet)

<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
</dependencies>

导入spring-webmvc坐标自动依赖spring相关坐标

【第四步】定义处理请求的功能类(UserController)

//定义表现层控制器bean
@Controller
public class UserController {
    //设置映射路径为/save,即外部访问路径
    @RequestMapping("/save")
    //设置当前操作返回结果为指定json数据(本质上是一个字符串信息)
    @ResponseBody
    public String save(){
        System.out.println("user save ...");
        return "{'info':'springmvc'}";
    }
}

==注意事项:==

对于SpringMVC而言,Controller方法返回值默认表示要跳转的页面,没有对应的页面就会报错。如果不想跳转页面而是响应数据,那么就需要在方法上使用@ResponseBody注解。

【第五步】编写SpringMVC配置类,加载处理请求的Bean。

//springmvc配置类,本质上还是一个spring配置类
@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig {
}

【第六步】加载SpringMVC配置,并设置SpringMVC请求拦截的路径

//web容器配置类
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class}
    };
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }
}

相关注解

@Controller注解

@RequestMapping注解:设置当前控制器方法请求访问路径

@RequestParam注解:匹配传递参数给形参

@ResponseBody注解:设置当前控制器方法响应内容为当前返回值,无需解析

@EnableWebMvc注解:配置类上,开启SpringMVC多项辅助功能

@RequestBody注解:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次

@DateTimeFormat注解:接收时间参数

@PathVariable注解:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应

image-20210805120253164

@GetMapping注解:RequestMapping+传参方法

@PostMapping注解

@PutMapping注解

@DeleteMapping注解

@RestController注解:@Controller与@ResponseBody

@RestControllerAdvice注解

@ExceptionHandler注解

  • @RequestBody、@RequestParam、@PathVariable区别和应用

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

AbstractDispatcherServletInitializer类

  • AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类

  • AbstractDispatcherServletInitializer提供三个接口方法供用户实现

    • createServletApplicationContext()方法,创建Servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象范围中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围。
    //加载springmvc配置类,产生springmvc容器(本质还是spring容器)
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(SpringMvcConfig.class);
        return ctx;
    }
    
    • getServletMappings()方法,设定SpringMVC对应的请求映射路径,设置为/表示拦截所有请求,任意请求都将转入到SpringMVC进行处理。
    //设置由springmvc控制器处理的请求映射路径
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
    
    • createRootApplicationContext()方法,如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方式同createServletApplicationContext()
    //加载spring配置类
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }
    

启动服务器初始化过程

  1. 服务器启动,执行ServletContainersInitConfig类,初始化web容器
  2. 执行createServletApplicationContext方法,创建了WebApplicationContext对象
  3. 加载SpringMvcConfig配置类
  4. 执行@ComponentScan加载对应的bean
  5. 加载UserController,每个@RequestMapping的名称对应一个具体的方法
  6. 执行getServletMappings方法,定义所有的请求都通过SpringMVC

image-20210804193122709

SpringMvc管理的bean与spring管理的区分

  1. 方式一:Spring加载的bean设定扫描范围为com.itheima,排除掉controller包内的bean
  2. 方式二:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等
  3. 方式三:不区分Spring与SpringMVC的环境,加载到同一个环境中

方式一代码实现

  • 名称:@ComponentScan
  • 类型:类注解
  • 范例
@Configuration
@ComponentScan(value = "com.itheima",
               excludeFilters = @ComponentScan.Filter(
                   type = FilterType.ANNOTATION,
                   classes = Controller.class
               )
              )
public class SpringConfig {
}
  • 属性

    1. excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes)
    2. includeFilters:加载指定的bean,需要指定类别(type)与具体项(classes)

请求参数

GET方式中文乱码

tomcat 8.5版本之后GET请求就不再出现中文乱码问题,但是我们使用的是tomcat7插件,所以会出现GET请求中文乱码问题。

解决方法

  • 解决:在pom.xml添加tomcat7插件处配置UTF-8字符集,解决GET请求中文乱码问题。
<build>
    <plugins>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.1</version>
        <configuration>
          <port>80</port><!--tomcat端口号-->
          <path>/</path> <!--虚拟目录-->
          <uriEncoding>UTF-8</uriEncoding><!--访问路径编解码字符集-->
        </configuration>
      </plugin>
    </plugins>
  </build>

POST请求中文乱码

在加载SpringMVC配置的配置类中指定字符过滤器。

public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }
​
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }
​
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
​
    //乱码处理
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF-8");
        return new Filter[]{filter};
    }
}

五种类型参数传递

  • 普通参数
  • 普通参数:当请求参数名与形参变量名不同,使用@RequestParam绑定参数关系

image-20210805104824258

//普通参数:请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系
@RequestMapping("/commonParamDifferentName")
@ResponseBody
public String commonParamDifferentName(@RequestParam("name") String userName , int age){
    System.out.println("普通参数传递 userName ==> "+userName);
    System.out.println("普通参数传递 age ==> "+age);
    return "{'module':'common param different name'}";
}
  • 名称:@RequestParam

  • 作用:绑定请求参数与处理器方法形参间的关系

  • 参数:

    • required:是否为必传参数
    • defaultValue:参数默认值
  • POJO类型参数
  • POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数

image-20210805105056731

==注意事项:请求参数key的名称要和POJO中属性的名称一致,否则无法封装。==

  • 嵌套POJO类型参数(同上)

==注意事项:请求参数key的名称要和POJO中属性的名称一致,否则无法封装。==

  • 数组类型参数

数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型即可接收参数

image-20210805105825688

  • 集合类型参数

集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系

image-20210805105957957

//集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
    System.out.println("集合参数传递 likes ==> "+ likes);
    return "{'module':'list param'}";
}

json数据参数传递

json数据参数介绍

  • json普通数组(["","","",...])
  • json对象({key:value,key:value,...})
  • json对象数组([{key:value,...},{key:value,...}])

传递json普通数组

  1. 添加json数据转换相关坐标
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.0</version>
</dependency>
  1. 设置发送json数据(请求body中添加json数据)

image-20210805110937684

  1. 开启自动转换json数据的支持
@Configuration
@ComponentScan("com.itheima.controller")
//开启json数据类型自动转换
@EnableWebMvc
public class SpringMvcConfig {
}

注意事项:

@EnableWebMvc注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换

  1. 在Controller中编写方法接收json参数
//集合参数:json格式
//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
//2.使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String> likes){
    System.out.println("list common(json)参数传递 list ==> "+likes);
    return "{'module':'list common for json param'}";
}

传递json对象

  • POJO参数:json数据与形参对象属性名相同,定义POJO类型形参即可接收参数

image-20210805111544701

//POJO参数:json格式
//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
//2.使用@RequestBody注解将外部传递的json数据映射到形参的实体类对象中,要求属性名称一一对应
@RequestMapping("/pojoParamForJson")
@ResponseBody
public String pojoParamForJson(@RequestBody User user){
    System.out.println("pojo(json)参数传递 user ==> "+user);
    return "{'module':'pojo for json param'}";
}

传递json对象数组

  • POJO集合参数:json数组数据与集合泛型属性名相同,定义List类型形参即可接收参数

image-20210805111626095

//集合参数:json格式
//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
//2.使用@RequestBody注解将外部传递的json数组数据映射到形参的保存实体类对象的集合对象中,要求属性名称一一对应
@RequestMapping("/listPojoParamForJson")
@ResponseBody
public String listPojoParamForJson(@RequestBody List<User> list){
    System.out.println("list pojo(json)参数传递 list ==> "+list);
    return "{'module':'list pojo for json param'}";
}

日期类型参数传递【重点】

//日期参数 http://localhost:80/dataParam?date=2088/08/08&date1=2088-08-18&date2=2088/08/28 8:08:08
//使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date,
                  @DateTimeFormat(pattern="yyyy-MM-dd") Date date1,
                  @DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2){
    System.out.println("参数传递 date ==> "+date);
    System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1);
    System.out.println("参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2);
    return "{'module':'data param'}";
}
  • 其内部依赖Converter接口
public interface Converter<S, T> {
    @Nullable
    T convert(S var1);
}
  • 请求参数年龄数据(String→Integer)
  • json数据转对象(json → POJO)
  • 日期格式转换(String → Date)
  • 传递日期类型参数必须在配置类上使用@EnableWebMvc注解。其功能之一:根据类型匹配对应的类型转换器。

响应

响应页面

文本数据

json数据【重点】

==注意:需要添加jackson-databind依赖以及在SpringMvcConfig配置类上添加@EnableWebMvc注解==

REST风格

@RestController

@GetMapping

@PostMapping

@PutMapping

@DeleteMapping

设置对静态资源的访问放行

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    //设置静态资源访问过滤,当前类需要设置为配置类,并被扫描加载
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        //当访问/pages/????时候,从/pages目录下查找内容
        registry.addResourceHandler("/pages/**")
            .addResourceLocations("/pages/");
        registry.addResourceHandler("/js/**")
            .addResourceLocations("/js/");              
        registry.addResourceHandler("/css/**")
            .addResourceLocations("/css/");       
        registry.addResourceHandler("/plugins/**")
            .addResourceLocations("/plugins/");
    }
}

表现层数据封装【重点】

定义Result类封装响应结果

public class Result {
    //描述统一格式中的数据
    private Object data;
    //描述统一格式中的编码,用于区分操作,可以简化配置0或1表示成功失败
    private Integer code;
    //描述统一格式中的消息,可选属性
    private String msg;
​
    public Result() {
    }
    public Result(Integer code,Object data) {
        this.data = data;
        this.code = code;
    }
    public Result(Integer code, Object data, String msg) {
        this.data = data;
        this.code = code;
        this.msg = msg;
    }
     //同学们自己添加getter、setter、toString()方法
}

Code类封装响应码

//状态码
public class Code {
    public static final Integer SAVE_OK = 20011;
    public static final Integer DELETE_OK = 20021;
    public static final Integer UPDATE_OK = 20031;
    public static final Integer GET_OK = 20041;
​
    public static final Integer SAVE_ERR = 20010;
    public static final Integer DELETE_ERR = 20020;
    public static final Integer UPDATE_ERR = 20030;
    public static final Integer GET_ERR = 20040;
}

异常处理器【理解】

编写异常处理器

@RestControllerAdvice  //用于标识当前类为REST风格对应的异常处理器
public class ProjectExceptionAdvice {
​
    //统一处理所有的Exception异常
    @ExceptionHandler(Exception.class)
    public Result doOtherException(Exception ex){
        return new Result(666,null);
    }
}

项目异常分类

  • 业务异常(BusinessException)

    • 规范的用户行为产生的异常
    • 不规范的用户行为操作产生的异常
  • 系统异常(SystemException)

    • 项目运行过程中可预计且无法避免的异常
  • 其他异常(Exception)

    • 编程人员未预期到的异常

项目异常处理方案

  • 业务异常(BusinessException)

    • ==发送对应消息传递给用户,提醒规范操作==
  • 系统异常(SystemException)

    • ==发送固定消息传递给用户,安抚用户==
    • 发送特定消息给运维人员,提醒维护
    • 记录日志
  • 其他异常(Exception)

    • ==发送固定消息传递给用户,安抚用户==
    • 发送特定消息给编程人员,提醒维护(纳入预期范围内)
    • 记录日志

拦截器【理解】

image-20230304200941368

拦截器和过滤器的区别

  • 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
  • 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强

image-20210805175539717

拦截器代码实现

【第一步】定义拦截器

做法:定义一个类,实现HandlerInterceptor接口即可

@Component //注意当前类必须受Spring容器控制
//定义拦截器类,实现HandlerInterceptor接口
public class ProjectInterceptor implements HandlerInterceptor {
    @Override
    //原始方法调用前执行的内容
    //返回值类型可以拦截控制的执行,true放行,false终止
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle..."+contentType);
        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...");
    }
}

【第二步】配置加载拦截器

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    @Autowired
    private ProjectInterceptor projectInterceptor;
​
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        //配置拦截器
        registry.addInterceptor(projectInterceptor)
            .addPathPatterns("/books","/books/*");
    }
}

使用标准接口WebMvcConfigurer简化开发(注意:侵入式较强)

@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {
    @Autowired
    private ProjectInterceptor projectInterceptor;
​
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //配置多拦截器
        registry.addInterceptor(projectInterceptor)
            .addPathPatterns("/books","/books/*");
    }
}

拦截器流程分析

image-20230304201103133

image-20230304201308564

拦截器参数

  1. request:请求对象
  2. response:响应对象
  3. handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
  4. modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行跳转
  5. ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理