【SpringMVC】SSM框架【二】——SpringMVC超详细

SpringMVC

学习目标:

1.SpringMVC简介

1)web访问流程

  • 1.web服务器通过浏览器访问页面
  • 2.前端页面使用异步提交的方式发送请求到后端服务器
  • 3.后端服务器采用:表现层---业务层---数据层的架构进行开发
  • 4.页面请求由表现层进行接收,获取用户的请求后将参数传递到业务层,再由业务层访问数据层获取数据库中的数据,返回给表现层
  • 5.得到用户需要访问的数据之后,表现层通过JSON的格式发送数据到前端页面
  • 6.前端页面得到数据之后,解析数据并组织对应的页面渲染到浏览器展示给用户

2)SpringMVC概述

  • 是一种基于Java实现的MVC模型的轻量级web框架(表现层框架技术,主要用于表现层开发)
  • 优点:
    • 使用简单,开发便捷
    • 灵活性强

2.SpringMVC入门案例

1)案例实施

  • 1.导入SpringMVCServlet的坐标
xml 复制代码
    <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>

配置tomcat插件:

xml 复制代码
    <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>
  • 2.创建SpringMVC控制器类:
java 复制代码
@Controller
public class controller {

    @RequestMapping("/print")
    @ResponseBody
    public String print() {
        System.out.println("user print ...");
        return "{user: name}";
    }
}
  • 3.初始化SpringMVC环境,设定SpringMVC加载对应的bean:
    SpringMVCConfig:
java 复制代码
@Configuration
@ComponentScan("com.springmvclearn.controller")
public class SpringMvcConfig {
}
  • 4.初始化Servelet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求:
    SpringMvcContainersInitConfig:
java 复制代码
public class ServeletContainersInitConfig extends AbstractDispatcherServletInitializer {


    //加载SpringMVC容器配置
    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(SpringMvcConfig.class);
        return context;
    }

    //设置哪些请求归属springMVC处理
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }
}
涉及的注解介绍



2)入门案例工作流程

3)不同层次结构Bean的加载控制

  • 在开发中,Spring一般加载业务层和数据层的bean,SpringMVC一般加载表现层的bean,而入门案例中配置时,Spring加载时会直接扫描整个包下的bean,包括表现层的bean
  • 为了避免Spring错误的加载到SpringMVC的bean:我们需要在Spring加载bean时,排除掉SpringMVC的bean
  • 1.方式一:Spring加载的bean设定扫描范围为com.itheima,排除掉controller包内的bean(SpringBoot中会使用这种方式进行bean的细粒度控制
java 复制代码
@Configuration
@ComponentScan(value = "com.springmvclearn", 
        excludeFilters = @ComponentScan.Filter(
                type = FilterType.ANNOTATION,
                classes = Controller.class
        )
)
public class SpringConfig {
}
  • 2.方式二:Spring加载的bean设定扫描范围为精确的包:比如直接对应service包,dao包等。
java 复制代码
@Configuration
@ComponentScan({"com.springmvclearn.service", "com.springmvclearn.dao"})
public class SpringConfig {
}

4)简化开发的书写格式

5)PostMan

  • 网页调试与发送网页http请求的Chrome插件

  • 常用于进行接口测试

  • 在workspaces中可以云备份历史请求操作

  • 创建请求:

  • 保持请求,并放入到一个集合中,方便分类复用:

3.请求与响应

1.请求映射路径

  • 当相同层级下的controller有相同的路径时(/save),在访问时就会有路径冲突问题

  • 一般解决就是在不同controller的路径前加入不同模块的请求路径前缀: /user/save/book/save

  • 通过RequestMapping在控制类上统一设置当前控制类下的方法请求路径前缀,然后在每个控制器方法上再加每个控制方法对应的请求访问路径

2.请求参数

1)普通参数请求
java 复制代码
//请求参数
@Controller
public class UserController {

    //普通参数:请求参数与形参名称对应即可完成参数传递
    @RequestMapping("/commonParam")
    @ResponseBody
    public String commonParam(String name ,int age){
        System.out.println("普通参数传递 name ==> "+name);
        System.out.println("普通参数传递 age ==> "+age);
        return "{'module':'common param'}";
    }

    //普通参数:请求参数名与形参名不同时,使用@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'}";
    }
}
  • 1.GET 请求:
  • 2.POST请求:
  • 3.POST中文参数乱码问题
java 复制代码
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};
    }
}
2)POJO参数传递
  • 当大量参数需要传递时,一般将参数封装成一个实体类,在参数传递时,直接将参数赋值给实体类对象,然后从实体类对象中拿取对应参数值

User:

java 复制代码
package com.itheima.domain;

public class User {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + 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;
    }
}

在controller中:

java 复制代码
//请求参数
@Controller
public class UserController {

    //POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
    @RequestMapping("/pojoParam")
    @ResponseBody
    public String pojoParam(User user){
        System.out.println("pojo参数传递 user ==> "+user);
        System.out.println("userName ==> "+user.getName());
        System.out.println("userAge ==> "+user.getAge());
        return "{'module':'pojo param'}";
    }
}


3)嵌套POJO参数传递
  • 当POJO中还有引用的POJO时,如何传递参数
  • 也就是在POJO中将该POJO注入,并给出setter和getter方法:

user:

java 复制代码
package com.itheima.domain;

public class User {
    private String name;
    private int age;

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

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    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;
    }
}

然后在controller中同样接收POJO对象即可:

java 复制代码
//请求参数
@Controller
public class UserController {
    //嵌套POJO参数:嵌套属性按照层次结构设定名称即可完成参数传递
    @RequestMapping("/pojoContainPojoParam")
    @ResponseBody
    public String pojoContainPojoParam(User user){
        System.out.println("pojo嵌套pojo参数传递 user ==> "+user);
        System.out.println("userName ==> "+user.getName());
        System.out.println("userAge ==> "+user.getAge());
        System.out.println("userAddress ==> "+user.getAddress());
        return "{'module':'pojo contain pojo param'}";
    }
}
4)数组参数传递
  • 需要传入一个数组类型的参数时:
java 复制代码
//请求参数
@Controller
public class UserController {
    //数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
    @RequestMapping("/arrayParam")
    @ResponseBody
    public String arrayParam(String[] hobby){
        System.out.println("数组参数传递 hobby ==> "+ Arrays.toString(hobby));
        return "{'module':'array param'}";
    }
}
5)集合类型参数
  • 需要集合类型参数时:我们需要设置该集合参数为RequestParam,否则无法识别需要传递的是集合
java 复制代码
//请求参数
@Controller
public class UserController {
    //集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
    @RequestMapping("/listParam")
    @ResponseBody
    public String listParam(@RequestParam List<String> likes){
        System.out.println("集合参数传递 likes ==> "+ likes);
        return "{'module':'list param'}";
    }
}

请求则同上,结果也是一样的。

3.JSON格式参数传递

  • 首先要加入解析JSON的坐标:
xml 复制代码
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.0</version>
    </dependency>
1)JSON格式的集合参数
  • 1.开启支持集合JSON格式的数据类型自动转换:
java 复制代码
@Configuration
@ComponentScan("com.itheima.controller")
//开启json数据类型自动转换
@EnableWebMvc
public class SpringMvcConfig {
}
  • 2.加上在参数前面加入@RequestBody,表示传入的参数是在JSON的body中
java 复制代码
//请求参数
@Controller
public class UserController {
    //集合参数: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'}";
    }
}


2)JSON格式的POJO参数传递(以及嵌套POJO)
  • 接收参数格式:
java 复制代码
//请求参数
@Controller
public class UserController {
    //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'}";
    }
}
  • 参数请求格式:

  • 结果:

3)JSON格式对象集合参数传递
  • 接收参数格式:
java 复制代码
//请求参数
@Controller
public class UserController {
    //集合参数: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'}";
    }
}
  • 参数请求格式:
  • 结果:

4.对比

5.日期型参数传递


  • 接收参数格式:
java 复制代码
//请求参数
@Controller
public class UserController {
    //日期参数
    //使用@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'}";
    }
}
  • 参数请求格式:
  • 结果:

6.响应

  • 1.响应页面
  • 2.响应文本
  • 3.响应POJO
  • 4.响应POJO集合

使用@ResponseBody

  • 将其注解在SpringMVC控制器方法定义的上方
  • 作用:设置当前控制器返回值为响应体(POJO转JSON,POJO集合转JSON)
java 复制代码
//请求参数
@Controller
public class UserController {
	//返回页面
    @RequestMapping("/pageParam")
    public String pageParam() {
        System.out.println("返回页面");
        return "index.jsp";
    }
	//返回文本数据
    @RequestMapping("/textParam")
    @ResponseBody
    public String totext() {
        System.out.println("返回文本数据");
        return "context text";
    }
	//返回POJO
    @RequestMapping("/userParam")
    @ResponseBody
    public User userParam(User user) {
        System.out.println("返回POJO");
        User new_user = new User();
        new_user.setName(user.getName());
        new_user.setAge(user.getAge());
        new_user.setAddress(user.getAddress());
        return new_user;
    }
	//返回POJO集合
    @RequestMapping("/usersParam")
    @ResponseBody
    public List<User> usersParam(List<User> user) {
        System.out.println("返回POJO集合");
        List<User> new_users = new ArrayList<>();
        new_users.addAll(user);
        return new_users;
    }
}

4.REST风格

  • 是一种定义资源描述的形式

  • REST风格访问资源时,使用行为动作区分对资源进行了何种操作:路径+请求方式

1)RESTful
  • 在请求时通过不同的请求方式

  • @PathVariable接收路径参数或者路径变量(路径中的参数获取:http://localhost/users/3 -> 3的获取)

java 复制代码
package com.itheima.controller;

import com.itheima.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
public class UserController {

    //设置当前请求方法为POST,表示REST风格中的添加操作
    @RequestMapping(value = "/users",method = RequestMethod.POST)
    @ResponseBody
    public String save(){
        System.out.println("user save...");
        return "{'module':'user save'}";
    }

    //设置当前请求方法为DELETE,表示REST风格中的删除操作
    //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
    @RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)
    @ResponseBody
    public String delete(@PathVariable Integer id){
        System.out.println("user delete..." + id);
        return "{'module':'user delete'}";
    }

    //设置当前请求方法为PUT,表示REST风格中的修改操作
    @RequestMapping(value = "/users",method = RequestMethod.PUT)
    @ResponseBody
    public String update(@RequestBody User user){
        System.out.println("user update..."+user);
        return "{'module':'user update'}";
    }

    //设置当前请求方法为GET,表示REST风格中的查询操作
    //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
    @RequestMapping(value = "/users/{id}" ,method = RequestMethod.GET)
    @ResponseBody
    public String getById(@PathVariable Integer id){
        System.out.println("user getById..."+id);
        return "{'module':'user getById'}";
    }

    //设置当前请求方法为GET,表示REST风格中的查询操作
    @RequestMapping(value = "/users",method = RequestMethod.GET)
    @ResponseBody
    public String getAll(){
        System.out.println("user getAll...");
        return "{'module':'user getAll'}";
    }
}
2)@RequestBody@RequestParam@PathVariable
3)RESTful快速开发
  • 在1)RESTful的编写中,同一个路径前缀等在每个控制器方法上都有,为了简化书写,以下注解和简化写法是标准的RESTful开发:

  • 标准RESTful开发:

java 复制代码
package com.itheima.controller;

import com.itheima.domain.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

//@Controller
//@ResponseBody配置在类上可以简化配置,表示设置当前每个方法的返回值都作为响应体
//@ResponseBody
@RestController     //使用@RestController注解替换@Controller与@ResponseBody注解,简化书写
@RequestMapping("/books")
public class BookController {

//    @RequestMapping( method = RequestMethod.POST)
    @PostMapping        //使用@PostMapping简化Post请求方法对应的映射配置
    public String save(@RequestBody Book book){
        System.out.println("book save..." + book);
        return "{'module':'book save'}";
    }

//    @RequestMapping(value = "/{id}" ,method = RequestMethod.DELETE)
    @DeleteMapping("/{id}")     //使用@DeleteMapping简化DELETE请求方法对应的映射配置
    public String delete(@PathVariable Integer id){
        System.out.println("book delete..." + id);
        return "{'module':'book delete'}";
    }

//    @RequestMapping(method = RequestMethod.PUT)
    @PutMapping         //使用@PutMapping简化Put请求方法对应的映射配置
    public String update(@RequestBody Book book){
        System.out.println("book update..."+book);
        return "{'module':'book update'}";
    }

//    @RequestMapping(value = "/{id}" ,method = RequestMethod.GET)
    @GetMapping("/{id}")    //使用@GetMapping简化GET请求方法对应的映射配置
    public String getById(@PathVariable Integer id){
        System.out.println("book getById..."+id);
        return "{'module':'book getById'}";
    }

//    @RequestMapping(method = RequestMethod.GET)
    @GetMapping             //使用@GetMapping简化GET请求方法对应的映射配置
    public String getAll(){
        System.out.println("book getAll...");
        return "{'module':'book getAll'}";
    }
}
4)案例:基于RESRful页面数据交互
  • 1.制作控制器,通过PostMan测试接口:

  • 2.放行静态资源访问:

    由于SpringMVC设置了访问路径拦截,当访问前端静态页面时,会被拦截,导致无法访问到,于是我们需要制作放行器,将对应的静态数据进行放行:

SpringMVCSupport:

java 复制代码
package com.itheima.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class SpringSupport extends WebMvcConfigurationSupport {

    //当访问/pages/????的时候,走/pages目录下的内容
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
        registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
    }
}
  • 3.异步提交访问后端控制器获取数据:

5.拦截器

  • 动态拦截方法调用的机制,在SpringMVC中动态拦截控制器的执行
  • 作用:
    • 在指定的方法调用前后执行预先设定的代码
    • 阻止原始方法的执行(权限控制)

1)拦截器与过滤器区别

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

2)拦截器入门案例

  • 1.声明拦截器的bean,并实现HandlerInterceptor接口:
java 复制代码
@Component
public class MyProjectInterceptor implements HandlerInterceptor {
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyProjectInterceptor posHandle");
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyProjectInterceptor preHandle");
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyProjectInterceptor afterCompletion");
    }
}
  • 2.定义配置类,继承WebMvcConfigurationSupport,实现addInterceptor方法:
java 复制代码
@Configuration
public class MySpringMvcSupport extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    	
    }
}
  • 3.添加拦截器并设定拦截的访问路径,路径可以通过可变参数设置多个:
java 复制代码
@Configuration
public class MySpringMvcSupport extends WebMvcConfigurerAdapter {

    @Autowired
    private MyProjectInterceptor projectInterceptor;
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/page/**").addResourceLocations("classpath:/pages/");
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    	//拦截器配置,当遇到哪些路径时,会将其拦截下来执行对应的postHandle和preHandle方法
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");
    }
}
  • 4.发送请求:
  • 5.拦截器拦截对应的路径下方法的调用,并在方法调用前后执行预先定义的方法:
拦截器执行流程:

3)拦截器参数(了解即可,主要用到的可能是前置处理)

  • 前置处理,拿到handler(原始方法对象的封装)可以对原始方法进行一些处理
  • 主要用处为在方法执行前作校验


4)拦截器链(了解即可)

  • 当配置多个拦截器是,形参拦截器链
  • 拦截器链的运行顺序参照拦截器添加的顺序为准

    *注:上述内容均来自黑马程序员SSM框架的课程学习,仅用作学习交流,不作为商业用途,如有侵权,联系删除。
相关推荐
计算机学姐20 小时前
基于SpringBoot的民宿预定管理系统【三角色+个性化推荐算法+数据可视化统计】
java·vue.js·spring boot·mysql·信息可视化·intellij-idea·推荐算法
noBt20 小时前
Windows IDEA 卡顿严重
java·ide·intellij-idea
shuair20 小时前
redis缓存预热、缓存击穿、缓存穿透、缓存雪崩
redis·spring·缓存
计算机程序设计小李同学21 小时前
基于 Spring Boot + Vue 的龙虾专营店管理系统的设计与实现
java·spring boot·后端·spring·vue
qq_12498707531 天前
基于Java Web的城市花园小区维修管理系统的设计与实现(源码+论文+部署+安装)
java·开发语言·前端·spring boot·spring·毕业设计·计算机毕业设计
Chasmれ1 天前
Spring Boot 1.x(基于Spring 4)中使用Java 8实现Token
java·spring boot·spring
计算机学姐1 天前
基于SpringBoot的校园社团管理系统
java·vue.js·spring boot·后端·spring·信息可视化·推荐算法
落霞的思绪1 天前
Spring AI Alibaba 集成 Redis 向量数据库实现 RAG 与记忆功能
java·spring·rag·springai
小疙瘩1 天前
去掉 IDEA 中 mybatis配置文件的局部背景颜色(图解)
java·ide·intellij-idea
Remember_9931 天前
MySQL 索引详解:从原理到实战优化
java·数据库·mysql·spring·http·adb·面试